diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 3c0b91aff..95712e499 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -3,8 +3,11 @@ packages: - cmake - py27-s3cmd - wget + - curl + - jq secrets: - 6c60aaee-92e7-4e7d-812c-114817689b4d + - dd0bd962-7664-4d3e-b0f3-41c9ee96b8b8 sources: - https://github.com/ziglang/zig tasks: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f07f3305..c35e5294a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,10 +10,14 @@ if(NOT CMAKE_INSTALL_PREFIX) "Directory to install zig to" FORCE) endif() +set(CMAKE_USER_MAKE_RULES_OVERRIDE + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/c_flag_overrides.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_flag_overrides.cmake) + project(zig C CXX) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) - set(ZIG_VERSION_MAJOR 0) set(ZIG_VERSION_MINOR 5) set(ZIG_VERSION_PATCH 0) @@ -42,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}") set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") +set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") if(ZIG_STATIC) set(ZIG_STATIC_LLVM "on") @@ -433,16 +438,20 @@ set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") # This is our shim which will be replaced by libuserland written in Zig. set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp") +if(ZIG_ENABLE_MEM_PROFILE) + set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp") +endif() + set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/analyze.cpp" "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" "${CMAKE_SOURCE_DIR}/src/bigint.cpp" "${CMAKE_SOURCE_DIR}/src/buffer.cpp" - "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp" "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" "${CMAKE_SOURCE_DIR}/src/codegen.cpp" "${CMAKE_SOURCE_DIR}/src/compiler.cpp" + "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp" "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/error.cpp" "${CMAKE_SOURCE_DIR}/src/glibc.cpp" @@ -453,11 +462,10 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/os.cpp" "${CMAKE_SOURCE_DIR}/src/parser.cpp" "${CMAKE_SOURCE_DIR}/src/range_set.cpp" - "${CMAKE_SOURCE_DIR}/src/stack_report.cpp" "${CMAKE_SOURCE_DIR}/src/target.cpp" "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" - "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" "${CMAKE_SOURCE_DIR}/src/util.cpp" + "${ZIG_SOURCES_MEM_PROFILE}" ) set(OPTIMIZED_C_SOURCES "${CMAKE_SOURCE_DIR}/src/blake2b.c" @@ -624,5 +632,10 @@ set_target_properties(zig PROPERTIES LINK_FLAGS ${EXE_LDFLAGS} ) target_link_libraries(zig compiler "${LIBUSERLAND}") +if(MSVC) + target_link_libraries(zig ntdll.lib) +elseif(MINGW) + target_link_libraries(zig ntdll) +endif() add_dependencies(zig zig_build_libuserland) install(TARGETS zig DESTINATION bin) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6bb3c3d0..a5aeeb9e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,3 +123,61 @@ When developing on Linux, another option is available to you: `-Denable-wine`. This will enable running behavior tests and std lib tests with Wine. It's recommended for Linux users to install Wine and enable this testing option when editing the standard library or anything Windows-related. + +#### Improving Translate-C + +Please read the [Editing Source Code](#editing-source-code) section as a +prerequisite to this one. + +`translate-c` is a feature provided by Zig that converts C source code into +Zig source code. It powers the `zig translate-c` command as well as +[@cImport](https://ziglang.org/documentation/master/#cImport), allowing Zig +code to not only take advantage of function prototypes defined in .h files, +but also `static inline` functions written in C, and even some macros. + +This feature works by using libclang API to parse and semantically analyze +C/C++ files, and then based on the provided AST and type information, +generating Zig AST, and finally using the mechanisms of `zig fmt` to render +the Zig AST to a file. + +The relevant tests for this feature are: + + * `test/run_translated_c.zig` - each test case is C code with a `main` function. The C code + is translated into Zig code, compiled, and run, and tests that the expected output is the + same, and that the program exits cleanly. This kind of test coverage is preferred, when + possible, because it makes sure that the resulting Zig code is actually viable. + + * `test/translate_c.zig` - each test case is C code, with a list of expected strings which + must be found in the resulting Zig code. This kind of test is more precise in what it + measures, but does not provide test coverage of whether the resulting Zig code is valid. + +This feature is self-hosted, even though Zig is not fully self-hosted yet. In the Zig source +repo, we maintain a C API on top of Clang's C++ API: + + * `src/zig_clang.h` - the C API that we maintain on top of Clang's C++ API. This + file does not include any Clang's C++ headers. Instead, C types and C enums are defined + here. + + * `src/zig_clang.cpp` - a lightweight wrapper that fulfills the C API on top of the + C++ API. It takes advantage of `static_assert` to make sure we get compile errors when + Clang's C++ API changes. This one file necessarily does include Clang's C++ headers, which + makes it the slowest-to-compile source file in all of Zig's codebase. + + * `src-self-hosted/clang.zig` - the Zig equivalent of `src/zig_clang.h`. This is a manually + maintained list of types and functions that are ABI-compatible with the Clang C API we + maintain. In theory this could be generated by running translate-c on `src/zig_clang.h`, + but that would introduce a dependency cycle, since we are using this file to implement + translate-c. + +Finally, the actual source code for the translate-c feature is +`src-self-hosted/translate_c.zig`. This code uses the Clang C API exposed by +`src-self-hosted/clang.zig`, and produces Zig AST. + +The steps for contributing to translate-c look like this: + + 1. Identify a test case you want to improve. Add it as a run-translated-c test + case (usually preferable), or as a translate-c test case. + + 2. Edit `src-self-hosted/translate_c.zig` to improve the behavior. + + 3. Run the relevant tests: `./zig build test-run-translated-c test-translate-c` diff --git a/README.md b/README.md index f2745e611..ac9e8208a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![ZIG](https://ziglang.org/zig-logo.svg) -A general-purpose programming language designed for **robustness**, -**optimality**, and **maintainability**. +A general-purpose programming language and toolchain for maintaining +**robust**, **optimal**, and **reusable** software. ## Resources diff --git a/build.zig b/build.zig index 390f1594f..979379d9f 100644 --- a/build.zig +++ b/build.zig @@ -20,10 +20,10 @@ pub fn build(b: *Builder) !void { const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe); const langref_out_path = fs.path.join( b.allocator, - [_][]const u8{ b.cache_root, "langref.html" }, + &[_][]const u8{ b.cache_root, "langref.html" }, ) catch unreachable; var docgen_cmd = docgen_exe.run(); - docgen_cmd.addArgs([_][]const u8{ + docgen_cmd.addArgs(&[_][]const u8{ rel_zig_exe, "doc" ++ fs.path.sep_str ++ "langref.html.in", langref_out_path, @@ -36,7 +36,7 @@ pub fn build(b: *Builder) !void { const test_step = b.step("test", "Run all the tests"); // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library - const build_info = try b.exec([_][]const u8{ + const build_info = try b.exec(&[_][]const u8{ b.zig_exe, "BUILD_INFO", }); @@ -54,8 +54,9 @@ pub fn build(b: *Builder) !void { var test_stage2 = b.addTest("src-self-hosted/test.zig"); test_stage2.setBuildMode(builtin.Mode.Debug); + test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig"); - const fmt_build_zig = b.addFmt([_][]const u8{"build.zig"}); + const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); @@ -72,9 +73,9 @@ pub fn build(b: *Builder) !void { const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false; const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false; const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false; - if (!skip_self_hosted) { - // TODO re-enable this after https://github.com/ziglang/zig/issues/2377 - //test_step.dependOn(&exe.step); + if (!skip_self_hosted and builtin.os == .linux) { + // TODO evented I/O other OS's + test_step.dependOn(&exe.step); } const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; @@ -87,7 +88,7 @@ pub fn build(b: *Builder) !void { .source_dir = "lib", .install_dir = .Lib, .install_subdir = "zig", - .exclude_extensions = [_][]const u8{ "test.zig", "README.md" }, + .exclude_extensions = &[_][]const u8{ "test.zig", "README.md" }, }); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); @@ -98,11 +99,7 @@ pub fn build(b: *Builder) !void { const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); - - // TODO see https://github.com/ziglang/zig/issues/1364 - if (false) { - test_step.dependOn(test_stage2_step); - } + test_step.dependOn(test_stage2_step); var chosen_modes: [4]builtin.Mode = undefined; var chosen_mode_index: usize = 0; @@ -140,6 +137,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); + test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter)); test_step.dependOn(tests.addGenHTests(b, test_filter)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(docs_step); @@ -151,16 +149,16 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { } const lib_dir = fs.path.join( b.allocator, - [_][]const u8{ dep.prefix, "lib" }, + &[_][]const u8{ dep.prefix, "lib" }, ) catch unreachable; for (dep.system_libs.toSliceConst()) |lib| { const static_bare_name = if (mem.eql(u8, lib, "curses")) - ([]const u8)("libncurses.a") + @as([]const u8, "libncurses.a") else - b.fmt("lib{}.a", lib); + b.fmt("lib{}.a", .{lib}); const static_lib_name = fs.path.join( b.allocator, - [_][]const u8{ lib_dir, static_bare_name }, + &[_][]const u8{ lib_dir, static_bare_name }, ) catch unreachable; const have_static = fileExists(static_lib_name) catch unreachable; if (have_static) { @@ -186,10 +184,10 @@ fn fileExists(filename: []const u8) !bool { } fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { - lib_exe_obj.addObjectFile(fs.path.join(b.allocator, [_][]const u8{ + lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ cmake_binary_dir, "zig_cpp", - b.fmt("{}{}{}", lib_exe_obj.target.libPrefix(), lib_name, lib_exe_obj.target.staticLibSuffix()), + b.fmt("{}{}{}", .{ lib_exe_obj.target.libPrefix(), lib_name, lib_exe_obj.target.staticLibSuffix() }), }) catch unreachable); } @@ -202,22 +200,22 @@ const LibraryDep = struct { }; fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { - const shared_mode = try b.exec([_][]const u8{ llvm_config_exe, "--shared-mode" }); + const shared_mode = try b.exec(&[_][]const u8{ llvm_config_exe, "--shared-mode" }); const is_static = mem.startsWith(u8, shared_mode, "static"); const libs_output = if (is_static) - try b.exec([_][]const u8{ + try b.exec(&[_][]const u8{ llvm_config_exe, "--libfiles", "--system-libs", }) else - try b.exec([_][]const u8{ + try b.exec(&[_][]const u8{ llvm_config_exe, "--libs", }); - const includes_output = try b.exec([_][]const u8{ llvm_config_exe, "--includedir" }); - const libdir_output = try b.exec([_][]const u8{ llvm_config_exe, "--libdir" }); - const prefix_output = try b.exec([_][]const u8{ llvm_config_exe, "--prefix" }); + const includes_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--includedir" }); + const libdir_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--libdir" }); + const prefix_output = try b.exec(&[_][]const u8{ llvm_config_exe, "--prefix" }); var result = LibraryDep{ .prefix = mem.tokenize(prefix_output, " \r\n").next().?, @@ -235,6 +233,9 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { if (fs.path.isAbsolute(lib_arg)) { try result.libs.append(lib_arg); } else { + if (mem.endsWith(u8, lib_arg, ".lib")) { + lib_arg = lib_arg[0 .. lib_arg.len - 4]; + } try result.system_libs.append(lib_arg); } } @@ -341,16 +342,16 @@ fn addCxxKnownPath( objname: []const u8, errtxt: ?[]const u8, ) !void { - const path_padded = try b.exec([_][]const u8{ + const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, - b.fmt("-print-file-name={}", objname), + b.fmt("-print-file-name={}", .{objname}), }); const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { - warn("{}", msg); + warn("{}", .{msg}); } else { - warn("Unable to determine path to {}\n", objname); + warn("Unable to determine path to {}\n", .{objname}); } return error.RequiredLibraryNotFound; } @@ -373,6 +374,7 @@ fn addLibUserlandStep(b: *Builder, mode: builtin.Mode) void { artifact.bundle_compiler_rt = true; artifact.setTarget(builtin.arch, builtin.os, builtin.abi); artifact.setBuildMode(mode); + artifact.force_pic = true; if (mode != .Debug) { artifact.strip = true; } diff --git a/ci/azure/linux_script b/ci/azure/linux_script index e12f16f15..1116099cd 100755 --- a/ci/azure/linux_script +++ b/ci/azure/linux_script @@ -5,7 +5,7 @@ set -e BUILDDIR="$(pwd)" -sudo sh -c 'echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main" >> /etc/apt/sources.list' +sudo sh -c 'echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" >> /etc/apt/sources.list' wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update -q @@ -14,6 +14,10 @@ sudo apt-get remove -y llvm-* sudo rm -rf /usr/local/* sudo apt-get install -y libxml2-dev libclang-9-dev llvm-9 llvm-9-dev cmake s3cmd gcc-7 g++-7 qemu +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git config core.abbrev 9 + export CC=gcc-7 export CXX=g++-7 mkdir build @@ -21,11 +25,12 @@ cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j2 install ./zig build test -Denable-qemu +VERSION="$(./zig version)" if [ "${BUILD_REASON}" != "PullRequest" ]; then ARTIFACTSDIR="$BUILDDIR/artifacts" mkdir "$ARTIFACTSDIR" - docker run -i --mount type=bind,source="$ARTIFACTSDIR",target=/z ziglang/static-base:llvm9-1 -j2 $BUILD_SOURCEVERSION + docker run -i --mount type=bind,source="$ARTIFACTSDIR",target=/z ziglang/static-base:llvm9-x86_64 -j2 $BUILD_SOURCEVERSION TARBALL="$(ls $ARTIFACTSDIR)" mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg" s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$ARTIFACTSDIR/$TARBALL" s3://ziglang.org/builds/ @@ -40,7 +45,7 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/linux-$VERSION.json" + s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/x86_64-linux-$VERSION.json" # `set -x` causes these variables to be mangled. # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html @@ -48,4 +53,5 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" + echo "##vso[task.setvariable variable=version;isOutput=true]$VERSION" fi diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 790b276f0..79eaa0703 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -8,7 +8,9 @@ system_profiler SPHardwareDataType brew install s3cmd gcc@8 ZIGDIR="$(pwd)" -CACHE_BASENAME="llvm+clang-10.0.0-macos-x86_64-gcc8-release" +LLVMVER="10.0.0" +ARCH="x86_64" +CACHE_BASENAME="llvm+clang+lld-$LLVMVER-$ARCH-macos-gcc8-release" PREFIX="$HOME/$CACHE_BASENAME" TMPDIR="$HOME/tmpz" JOBS="-j2" @@ -44,18 +46,27 @@ else rm $PREFIX/lib/libz*dylib cd $TMPDIR - wget https://releases.llvm.org/10.0.0/llvm-10.0.0.src.tar.xz - tar xf llvm-10.0.0.src.tar.xz - cd llvm-10.0.0.src/ + wget https://releases.llvm.org/$LLVMVER/llvm-$LLVMVER.src.tar.xz + tar xf llvm-$LLVMVER.src.tar.xz + cd llvm-$LLVMVER.src/ mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_PREFIX_PATH=$PREFIX -DCMAKE_BUILD_TYPE=Release -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="AVR" -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_TERMINFO=OFF make $JOBS install cd $TMPDIR - wget https://releases.llvm.org/10.0.0/cfe-10.0.0.src.tar.xz - tar xf cfe-10.0.0.src.tar.xz - cd cfe-10.0.0.src/ + wget https://releases.llvm.org/$LLVMVER/cfe-$LLVMVER.src.tar.xz + tar xf cfe-$LLVMVER.src.tar.xz + cd cfe-$LLVMVER.src/ + mkdir build + cd build + cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_PREFIX_PATH=$PREFIX -DCMAKE_BUILD_TYPE=Release + make $JOBS install + + cd $TMPDIR + wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVMVER/lld-$LLVMVER.src.tar.xz + tar xf lld-$LLVMVER.src.tar.xz + cd lld-$LLVMVER.src/ mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_PREFIX_PATH=$PREFIX -DCMAKE_BUILD_TYPE=Release @@ -68,6 +79,11 @@ else fi cd $ZIGDIR + +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git config core.abbrev 9 + mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX -DCMAKE_INSTALL_PREFIX=$(pwd)/release -DZIG_STATIC=ON @@ -81,7 +97,7 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then rmdir release/bin VERSION=$(release/zig version) - DIRNAME="zig-macos-x86_64-$VERSION" + DIRNAME="zig-macos-$ARCH-$VERSION" TARBALL="$DIRNAME.tar.xz" mv release "$DIRNAME" tar cfJ "$TARBALL" "$DIRNAME" @@ -99,7 +115,7 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/macos-$VERSION.json" + s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$ARCH-macos-$VERSION.json" # `set -x` causes these variables to be mangled. # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html diff --git a/ci/azure/on_master_success b/ci/azure/on_master_success new file mode 100755 index 000000000..cef618361 --- /dev/null +++ b/ci/azure/on_master_success @@ -0,0 +1,12 @@ +#!/bin/sh + +# We do not set -x because this would leak the oauth access token. +set +x + +set -e + +sudo apt-get update -y +sudo apt-get install -y curl jq + +OAUTH_TOKEN="$(cat "$DOWNLOADSECUREFILE_SECUREFILEPATH")" +./ci/srht/on_master_success "$VERSION" "$OAUTH_TOKEN" diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index bcc2f1194..5dbfe5a33 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -14,7 +14,7 @@ jobs: displayName: 'Build and test' - job: BuildLinux pool: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-18.04' timeoutInMinutes: 360 @@ -44,7 +44,7 @@ jobs: - script: ci/azure/windows_script.bat name: main displayName: 'Build and test' -- job: UpdateDownloadPage +- job: OnMasterSuccess dependsOn: - BuildMacOS - BuildLinux @@ -53,20 +53,12 @@ jobs: strategy: maxParallel: 1 pool: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-18.04' variables: - macos_tarball: $[ dependencies.BuildMacOS.outputs['main.tarball'] ] - macos_shasum: $[ dependencies.BuildMacOS.outputs['main.shasum'] ] - macos_bytesize: $[ dependencies.BuildMacOS.outputs['main.bytesize'] ] - linux_tarball: $[ dependencies.BuildLinux.outputs['main.tarball'] ] - linux_shasum: $[ dependencies.BuildLinux.outputs['main.shasum'] ] - linux_bytesize: $[ dependencies.BuildLinux.outputs['main.bytesize'] ] - windows_tarball: $[ dependencies.BuildWindows.outputs['main.tarball'] ] - windows_shasum: $[ dependencies.BuildWindows.outputs['main.shasum'] ] - windows_bytesize: $[ dependencies.BuildWindows.outputs['main.bytesize'] ] + version: $[ dependencies.BuildLinux.outputs['main.version'] ] steps: - task: DownloadSecureFile@1 inputs: - secureFile: s3cfg - - script: ci/azure/update_download_page - displayName: 'Update download page' + secureFile: oauth_token + - script: ci/azure/on_master_success + displayName: 'master branch success hook' diff --git a/ci/azure/update_download_page b/ci/azure/update_download_page deleted file mode 100755 index c6a690e8e..000000000 --- a/ci/azure/update_download_page +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -set -x -set -e - -SRCDIR=$(pwd) -rm -rf .git -sudo apt-get update -y -sudo apt-get install -y s3cmd curl jq - -cd "$HOME" -wget "https://ziglang.org/builds/$LINUX_TARBALL" -tar xf $LINUX_TARBALL -ZIGDIR=$(basename -s .tar.xz $LINUX_TARBALL) -ZIG="$ZIGDIR/zig" -LANGREF="$ZIGDIR/langref.html" -VERSION=$($ZIG version) -SRCTARBALLDIR="zig-$VERSION" -export SRC_TARBALL="$SRCTARBALLDIR.tar.xz" -mv "$SRCDIR" "$SRCTARBALLDIR" -tar cfJ "$SRC_TARBALL" "$SRCTARBALLDIR" -export SRC_SHASUM=$(sha256sum $SRC_TARBALL | cut '-d ' -f1) -export SRC_BYTESIZE=$(wc -c < $SRC_TARBALL) - -# the freebsd build has to be there too -FREEBSD_JSON=$(curl --fail "https://ziglang.org/builds/freebsd-$VERSION.json" || exit 1) -export FREEBSD_TARBALL="$(echo "$FREEBSD_JSON" | jq .tarball -r)" -export FREEBSD_BYTESIZE="$(echo "$FREEBSD_JSON" | jq .size -r)" -export FREEBSD_SHASUM="$(echo "$FREEBSD_JSON" | jq .shasum -r)" - -git clone https://github.com/ziglang/www.ziglang.org --depth 1 -cd www.ziglang.org -export MASTER_DATE=$(date +%Y-%m-%d) -env -"../$ZIG" run update-download-page.zig - -mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg" -s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "../$SRC_TARBALL" s3://ziglang.org/builds/ -s3cmd put -P "../$LANGREF" s3://ziglang.org/documentation/master/index.html --add-header="Cache-Control: max-age=0, must-revalidate" -s3cmd put -P www/download/index.html s3://ziglang.org/download/index.html --add-header="Cache-Control: max-age=0, must-revalidate" -s3cmd put -P www/download/index.json s3://ziglang.org/download/index.json --add-header="Cache-Control: max-age=0, must-revalidate" diff --git a/ci/azure/windows_install b/ci/azure/windows_install index 6c2a5442d..6fc1d86c8 100755 --- a/ci/azure/windows_install +++ b/ci/azure/windows_install @@ -6,5 +6,5 @@ set -e pacman -Su --needed --noconfirm pacman -S --needed --noconfirm wget p7zip python3-pip pip install s3cmd -wget -nv "https://ziglang.org/deps/llvm%2bclang-10.0.0-win64-msvc-release.tar.xz" +wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-mt.tar.xz" tar xf llvm+clang-10.0.0-win64-msvc-release.tar.xz diff --git a/ci/azure/windows_script.bat b/ci/azure/windows_script.bat index 4db80560c..d83d2075a 100644 --- a/ci/azure/windows_script.bat +++ b/ci/azure/windows_script.bat @@ -11,15 +11,18 @@ SET "MSYSTEM=%PREVMSYSTEM%" SET "ZIGBUILDDIR=%SRCROOT%\build" SET "ZIGINSTALLDIR=%ZIGBUILDDIR%\dist" -SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang-10.0.0-win64-msvc-release" +SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang+lld-10.0.0-x86_64-windows-msvc-mt" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 +REM Make the `zig version` number consistent. +REM This will affect the cmake command below. +git.exe config core.abbrev 9 + mkdir %ZIGBUILDDIR% cd %ZIGBUILDDIR% -REM Here we use MinSizeRel instead of Release to work around https://github.com/ziglang/zig/issues/3024 -cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=MinSizeRel || exit /b -msbuild /p:Configuration=MinSizeRel INSTALL.vcxproj || exit /b +cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b +msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test || exit /b diff --git a/ci/azure/windows_upload b/ci/azure/windows_upload index d5527fa13..7ed1b7d1f 100755 --- a/ci/azure/windows_upload +++ b/ci/azure/windows_upload @@ -30,7 +30,7 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/windows-$VERSION.json" + s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/x86_64-windows-$VERSION.json" # `set -x` causes these variables to be mangled. # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html diff --git a/ci/drone/drone.yml b/ci/drone/drone.yml new file mode 100644 index 000000000..8fff358a9 --- /dev/null +++ b/ci/drone/drone.yml @@ -0,0 +1,19 @@ +--- +kind: pipeline +name: test-aarch64-linux-musl + +platform: + arch: arm64 + +steps: +- name: build-and-test + image: ziglang/static-base:llvm9-1 + environment: + SRHT_OAUTH_TOKEN: + from_secret: SRHT_OAUTH_TOKEN + AWS_ACCESS_KEY_ID: + from_secret: AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY: + from_secret: AWS_SECRET_ACCESS_KEY + commands: + - ./ci/drone/linux_script diff --git a/ci/drone/linux_script b/ci/drone/linux_script new file mode 100755 index 000000000..61a5908de --- /dev/null +++ b/ci/drone/linux_script @@ -0,0 +1,70 @@ +#!/bin/sh + +set -x +set -e + +TRIPLEARCH="$(uname -m)" +BUILDDIR="$(pwd)" +DISTDIR="$(pwd)/dist" + +apk update +apk add py3-pip xz perl-utils jq curl +pip3 install s3cmd + +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git config core.abbrev 9 + +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local + +make -j$(nproc) install +./zig build test-fmt test-behavior test-std test-compiler-rt -Dskip-release -Dskip-non-native +# TODO test-compare-output is hitting https://github.com/ziglang/zig/issues/3526 +./zig build test-standalone test-stack-traces +# TODO test-cli is hitting https://github.com/ziglang/zig/issues/3526 +./zig build test-asm-link test-runtime-safety +# TODO test-translate-c is hitting https://github.com/ziglang/zig/issues/3526 +./zig build test-gen-h +# TODO test-compile-errors is hitting https://github.com/ziglang/zig/issues/3526 +# TODO building docs is hitting https://github.com/ziglang/zig/issues/3526 + +# TODO full test suite: +#./zig build test + +if [ -z "$DRONE_PULL_REQUEST" ]; then + mv ../LICENSE "$DISTDIR/" + # TODO uncomment when the docs are generated + # mv ../zig-cache/langref.html "$DISTDIR/" + mv "$DISTDIR/bin/zig" "$DISTDIR/" + rmdir "$DISTDIR/bin" + + GITBRANCH="$DRONE_BRANCH" + VERSION="$("$DISTDIR/zig" version)" + DIRNAME="zig-linux-$TRIPLEARCH-$VERSION" + TARBALL="$DIRNAME.tar.xz" + mv "$DISTDIR" "$DIRNAME" + tar cfJ "$TARBALL" "$DIRNAME" + + s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ + + SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) + BYTESIZE=$(wc -c < $TARBALL) + + JSONFILE="$TRIPLEARCH-linux-$GITBRANCH.json" + touch $JSONFILE + echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE + echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE + echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE + + s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" + s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json" + if [ "$GITBRANCH" = "master" ]; then + # avoid leaking oauth token + set +x + + cd "$BUILDDIR" + ./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN" + fi +fi diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 2e16ca917..fbdf8efd8 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -13,9 +13,14 @@ wget -nv "https://ziglang.org/builds/$CACHE_BASENAME.tar.xz" tar xf "$CACHE_BASENAME.tar.xz" cd $ZIGDIR + +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git config core.abbrev 9 + mkdir build cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX -DCMAKE_INSTALL_PREFIX=$(pwd)/release -DZIG_STATIC=ON +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX "-DCMAKE_INSTALL_PREFIX=$(pwd)/release" -DZIG_STATIC=ON make $JOBS install release/bin/zig build test-fmt @@ -80,5 +85,14 @@ if [ -f ~/.s3cfg ]; then echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/freebsd-$VERSION.json" + s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/x86_64-freebsd-$VERSION.json" + + if [ "$GITBRANCH" = "master" ]; then + # avoid leaking oauth token + set +x + + OAUTH_TOKEN="$(cat ~/.oauth_token)" + cd "$ZIGDIR" + ./ci/srht/on_master_success "$VERSION" "$OAUTH_TOKEN" + fi fi diff --git a/ci/srht/on_master_success b/ci/srht/on_master_success new file mode 100755 index 000000000..c5a01f3bf --- /dev/null +++ b/ci/srht/on_master_success @@ -0,0 +1,39 @@ +#!/bin/sh + +# This script must run on a lot of different platforms. +# It assumes that the following things are installed: +# * curl +# * jq +# * cat + +# We do not set -x because this would leak the oauth access token. +set +x + +set -e + +VERSION="$1" +OAUTH_TOKEN="$2" +YML_FILE="tmp.yml" + +cat <"$YML_FILE" +image: alpine/latest +packages: + - py3-pip + - curl + - jq + - xz +secrets: + - 6c60aaee-92e7-4e7d-812c-114817689b4d +sources: + - https://github.com/ziglang/zig +tasks: + - build: cd zig && ./ci/srht/update_download_page $VERSION +EOF + +jq <$YML_FILE -sR '{ + "manifest": ., + }' | curl \ + -H Authorization:"token $OAUTH_TOKEN" \ + -H Content-Type:application/json \ + -X POST \ + -d @- "https://builds.sr.ht/api/jobs" diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page new file mode 100755 index 000000000..1a721bec8 --- /dev/null +++ b/ci/srht/update_download_page @@ -0,0 +1,79 @@ +#!/bin/sh + +set -x +set -e + +VERSION="$1" +SRCDIR="$(pwd)" +NATIVE_TARBALL="zig-linux-$(uname -m)-$VERSION.tar.xz" + +# Check for all the builds being completed. It's expected that this script is run several times +# before they are all complete. +AARCH64_LINUX_JSON_URL="https://ziglang.org/builds/aarch64-linux-$VERSION.json" +X86_64_LINUX_JSON_URL="https://ziglang.org/builds/x86_64-linux-$VERSION.json" +X86_64_WINDOWS_JSON_URL="https://ziglang.org/builds/x86_64-windows-$VERSION.json" +X86_64_MACOS_JSON_URL="https://ziglang.org/builds/x86_64-macos-$VERSION.json" +X86_64_FREEBSD_JSON_URL="https://ziglang.org/builds/x86_64-freebsd-$VERSION.json" + +# If any of these fail, it's not really this job failing; rather we have detected +# that this job will be called again later when other jobs have completed. +curl --fail -I "$AARCH64_LINUX_JSON_URL" >/dev/null || exit 0 +curl --fail -I "$X86_64_LINUX_JSON_URL" >/dev/null || exit 0 +curl --fail -I "$X86_64_WINDOWS_JSON_URL" >/dev/null || exit 0 +curl --fail -I "$X86_64_MACOS_JSON_URL" >/dev/null || exit 0 +curl --fail -I "$X86_64_FREEBSD_JSON_URL" >/dev/null || exit 0 + +# Without --user, this gave me: +# ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied +pip3 install s3cmd --user +S3CMD="$HOME/.local/bin/s3cmd" + +rm -rf .git + +cd "$HOME" +wget "https://ziglang.org/builds/$NATIVE_TARBALL" +tar xf "$NATIVE_TARBALL" +ZIGDIR=$(basename $NATIVE_TARBALL .tar.xz) +ZIG="$ZIGDIR/zig" +LANGREF="$ZIGDIR/langref.html" +SRCTARBALLDIR="zig-$VERSION" +export SRC_TARBALL="$SRCTARBALLDIR.tar.xz" +mv "$SRCDIR" "$SRCTARBALLDIR" +tar cfJ "$SRC_TARBALL" "$SRCTARBALLDIR" +export SRC_SHASUM=$(sha256sum $SRC_TARBALL | cut '-d ' -f1) +export SRC_BYTESIZE=$(wc -c < $SRC_TARBALL) + +X86_64_WINDOWS_JSON=$(curl --fail "$X86_64_WINDOWS_JSON_URL" || exit 1) +export X86_64_WINDOWS_TARBALL="$(echo "$X86_64_WINDOWS_JSON" | jq .tarball -r)" +export X86_64_WINDOWS_BYTESIZE="$(echo "$X86_64_WINDOWS_JSON" | jq .size -r)" +export X86_64_WINDOWS_SHASUM="$(echo "$X86_64_WINDOWS_JSON" | jq .shasum -r)" + +X86_64_MACOS_JSON=$(curl --fail "$X86_64_MACOS_JSON_URL" || exit 1) +export X86_64_MACOS_TARBALL="$(echo "$X86_64_MACOS_JSON" | jq .tarball -r)" +export X86_64_MACOS_BYTESIZE="$(echo "$X86_64_MACOS_JSON" | jq .size -r)" +export X86_64_MACOS_SHASUM="$(echo "$X86_64_MACOS_JSON" | jq .shasum -r)" + +X86_64_LINUX_JSON=$(curl --fail "$X86_64_LINUX_JSON_URL" || exit 1) +export X86_64_LINUX_TARBALL="$(echo "$X86_64_LINUX_JSON" | jq .tarball -r)" +export X86_64_LINUX_BYTESIZE="$(echo "$X86_64_LINUX_JSON" | jq .size -r)" +export X86_64_LINUX_SHASUM="$(echo "$X86_64_LINUX_JSON" | jq .shasum -r)" + +AARCH64_LINUX_JSON=$(curl --fail "$AARCH64_LINUX_JSON_URL" || exit 1) +export AARCH64_LINUX_TARBALL="$(echo "$AARCH64_LINUX_JSON" | jq .tarball -r)" +export AARCH64_LINUX_BYTESIZE="$(echo "$AARCH64_LINUX_JSON" | jq .size -r)" +export AARCH64_LINUX_SHASUM="$(echo "$AARCH64_LINUX_JSON" | jq .shasum -r)" + +X86_64_FREEBSD_JSON=$(curl --fail "$X86_64_FREEBSD_JSON_URL" || exit 1) +export X86_64_FREEBSD_TARBALL="$(echo "$X86_64_FREEBSD_JSON" | jq .tarball -r)" +export X86_64_FREEBSD_BYTESIZE="$(echo "$X86_64_FREEBSD_JSON" | jq .size -r)" +export X86_64_FREEBSD_SHASUM="$(echo "$X86_64_FREEBSD_JSON" | jq .shasum -r)" + +git clone https://github.com/ziglang/www.ziglang.org --depth 1 +cd www.ziglang.org +export MASTER_DATE="$(date +%Y-%m-%d)" +"../$ZIG" run update-download-page.zig + +$S3CMD put -P --no-mime-magic --add-header="cache-control: public, max-age=31536000, immutable" "../$SRC_TARBALL" s3://ziglang.org/builds/ +$S3CMD put -P --no-mime-magic "../$LANGREF" s3://ziglang.org/documentation/master/index.html --add-header="Cache-Control: max-age=0, must-revalidate" +$S3CMD put -P --no-mime-magic www/download/index.html s3://ziglang.org/download/index.html --add-header="Cache-Control: max-age=0, must-revalidate" +$S3CMD put -P --no-mime-magic www/download/index.json s3://ziglang.org/download/index.json --add-header="Cache-Control: max-age=0, must-revalidate" diff --git a/cmake/c_flag_overrides.cmake b/cmake/c_flag_overrides.cmake new file mode 100644 index 000000000..c3225d2c4 --- /dev/null +++ b/cmake/c_flag_overrides.cmake @@ -0,0 +1,13 @@ +if(MSVC) + set(CMAKE_C_FLAGS_DEBUG_INIT + "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1") + + set(CMAKE_C_FLAGS_MINSIZEREL_INIT + "/MT /O1 /Ob1 /D NDEBUG") + + set(CMAKE_C_FLAGS_RELEASE_INIT + "/MT /O2 /Ob1 /D NDEBUG") + + set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT + "/MT /Zi /O2 /Ob1 /D NDEBUG") +endif() diff --git a/cmake/cxx_flag_overrides.cmake b/cmake/cxx_flag_overrides.cmake new file mode 100644 index 000000000..2f5473df9 --- /dev/null +++ b/cmake/cxx_flag_overrides.cmake @@ -0,0 +1,13 @@ +if(MSVC) + set(CMAKE_CXX_FLAGS_DEBUG_INIT + "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1") + + set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT + "/MT /O1 /Ob1 /D NDEBUG") + + set(CMAKE_CXX_FLAGS_RELEASE_INIT + "/MT /O2 /Ob1 /D NDEBUG") + + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT + "/MT /Zi /O2 /Ob1 /D NDEBUG") +endif() diff --git a/doc/docgen.zig b/doc/docgen.zig index 73289e5ed..0218f5041 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -10,13 +10,13 @@ const testing = std.testing; const max_doc_file_size = 10 * 1024 * 1024; -const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt(); -const obj_ext = std.build.Target(std.build.Target.Native).oFileExt(); +const exe_ext = @as(std.build.Target, std.build.Target.Native).exeFileExt(); +const obj_ext = @as(std.build.Target, std.build.Target.Native).oFileExt(); const tmp_dir_name = "docgen_tmp"; const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = &arena.allocator; @@ -51,7 +51,7 @@ pub fn main() !void { var toc = try genToc(allocator, &tokenizer); try fs.makePath(allocator, tmp_dir_name); - defer fs.deleteTree(allocator, tmp_dir_name) catch {}; + defer fs.deleteTree(tmp_dir_name) catch {}; try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); try buffered_out_stream.flush(); @@ -215,32 +215,33 @@ const Tokenizer = struct { } }; -fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: ...) anyerror { +fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: var) anyerror { const loc = tokenizer.getTokenLocation(token); - warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args); + const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 }; + warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); if (loc.line_start <= loc.line_end) { - warn("{}\n", tokenizer.buffer[loc.line_start..loc.line_end]); + warn("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { - warn(" "); + warn(" ", .{}); } } { const caret_count = token.end - token.start; var i: usize = 0; while (i < caret_count) : (i += 1) { - warn("~"); + warn("~", .{}); } } - warn("\n"); + warn("\n", .{}); } return error.ParseError; } fn assertToken(tokenizer: *Tokenizer, token: Token, id: Token.Id) !void { if (token.id != id) { - return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); + return parseError(tokenizer, token, "expected {}, found {}", .{ @tagName(id), @tagName(token.id) }); } } @@ -339,7 +340,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { switch (token.id) { Token.Id.Eof => { if (header_stack_size != 0) { - return parseError(tokenizer, token, "unbalanced headers"); + return parseError(tokenizer, token, "unbalanced headers", .{}); } try toc.write(" \n"); break; @@ -370,13 +371,18 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { .Separator => continue, .TagContent => { const param = tokenizer.buffer[bracket_tok.start..bracket_tok.end]; - if (mem.eql(u8, param, "3col")) { - columns = 3; + if (mem.eql(u8, param, "2col")) { + columns = 2; } else { - return parseError(tokenizer, bracket_tok, "unrecognized header_open param: {}", param); + return parseError( + tokenizer, + bracket_tok, + "unrecognized header_open param: {}", + .{param}, + ); } }, - else => return parseError(tokenizer, bracket_tok, "invalid header_open token"), + else => return parseError(tokenizer, bracket_tok, "invalid header_open token", .{}), } } @@ -391,15 +397,15 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { }, }); if (try urls.put(urlized, tag_token)) |entry| { - parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {}; - parseError(tokenizer, entry.value, "other tag here") catch {}; + parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; + parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; return error.ParseError; } if (last_action == Action.Open) { try toc.writeByte('\n'); try toc.writeByteNTimes(' ', header_stack_size * 4); if (last_columns) |n| { - try toc.print(" -
{#syntax#}u32(0) -% 1 == std.math.maxInt(u32){#endsyntax#}
+
{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}
@@ -1113,7 +1128,7 @@ a -%= b{#endsyntax#} -
{#syntax#}-%i32(std.math.minInt(i32)) == std.math.minInt(i32){#endsyntax#}
+
{#syntax#}-%@as(i32, std.math.minInt(i32)) == std.math.minInt(i32){#endsyntax#}
@@ -1152,7 +1167,7 @@ a *%= b{#endsyntax#} -
{#syntax#}u8(200) *% 2 == 144{#endsyntax#}
+
{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}
@@ -1304,7 +1319,7 @@ a ^= b{#endsyntax#} Bitwise NOT. -
{#syntax#}~u8(0b10101111) == 0b01010000{#endsyntax#}
+
{#syntax#}~@as(u8, 0b10101111) == 0b01010000{#endsyntax#}
@@ -1538,7 +1553,7 @@ value == null{#endsyntax#} const array1 = [_]u32{1,2}; const array2 = [_]u32{3,4}; const together = array1 ++ array2; -mem.eql(u32, together, [_]u32{1,2,3,4}){#endsyntax#} +mem.eql(u32, together, &[_]u32{1,2,3,4}){#endsyntax#} @@ -1640,12 +1655,11 @@ comptime { assert(message.len == 5); } -// a string literal is an array literal +// A string literal is a pointer to an array literal. const same_message = "hello"; comptime { - assert(mem.eql(u8, message, same_message)); - assert(@typeOf(message) == @typeOf(same_message)); + assert(mem.eql(u8, &message, same_message)); } test "iterate over an array" { @@ -1653,7 +1667,7 @@ test "iterate over an array" { for (message) |byte| { sum += byte; } - assert(sum == usize('h') + usize('e') + usize('l') * 2 + usize('o')); + assert(sum == 'h' + 'e' + 'l' * 2 + 'o'); } // modifiable array @@ -1673,7 +1687,7 @@ const part_one = [_]i32{ 1, 2, 3, 4 }; const part_two = [_]i32{ 5, 6, 7, 8 }; const all_of_it = part_one ++ part_two; comptime { - assert(mem.eql(i32, all_of_it, [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 })); + assert(mem.eql(i32, &all_of_it, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 })); } // remember that string literals are arrays @@ -1735,6 +1749,43 @@ test "array initialization with function calls" { {#code_end#} {#see_also|for|Slices#} + {#header_open|Anonymous List Literals#} +

Similar to {#link|Enum Literals#} and {#link|Anonymous Struct Literals#} + the type can be omitted from array literals:

+ {#code_begin|test|anon_list#} +const std = @import("std"); +const assert = std.debug.assert; + +test "anonymous list literal syntax" { + var array: [4]u8 = .{11, 22, 33, 44}; + assert(array[0] == 11); + assert(array[1] == 22); + assert(array[2] == 33); + assert(array[3] == 44); +} + {#code_end#} +

+ If there is no type in the result location then an anonymous list literal actually + turns into a {#link|struct#} with numbered field names: +

+ {#code_begin|test|infer_list_literal#} +const std = @import("std"); +const assert = std.debug.assert; + +test "fully anonymous list literal" { + dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"}); +} + +fn dump(args: var) void { + assert(args.@"0" == 1234); + assert(args.@"1" == 12.34); + assert(args.@"2"); + assert(args.@"3"[0] == 'h'); + assert(args.@"3"[1] == 'i'); +} + {#code_end#} + {#header_close#} + {#header_open|Multidimensional Arrays#}

Mutlidimensional arrays can be created by nesting arrays: @@ -1764,6 +1815,26 @@ test "multidimensional arrays" { } {#code_end#} {#header_close#} + + {#header_open|Sentinel-Terminated Arrays#} +

+ The syntax {#syntax#}[N:x]T{#endsyntax#} describes an array which has a sentinel element of value {#syntax#}x{#endsyntax#} at the + index corresponding to {#syntax#}len{#endsyntax#}. +

+ {#code_begin|test|null_terminated_array#} +const std = @import("std"); +const assert = std.debug.assert; + +test "null terminated array" { + const array = [_:0]u8 {1, 2, 3, 4}; + + assert(@TypeOf(array) == [4:0]u8); + assert(array.len == 4); + assert(array[4] == 0); +} + {#code_end#} + {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#} + {#header_close#} {#header_close#} {#header_open|Vectors#} @@ -1840,12 +1911,12 @@ test "address of syntax" { assert(x_ptr.* == 1234); // When you get the address of a const variable, you get a const pointer to a single item. - assert(@typeOf(x_ptr) == *const i32); + assert(@TypeOf(x_ptr) == *const i32); // If you want to mutate the value, you'd need an address of a mutable variable: var y: i32 = 5678; const y_ptr = &y; - assert(@typeOf(y_ptr) == *i32); + assert(@TypeOf(y_ptr) == *i32); y_ptr.* += 1; assert(y_ptr.* == 5679); } @@ -1856,7 +1927,7 @@ test "pointer array access" { // does not support pointer arithmetic. var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const ptr = &array[2]; - assert(@typeOf(ptr) == *u8); + assert(@TypeOf(ptr) == *u8); assert(array[2] == 3); ptr.* += 1; @@ -1864,7 +1935,7 @@ test "pointer array access" { } {#code_end#}

- In Zig, we prefer slices over pointers to null-terminated arrays. + In Zig, we generally prefer {#link|Slices#} rather than {#link|Sentinel-Terminated Pointers#}. You can turn an array or pointer into a slice using slice syntax.

@@ -1906,10 +1977,10 @@ test "comptime pointers" { const assert = @import("std").debug.assert; test "@ptrToInt and @intToPtr" { - const ptr = @intToPtr(*i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbee0); const addr = @ptrToInt(ptr); - assert(@typeOf(addr) == usize); - assert(addr == 0xdeadbeef); + assert(@TypeOf(addr) == usize); + assert(addr == 0xdeadbee0); } {#code_end#}

Zig is able to preserve memory addresses in comptime code, as long as @@ -1921,10 +1992,10 @@ test "comptime @intToPtr" { comptime { // Zig is able to do this at compile-time, as long as // ptr is never dereferenced. - const ptr = @intToPtr(*i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbee0); const addr = @ptrToInt(ptr); - assert(@typeOf(addr) == usize); - assert(addr == 0xdeadbeef); + assert(@TypeOf(addr) == usize); + assert(addr == 0xdeadbee0); } } {#code_end#} @@ -1939,7 +2010,7 @@ const assert = @import("std").debug.assert; test "volatile" { const mmio_ptr = @intToPtr(*volatile u8, 0x12345678); - assert(@typeOf(mmio_ptr) == *volatile u8); + assert(@TypeOf(mmio_ptr) == *volatile u8); } {#code_end#}

@@ -1996,15 +2067,15 @@ const builtin = @import("builtin"); test "variable alignment" { var x: i32 = 1234; - const align_of_i32 = @alignOf(@typeOf(x)); - assert(@typeOf(&x) == *i32); + const align_of_i32 = @alignOf(@TypeOf(x)); + assert(@TypeOf(&x) == *i32); assert(*i32 == *align(align_of_i32) i32); if (builtin.arch == builtin.Arch.x86_64) { assert((*i32).alignment == 4); } } {#code_end#} -

In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|implicitly cast|Implicit Casts#} to a +

In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|coerced|Type Coercion#} to a {#syntax#}*const i32{#endsyntax#}, a pointer with a larger alignment can be implicitly cast to a pointer with a smaller alignment, but not vice versa.

@@ -2018,10 +2089,10 @@ const assert = @import("std").debug.assert; var foo: u8 align(4) = 100; test "global variable alignment" { - assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == *align(4) u8); - const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); + assert(@TypeOf(&foo).alignment == 4); + assert(@TypeOf(&foo) == *align(4) u8); + const slice = @as(*[1]u8, &foo)[0..]; + assert(@TypeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } @@ -2030,8 +2101,8 @@ fn noop4() align(4) void {} test "function alignment" { assert(derp() == 1234); - assert(@typeOf(noop1) == fn() align(1) void); - assert(@typeOf(noop4) == fn() align(4) void); + assert(@TypeOf(noop1) == fn() align(1) void); + assert(@TypeOf(noop4) == fn() align(4) void); noop1(); noop4(); } @@ -2062,8 +2133,10 @@ fn foo(bytes: []u8) u32 {

This pointer attribute allows a pointer to have address zero. This is only ever needed on the freestanding OS target, where the address zero is mappable. If you want to represent null pointers, use - {#link|Optional Pointers#} instead. In this code example, if the pointer did not have the - {#syntax#}allowzero{#endsyntax#} attribute, this would be a {#link|Pointer Cast Invalid Null#} panic: + {#link|Optional Pointers#} instead. {#link|Optional Pointers#} with {#syntax#}allowzero{#endsyntax#} + are not the same size as pointers. In this code example, if the pointer + did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a + {#link|Pointer Cast Invalid Null#} panic:

{#code_begin|test|allowzero#} const std = @import("std"); @@ -2076,6 +2149,30 @@ test "allowzero" { } {#code_end#} {#header_close#} + + {#header_open|Sentinel-Terminated Pointers#} +

+ The syntax {#syntax#}[*:x]T{#endsyntax#} describes a pointer that + has a length determined by a sentinel value. This provides protection + against buffer overflow and overreads. +

+ {#code_begin|exe_build_err#} + {#link_libc#} +const std = @import("std"); + +// This is also available as `std.c.printf`. +pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + +pub fn main() anyerror!void { + _ = printf("Hello, world!\n"); // OK + + const msg = "Hello, world!\n"; + const non_null_terminated_msg: [msg.len]u8 = msg.*; + _ = printf(&non_null_terminated_msg); +} + {#code_end#} + {#see_also|Sentinel-Terminated Slices|Sentinel-Terminated Arrays#} + {#header_close#} {#header_close#} {#header_open|Slices#} @@ -2094,8 +2191,8 @@ test "basic slices" { // Using the address-of operator on a slice gives a pointer to a single // item, while using the `ptr` field gives an unknown length pointer. - assert(@typeOf(slice.ptr) == [*]i32); - assert(@typeOf(&slice[0]) == *i32); + assert(@TypeOf(slice.ptr) == [*]i32); + assert(@TypeOf(&slice[0]) == *i32); assert(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0])); // Slices have array bounds checking. If you try to access something out @@ -2115,7 +2212,7 @@ const fmt = @import("std").fmt; test "using slices for strings" { // Zig has no concept of strings. String literals are arrays of u8, and // in general the string type is []u8 (slice of u8). - // Here we implicitly cast [5]u8 to []const u8 + // Here we coerce [5]u8 to []const u8 const hello: []const u8 = "hello"; const world: []const u8 = "ไธ–็•Œ"; @@ -2123,7 +2220,7 @@ test "using slices for strings" { // You can use slice syntax on an array to convert an array into a slice. const all_together_slice = all_together[0..]; // String concatenation example. - const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", hello, world); + const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{hello, world}); // Generally, you can use UTF-8 and not worry about whether something is a // string. If you don't need to deal with individual characters, no need @@ -2140,7 +2237,7 @@ test "slice pointer" { slice[2] = 3; assert(slice[2] == 3); // The slice is mutable because we sliced a mutable pointer. - assert(@typeOf(slice) == []u8); + assert(@TypeOf(slice) == []u8); // You can also slice a slice: const slice2 = slice[2..3]; @@ -2159,7 +2256,29 @@ test "slice widening" { } {#code_end#} {#see_also|Pointers|for|Arrays#} + + {#header_open|Sentinel-Terminated Slices#} +

+ The syntax {#syntax#}[:x]T{#endsyntax#} is a slice which has a runtime known length + and also guarantees a sentinel value at the element indexed by the length. The type does not + guarantee that there are no sentinel elements before that. Sentinel-terminated slices allow element + access to the {#syntax#}len{#endsyntax#} index. +

+ {#code_begin|test|null_terminated_slice#} +const std = @import("std"); +const assert = std.debug.assert; + +test "null terminated slice" { + const slice: [:0]const u8 = "hello"; + + assert(slice.len == 5); + assert(slice[5] == 0); +} + {#code_end#} + {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Arrays#} {#header_close#} + {#header_close#} + {#header_open|struct#} {#code_begin|test|structs#} // Declare a struct. @@ -2527,7 +2646,8 @@ test "overaligned pointer to packed struct" { Don't worry, there will be a good solution for this use case in zig.

{#header_close#} - {#header_open|struct Naming#} + + {#header_open|Struct Naming#}

Since all structs are anonymous, Zig infers the type name based on a few rules.

- This kind of type resolution chooses a type that all peer types can implicitly cast into. Here are + This kind of type resolution chooses a type that all peer types can coerce into. Here are some examples:

- {#code_begin|test#} + {#code_begin|test|peer_type_resolution#} const std = @import("std"); const assert = std.debug.assert; const mem = std.mem; @@ -4958,7 +5187,7 @@ test "peer resolve int widening" { var b: i16 = 34; var c = a + b; assert(c == 46); - assert(@typeOf(c) == i16); + assert(@TypeOf(c) == i16); } test "peer resolve arrays of different size to const slice" { @@ -4976,8 +5205,8 @@ test "peer resolve array and const slice" { comptime testPeerResolveArrayConstSlice(true); } fn testPeerResolveArrayConstSlice(b: bool) void { - const value1 = if (b) "aoeu" else ([]const u8)("zz"); - const value2 = if (b) ([]const u8)("zz") else "aoeu"; + const value1 = if (b) "aoeu" else @as([]const u8, "zz"); + const value2 = if (b) @as([]const u8, "zz") else "aoeu"; assert(mem.eql(u8, value1, "aoeu")); assert(mem.eql(u8, value2, "zz")); } @@ -4992,13 +5221,13 @@ test "peer type resolution: ?T and T" { } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { if (c) { - return if (b) null else usize(0); + return if (b) null else @as(usize, 0); } - return usize(3); + return @as(usize, 3); } -test "peer type resolution: [0]u8 and []const u8" { +test "peer type resolution: *[0]u8 and []const u8" { assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { @@ -5008,20 +5237,20 @@ test "peer type resolution: [0]u8 and []const u8" { } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { if (a) { - return [_]u8{}; + return &[_]u8{}; } return slice[0..1]; } -test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { +test "peer type resolution: *[0]u8, []const u8, and anyerror![]u8" { { - var data = "hi"; + var data = "hi".*; const slice = data[0..]; assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { - var data = "hi"; + var data = "hi".*; const slice = data[0..]; assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); @@ -5029,15 +5258,15 @@ test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { if (a) { - return [_]u8{}; + return &[_]u8{}; } return slice[0..1]; } test "peer type resolution: *const T and ?*T" { - const a = @intToPtr(*const usize, 0x123456789); - const b = @intToPtr(?*usize, 0x123456789); + const a = @intToPtr(*const usize, 0x123456780); + const b = @intToPtr(?*usize, 0x123456780); assert(a == b); assert(b == a); } @@ -5258,11 +5487,9 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
  • In the function definition, the value is known at compile-time.
  • -

    -

    For example, if we were to introduce another function to the above snippet:

    - {#code_begin|test_err|cannot store runtime value in type 'type'#} + {#code_begin|test_err|values of type 'type' must be comptime known#} fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } @@ -5559,7 +5786,7 @@ test "fibonacci" {

    {#code_begin|test#} const first_25_primes = firstNPrimes(25); -const sum_of_first_25_primes = sum(first_25_primes); +const sum_of_first_25_primes = sum(&first_25_primes); fn firstNPrimes(comptime n: usize) [n]i32 { var prime_list: [n]i32 = undefined; @@ -5658,7 +5885,7 @@ const a_number: i32 = 1234; const a_string = "foobar"; pub fn main() void { - warn("here is a string: '{}' here is a number: {}\n", a_string, a_number); + warn("here is a string: '{}' here is a number: {}\n", .{a_string, a_number}); } {#code_end#} @@ -5668,7 +5895,7 @@ pub fn main() void { {#code_begin|syntax#} /// Calls print and then flushes the buffer. -pub fn printf(self: *OutStream, comptime format: []const u8, args: ...) anyerror!void { +pub fn printf(self: *OutStream, comptime format: []const u8, args: var) anyerror!void { const State = enum { Start, OpenBrace, @@ -5755,7 +5982,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void {

    {#code_begin|syntax#} pub fn printValue(self: *OutStream, value: var) !void { - const T = @typeOf(value); + const T = @TypeOf(value); if (@isInteger(T)) { return self.printInt(T, value); } else if (@isFloat(T)) { @@ -5775,8 +6002,11 @@ const a_number: i32 = 1234; const a_string = "foobar"; test "printf too many arguments" { - warn("here is a string: '{}' here is a number: {}\n", - a_string, a_number, a_number); + warn("here is a string: '{}' here is a number: {}\n", .{ + a_string, + a_number, + a_number, + }); } {#code_end#}

    @@ -5784,7 +6014,7 @@ test "printf too many arguments" {

    Zig doesn't care whether the format argument is a string literal, - only that it is a compile-time known value that is implicitly castable to a {#syntax#}[]const u8{#endsyntax#}: + only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:

    {#code_begin|exe|printf#} const warn = @import("std").debug.warn; @@ -5794,7 +6024,7 @@ const a_string = "foobar"; const fmt = "here is a string: '{}' here is a number: {}\n"; pub fn main() void { - warn(fmt, a_string, a_number); + warn(fmt, .{a_string, a_number}); } {#code_end#}

    @@ -5819,7 +6049,7 @@ pub fn main() void { {#target_linux_x86_64#} pub fn main() noreturn { const msg = "hello world\n"; - _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(&msg), msg.len); + _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len); _ = syscall1(SYS_exit, 0); unreachable; } @@ -6068,7 +6298,7 @@ test "async function suspend with block" { fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@frame()) == *@Frame(testSuspendBlock)); + comptime assert(@TypeOf(@frame()) == *@Frame(testSuspendBlock)); the_frame = @frame(); } result = true; @@ -6135,7 +6365,7 @@ test "async and await" { fn amain() void { var frame = async func(); - comptime assert(@typeOf(frame) == @Frame(func)); + comptime assert(@TypeOf(frame) == @Frame(func)); const ptr: anyframe->void = &frame; const any_ptr: anyframe = ptr; @@ -6154,7 +6384,7 @@ fn func() void {

    {#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that - implicitly casts to {#syntax#}anyframe->T{#endsyntax#}. + coerces to {#syntax#}anyframe->T{#endsyntax#}.

    There is a common misconception that {#syntax#}await{#endsyntax#} resumes the target function. @@ -6177,7 +6407,7 @@ test "async function await" { resume the_frame; seq('i'); assert(final_result == 1234); - assert(std.mem.eql(u8, seq_points, "abcdefghi")); + assert(std.mem.eql(u8, &seq_points, "abcdefghi")); } fn amain() void { seq('b'); @@ -6232,7 +6462,7 @@ pub fn main() void { fn amainWrap() void { amain() catch |e| { - std.debug.warn("{}\n", e); + std.debug.warn("{}\n", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -6241,7 +6471,7 @@ fn amainWrap() void { } fn amain() !void { - const allocator = std.heap.direct_allocator; + const allocator = std.heap.page_allocator; var download_frame = async fetchUrl(allocator, "https://example.com/"); var awaited_download_frame = false; errdefer if (!awaited_download_frame) { @@ -6262,8 +6492,8 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.warn("download_text: {}\n", download_text); - std.debug.warn("file_text: {}\n", file_text); + std.debug.warn("download_text: {}\n", .{download_text}); + std.debug.warn("file_text: {}\n", .{file_text}); } var global_download_frame: anyframe = undefined; @@ -6273,7 +6503,7 @@ fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 { suspend { global_download_frame = @frame(); } - std.debug.warn("fetchUrl returning\n"); + std.debug.warn("fetchUrl returning\n", .{}); return result; } @@ -6284,7 +6514,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 { suspend { global_file_frame = @frame(); } - std.debug.warn("readFile returning\n"); + std.debug.warn("readFile returning\n", .{}); return result; } {#code_end#} @@ -6302,7 +6532,7 @@ pub fn main() void { fn amainWrap() void { amain() catch |e| { - std.debug.warn("{}\n", e); + std.debug.warn("{}\n", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -6311,7 +6541,7 @@ fn amainWrap() void { } fn amain() !void { - const allocator = std.heap.direct_allocator; + const allocator = std.heap.page_allocator; var download_frame = async fetchUrl(allocator, "https://example.com/"); var awaited_download_frame = false; errdefer if (!awaited_download_frame) { @@ -6332,21 +6562,21 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.warn("download_text: {}\n", download_text); - std.debug.warn("file_text: {}\n", file_text); + std.debug.warn("download_text: {}\n", .{download_text}); + std.debug.warn("file_text: {}\n", .{file_text}); } fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 { const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents"); errdefer allocator.free(result); - std.debug.warn("fetchUrl returning\n"); + std.debug.warn("fetchUrl returning\n", .{}); return result; } fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 { const result = try std.mem.dupe(allocator, u8, "this is the file contents"); errdefer allocator.free(result); - std.debug.warn("readFile returning\n"); + std.debug.warn("readFile returning\n", .{}); return result; } {#code_end#} @@ -6359,7 +6589,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 { {#header_close#} {#header_close#} - {#header_open|Builtin Functions|3col#} + {#header_open|Builtin Functions|2col#}

    Builtin functions are provided by the compiler and are prefixed with @. The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known @@ -6414,6 +6644,14 @@ comptime {

    {#header_close#} + {#header_open|@as#} +
    {#syntax#}@as(comptime T: type, expression) T{#endsyntax#}
    +

    + Performs {#link|Type Coercion#}. This cast is allowed when the conversion is unambiguous and safe, + and is the preferred way to convert between types, whenever possible. +

    + {#header_close#} + {#header_open|@asyncCall#}
    {#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}

    @@ -6462,14 +6700,14 @@ async fn func(y: *i32) void { This builtin function atomically dereferences a pointer and returns the value.

    - {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, - or an integer whose bit count meets these requirements: + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, + an integer whose bit count meets these requirements:

    + or an enum with a valid integer tag type.

    TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe we can remove this restriction @@ -6497,7 +6735,7 @@ async fn func(y: *i32) void { Supported operations:

    {#header_close#} + {#header_open|@atomicStore#} +
    {#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}
    +

    + This builtin function atomically stores a value. +

    +

    + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float, + an integer whose bit count meets these requirements: +

    + or an enum with a valid integer tag type. +

    + TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe + we can remove this restriction +

    + {#header_close#} {#header_open|@bitCast#}
    {#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}

    Converts a value of one type to another type.

    - Asserts that {#syntax#}@sizeOf(@typeOf(value)) == @sizeOf(DestType){#endsyntax#}. + Asserts that {#syntax#}@sizeOf(@TypeOf(value)) == @sizeOf(DestType){#endsyntax#}.

    Asserts that {#syntax#}@typeId(DestType) != @import("builtin").TypeId.Pointer{#endsyntax#}. Use {#syntax#}@ptrCast{#endsyntax#} or {#syntax#}@intToPtr{#endsyntax#} if you need this. @@ -6572,9 +6829,12 @@ async fn func(y: *i32) void { {#header_open|@mulAdd#}

    {#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}

    - Fused multiply add (for floats), similar to {#syntax#}(a * b) + c{#endsyntax#}, except + Fused multiply add, similar to {#syntax#}(a * b) + c{#endsyntax#}, except only rounds once, and is thus more accurate.

    +

    + Supports Floats and Vectors of floats. +

    {#header_close#} {#header_open|@byteSwap#} @@ -6627,6 +6887,102 @@ async fn func(y: *i32) void {

    {#header_close#} + {#header_open|@call#} +
    {#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}
    +

    + Calls a function, in the same way that invoking an expression with parentheses does: +

    + {#code_begin|test|call#} +const assert = @import("std").debug.assert; + +test "noinline function call" { + assert(@call(.{}, add, .{3, 9}) == 12); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#} +

    + {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The + {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: +

    + {#code_begin|syntax#} +pub const CallOptions = struct { + modifier: Modifier = .auto, + stack: ?[]align(std.Target.stack_align) u8 = null, + + pub const Modifier = enum { + /// Equivalent to function call syntax. + auto, + + /// Equivalent to async keyword used with function call syntax. + async_kw, + + /// Prevents tail call optimization. This guarantees that the return + /// address will point to the callsite, as opposed to the callsite's + /// callsite. If the call is otherwise required to be tail-called + /// or inlined, a compile error is emitted instead. + never_tail, + + /// Guarantees that the call will not be inlined. If the call is + /// otherwise required to be inlined, a compile error is emitted instead. + never_inline, + + /// Asserts that the function call will not suspend. This allows a + /// non-async function to call an async function. + no_async, + + /// Guarantees that the call will be generated with tail call optimization. + /// If this is not possible, a compile error is emitted instead. + always_tail, + + /// Guarantees that the call will inlined at the callsite. + /// If this is not possible, a compile error is emitted instead. + always_inline, + + /// Evaluates the call at compile-time. If the call cannot be completed at + /// compile-time, a compile error is emitted instead. + compile_time, + }; +}; + {#code_end#} + + {#header_open|Calling with a New Stack#} +

    + When the {#syntax#}stack{#endsyntax#} option is provided, instead of using the same stack as the caller, the function uses the provided stack. +

    + {#code_begin|test|new_stack_call#} +const std = @import("std"); +const assert = std.debug.assert; + +var new_stack_bytes: [1024]u8 align(16) = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @call(.{.stack = new_stack_bytes[0..512]}, targetFunction, .{arg}); + const b = @call(.{.stack = new_stack_bytes[512..]}, targetFunction, .{arg}); + _ = targetFunction(arg); + + assert(arg == 1234); + assert(a < b); +} + +fn targetFunction(x: i32) usize { + assert(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + ptr.* += 1; + + assert(local_variable == 43); + return @ptrToInt(ptr); +} + {#code_end#} + {#header_close#} + {#header_close#} + {#header_open|@cDefine#}
    {#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}

    @@ -6677,7 +7033,7 @@ async fn func(y: *i32) void { This function can only occur inside {#syntax#}@cImport{#endsyntax#}.

    - This appends #include <$path>\n to the {#syntax#}c_import{#endsyntax#} + This appends #include <$path>\n to the {#syntax#}c_import{#endsyntax#} temporary buffer.

    {#see_also|Import from C Header File|@cImport|@cDefine|@cUndef#} @@ -6725,7 +7081,7 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v

    {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("builtin").AtomicOrder{#endsyntax#}.

    -

    {#syntax#}@typeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}

    +

    {#syntax#}@TypeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}

    {#see_also|Compile Variables|cmpxchgWeak#} {#header_close#} {#header_open|@cmpxchgWeak#} @@ -6753,7 +7109,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val

    {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("builtin").AtomicOrder{#endsyntax#}.

    -

    {#syntax#}@typeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}

    +

    {#syntax#}@TypeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}

    {#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} @@ -6798,10 +7154,9 @@ const num1 = blk: { test "main" { @compileLog("comptime in main"); - warn("Runtime in main, num1 = {}.\n", num1); + warn("Runtime in main, num1 = {}.\n", .{num1}); } {#code_end#} -

    will ouput:

    @@ -6820,7 +7175,7 @@ const num1 = blk: { }; test "main" { - warn("Runtime in main, num1 = {}.\n", num1); + warn("Runtime in main, num1 = {}.\n", .{num1}); } {#code_end#} {#header_close#} @@ -6973,21 +7328,21 @@ test "main" { {#header_close#} {#header_open|@export#} -
    {#syntax#}@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) void{#endsyntax#}
    +
    {#syntax#}@export(target: var, comptime options: std.builtin.ExportOptions) void{#endsyntax#}

    Creates a symbol in the output object file.

    This function can be called from a {#link|comptime#} block to conditionally export symbols. When {#syntax#}target{#endsyntax#} is a function with the C calling convention and - {#syntax#}linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to + {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to the {#syntax#}export{#endsyntax#} keyword used on a function:

    {#code_begin|obj#} const builtin = @import("builtin"); comptime { - @export("foo", internalName, builtin.GlobalLinkage.Strong); + @export(internalName, .{ .name = "foo", .linkage = .Strong }); } extern fn internalName() void {} @@ -7077,7 +7432,7 @@ test "field access by string" {
    {#syntax#}@frame() *@Frame(func){#endsyntax#}

    This function returns a pointer to the frame for a given function. This type - can be {#link|implicitly cast|Implicit Casts#} to {#syntax#}anyframe->T{#endsyntax#} and + can be {#link|coerced|Type Coercion#} to {#syntax#}anyframe->T{#endsyntax#} and to {#syntax#}anyframe{#endsyntax#}, where {#syntax#}T{#endsyntax#} is the return type of the function in scope.

    @@ -7101,7 +7456,7 @@ test "field access by string" { const std = @import("std"); test "heap allocated frame" { - const frame = try std.heap.direct_allocator.create(@Frame(func)); + const frame = try std.heap.page_allocator.create(@Frame(func)); frame.* = async func(); } @@ -7213,27 +7568,6 @@ test "@hasDecl" { {#see_also|Compile Variables|@embedFile#} {#header_close#} - {#header_open|@inlineCall#} -
    {#syntax#}@inlineCall(function: X, args: ...) Y{#endsyntax#}
    -

    - This calls a function, in the same way that invoking an expression with parentheses does: -

    - {#code_begin|test#} -const assert = @import("std").debug.assert; - -test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { return a + b; } - {#code_end#} -

    - Unlike a normal function call, however, {#syntax#}@inlineCall{#endsyntax#} guarantees that the call - will be inlined. If the call cannot be inlined, a compile error is emitted. -

    - {#see_also|@noInlineCall#} - {#header_close#} - {#header_open|@intCast#}
    {#syntax#}@intCast(comptime DestType: type, int: var) DestType{#endsyntax#}

    @@ -7241,6 +7575,10 @@ fn add(a: i32, b: i32) i32 { return a + b; } Attempting to convert a number which is out of range of the destination type results in safety-protected {#link|Undefined Behavior#}.

    +

    + If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, + then this is semantically equivalent to {#link|Type Coercion#}. +

    {#header_close#} {#header_open|@intToEnum#} @@ -7387,71 +7725,6 @@ mem.set(u8, dest, c);{#endsyntax#}

    {#header_close#} - {#header_open|@newStackCall#} -
    {#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}
    -

    - This calls a function, in the same way that invoking an expression with parentheses does. However, - instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#} - parameter. -

    -

    - The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific - number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can - also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}. -

    - {#code_begin|test#} -const std = @import("std"); -const assert = std.debug.assert; - -var new_stack_bytes: [1024]u8 align(16) = undefined; - -test "calling a function with a new stack" { - const arg = 1234; - - const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); - const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); - _ = targetFunction(arg); - - assert(arg == 1234); - assert(a < b); -} - -fn targetFunction(x: i32) usize { - assert(x == 1234); - - var local_variable: i32 = 42; - const ptr = &local_variable; - ptr.* += 1; - - assert(local_variable == 43); - return @ptrToInt(ptr); -} - {#code_end#} - {#header_close#} - - {#header_open|@noInlineCall#} -
    {#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}
    -

    - This calls a function, in the same way that invoking an expression with parentheses does: -

    - {#code_begin|test#} -const assert = @import("std").debug.assert; - -test "noinline function call" { - assert(@noInlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - {#code_end#} -

    - Unlike a normal function call, however, {#syntax#}@noInlineCall{#endsyntax#} guarantees that the call - will not be inlined. If the call must be inlined, a compile error is emitted. -

    - {#see_also|@inlineCall#} - {#header_close#} - {#header_open|@OpaqueType#}
    {#syntax#}@OpaqueType() type{#endsyntax#}

    @@ -7466,7 +7739,7 @@ const Derp = @OpaqueType(); const Wat = @OpaqueType(); extern fn bar(d: *Derp) void; -export fn foo(w: *Wat) void { +fn foo(w: *Wat) callconv(.C) void { bar(w); } @@ -7729,7 +8002,7 @@ test "@setRuntimeSafety" { {#syntax#}b{#endsyntax#}. Positive numbers select from {#syntax#}a{#endsyntax#} starting at 0. Negative values select from {#syntax#}b{#endsyntax#}, starting at {#syntax#}-1{#endsyntax#} and going down. It is recommended to use the {#syntax#}~{#endsyntax#} operator from indexes from {#syntax#}b{#endsyntax#} - so that both indexes can start from {#syntax#}0{#endsyntax#} (i.e. {#syntax#}~i32(0){#endsyntax#} is + so that both indexes can start from {#syntax#}0{#endsyntax#} (i.e. {#syntax#}~@as(i32, 0){#endsyntax#} is {#syntax#}-1{#endsyntax#}).

    @@ -7783,7 +8056,7 @@ test "@setRuntimeSafety" { {#header_close#} {#header_open|@splat#} -

    {#syntax#}@splat(comptime len: u32, scalar: var) @Vector(len, @typeOf(scalar)){#endsyntax#}
    +
    {#syntax#}@splat(comptime len: u32, scalar: var) @Vector(len, @TypeOf(scalar)){#endsyntax#}

    Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value {#syntax#}scalar{#endsyntax#}: @@ -7795,8 +8068,8 @@ const assert = std.debug.assert; test "vector @splat" { const scalar: u32 = 5; const result = @splat(4, scalar); - comptime assert(@typeOf(result) == @Vector(4, u32)); - assert(std.mem.eql(u32, ([4]u32)(result), [_]u32{ 5, 5, 5, 5 })); + comptime assert(@TypeOf(result) == @Vector(4, u32)); + assert(std.mem.eql(u32, &@as([4]u32, result), &[_]u32{ 5, 5, 5, 5 })); } {#code_end#}

    @@ -7807,94 +8080,146 @@ test "vector @splat" { {#header_close#} {#header_open|@sqrt#} -

    {#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@sqrt(value: var) @TypeOf(value){#endsyntax#}

    Performs the square root of a floating point number. Uses a dedicated hardware instruction - when available. Supports {#syntax#}f16{#endsyntax#}, {#syntax#}f32{#endsyntax#}, {#syntax#}f64{#endsyntax#}, and {#syntax#}f128{#endsyntax#}, as well as vectors. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@sin#} -
    {#syntax#}@sin(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@sin(value: var) @TypeOf(value){#endsyntax#}

    Sine trigometric function on a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@cos#} -
    {#syntax#}@cos(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@cos(value: var) @TypeOf(value){#endsyntax#}

    Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@exp#} -
    {#syntax#}@exp(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@exp(value: var) @TypeOf(value){#endsyntax#}

    Base-e exponential function on a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@exp2#} -
    {#syntax#}@exp2(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@exp2(value: var) @TypeOf(value){#endsyntax#}

    Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} - {#header_open|@ln#} -
    {#syntax#}@ln(comptime T: type, value: T) T{#endsyntax#}
    + {#header_open|@log#} +
    {#syntax#}@log(value: var) @TypeOf(value){#endsyntax#}

    Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@log2#} -
    {#syntax#}@log2(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@log2(value: var) @TypeOf(value){#endsyntax#}

    Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@log10#} -
    {#syntax#}@log10(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@log10(value: var) @TypeOf(value){#endsyntax#}

    Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@fabs#} -
    {#syntax#}@fabs(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@fabs(value: var) @TypeOf(value){#endsyntax#}

    Returns the absolute value of a floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@floor#} -
    {#syntax#}@floor(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@floor(value: var) @TypeOf(value){#endsyntax#}

    - Returns the largest integral value not greater than the given floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + Returns the largest integral value not greater than the given floating point number. + Uses a dedicated hardware instruction when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@ceil#} -
    {#syntax#}@ceil(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@ceil(value: var) @TypeOf(value){#endsyntax#}

    - Returns the largest integral value not less than the given floating point number. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + Returns the largest integral value not less than the given floating point number. + Uses a dedicated hardware instruction when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@trunc#} -
    {#syntax#}@trunc(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@trunc(value: var) @TypeOf(value){#endsyntax#}

    - Rounds the given floating point number to an integer, towards zero. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + Rounds the given floating point number to an integer, towards zero. + Uses a dedicated hardware instruction when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} {#header_open|@round#} -
    {#syntax#}@round(comptime T: type, value: T) T{#endsyntax#}
    +
    {#syntax#}@round(value: var) @TypeOf(value){#endsyntax#}

    Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction - when available. Currently supports {#syntax#}f32{#endsyntax#} and {#syntax#}f64{#endsyntax#}. + when available. +

    +

    + Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types.

    {#header_close#} @@ -7992,10 +8317,6 @@ test "integer truncation" { This function always truncates the significant bits of the integer, regardless of endianness on the target platform.

    -

    - If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, - then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}. -

    {#header_close#} {#header_open|@Type#} @@ -8012,13 +8333,13 @@ test "integer truncation" {
  • {#syntax#}noreturn{#endsyntax#}
  • {#syntax#}void{#endsyntax#}
  • {#syntax#}bool{#endsyntax#}
  • -
  • {#link|Integers#}
  • - The maximum bit count for an integer type is {#syntax#}65535{#endsyntax#}. +
  • {#link|Integers#} - The maximum bit count for an integer type is {#syntax#}65535{#endsyntax#}.
  • {#link|Floats#}
  • {#link|Pointers#}
  • {#syntax#}comptime_int{#endsyntax#}
  • {#syntax#}comptime_float{#endsyntax#}
  • -
  • {#syntax#}@typeOf(undefined){#endsyntax#}
  • -
  • {#syntax#}@typeOf(null){#endsyntax#}
  • +
  • {#syntax#}@TypeOf(undefined){#endsyntax#}
  • +
  • {#syntax#}@TypeOf(null){#endsyntax#}
  • For these types it is a @@ -8044,7 +8365,6 @@ test "integer truncation" {

  • {#link|union#}
  • {#link|Functions#}
  • BoundFn
  • -
  • ArgTuple
  • {#link|struct#}
  • {#header_close#} @@ -8071,205 +8391,25 @@ pub const TypeId = enum { Null, Optional, ErrorUnion, - Error, + ErrorSet, Enum, Union, Fn, - Block, BoundFn, - ArgTuple, Opaque, + Frame, + AnyFrame, + Vector, + EnumLiteral, }; {#code_end#} {#header_close#} {#header_open|@typeInfo#} -
    {#syntax#}@typeInfo(comptime T: type) @import("builtin").TypeInfo{#endsyntax#}
    +
    {#syntax#}@typeInfo(comptime T: type) @import("std").builtin.TypeInfo{#endsyntax#}

    - Returns information on the type. Returns a value of the following union: + Provides type reflection.

    - {#code_begin|syntax#} -pub const TypeInfo = union(TypeId) { - Type: void, - Void: void, - Bool: void, - NoReturn: void, - Int: Int, - Float: Float, - Pointer: Pointer, - Array: Array, - Struct: Struct, - ComptimeFloat: void, - ComptimeInt: void, - Undefined: void, - Null: void, - Optional: Optional, - ErrorUnion: ErrorUnion, - ErrorSet: ErrorSet, - Enum: Enum, - Union: Union, - Fn: Fn, - BoundFn: Fn, - ArgTuple: void, - Opaque: void, - Promise: Promise, - Vector: Vector, - EnumLiteral: void, - - - pub const Int = struct { - is_signed: bool, - bits: comptime_int, - }; - - pub const Float = struct { - bits: comptime_int, - }; - - pub const Pointer = struct { - size: Size, - is_const: bool, - is_volatile: bool, - alignment: comptime_int, - child: type, - is_allowzero: bool, - - pub const Size = enum { - One, - Many, - Slice, - C, - }; - }; - - pub const Array = struct { - len: comptime_int, - child: type, - }; - - pub const ContainerLayout = enum { - Auto, - Extern, - Packed, - }; - - pub const StructField = struct { - name: []const u8, - offset: ?comptime_int, - field_type: type, - }; - - pub const Struct = struct { - layout: ContainerLayout, - fields: []StructField, - decls: []Declaration, - }; - - pub const Optional = struct { - child: type, - }; - - pub const ErrorUnion = struct { - error_set: type, - payload: type, - }; - - pub const Error = struct { - name: []const u8, - value: comptime_int, - }; - - pub const ErrorSet = ?[]Error; - - pub const EnumField = struct { - name: []const u8, - value: comptime_int, - }; - - pub const Enum = struct { - layout: ContainerLayout, - tag_type: type, - fields: []EnumField, - decls: []Declaration, - }; - - pub const UnionField = struct { - name: []const u8, - enum_field: ?EnumField, - field_type: type, - }; - - pub const Union = struct { - layout: ContainerLayout, - tag_type: ?type, - fields: []UnionField, - decls: []Declaration, - }; - - pub const CallingConvention = enum { - Unspecified, - C, - Cold, - Naked, - Stdcall, - Async, - }; - - pub const FnArg = struct { - is_generic: bool, - is_noalias: bool, - arg_type: ?type, - }; - - pub const Fn = struct { - calling_convention: CallingConvention, - is_generic: bool, - is_var_args: bool, - return_type: ?type, - async_allocator_type: ?type, - args: []FnArg, - }; - - pub const Promise = struct { - child: ?type, - }; - - pub const Vector = struct { - len: comptime_int, - child: type, - }; - - pub const Declaration = struct { - name: []const u8, - is_pub: bool, - data: Data, - - pub const Data = union(enum) { - Type: type, - Var: type, - Fn: FnDecl, - - pub const FnDecl = struct { - fn_type: type, - inline_type: Inline, - calling_convention: CallingConvention, - is_var_args: bool, - is_extern: bool, - is_export: bool, - lib_name: ?[]const u8, - return_type: type, - arg_names: [][] const u8, - - pub const Inline = enum { - Auto, - Always, - Never, - }; - }; - }; - }; -}; - {#code_end#}

    For {#link|structs|struct#}, {#link|unions|union#}, {#link|enums|enum#}, and {#link|error sets|Error Set Type#}, the fields are guaranteed to be in the same @@ -8286,20 +8426,20 @@ pub const TypeInfo = union(TypeId) { {#header_close#} - {#header_open|@typeOf#} -

    {#syntax#}@typeOf(expression) type{#endsyntax#}
    + {#header_open|@TypeOf#} +
    {#syntax#}@TypeOf(expression) type{#endsyntax#}

    This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated.

    -

    {#syntax#}@typeOf{#endsyntax#} guarantees no run-time side-effects within the expression:

    +

    {#syntax#}@TypeOf{#endsyntax#} guarantees no run-time side-effects within the expression:

    {#code_begin|test#} const std = @import("std"); const assert = std.debug.assert; test "no runtime side effects" { var data: i32 = 0; - const T = @typeOf(foo(i32, &data)); + const T = @TypeOf(foo(i32, &data)); comptime assert(T == i32); assert(data == 0); } @@ -8406,7 +8546,7 @@ pub fn build(b: *Builder) void { {#header_close#} {#header_open|Single Threaded Builds#} -

    Zig has a compile option --single-threaded which has the following effects: +

    Zig has a compile option --single-threaded which has the following effects:

    -

    {#header_close#} {#header_open|Undefined Behavior#} @@ -8459,7 +8598,7 @@ pub fn main() void {

    At compile-time:

    {#code_begin|test_err|index 5 outside array of size 5#} comptime { - const array = "hello"; + const array: [5]u8 = "hello".*; const garbage = array[5]; } {#code_end#} @@ -8489,7 +8628,7 @@ const std = @import("std"); pub fn main() void { var value: i32 = -1; var unsigned = @intCast(u32, value); - std.debug.warn("value: {}\n", unsigned); + std.debug.warn("value: {}\n", .{unsigned}); } {#code_end#}

    @@ -8498,7 +8637,7 @@ pub fn main() void { {#header_close#} {#header_open|Cast Truncates Data#}

    At compile-time:

    - {#code_begin|test_err|integer value 300 cannot be implicitly casted to type 'u8'#} + {#code_begin|test_err|integer value 300 cannot be coerced to type 'u8'#} comptime { const spartan_count: u16 = 300; const byte = @intCast(u8, spartan_count); @@ -8511,7 +8650,7 @@ const std = @import("std"); pub fn main() void { var spartan_count: u16 = 300; const byte = @intCast(u8, spartan_count); - std.debug.warn("value: {}\n", byte); + std.debug.warn("value: {}\n", .{byte}); } {#code_end#}

    @@ -8545,7 +8684,7 @@ const std = @import("std"); pub fn main() void { var byte: u8 = 255; byte += 1; - std.debug.warn("value: {}\n", byte); + std.debug.warn("value: {}\n", .{byte}); } {#code_end#} {#header_close#} @@ -8568,11 +8707,11 @@ pub fn main() !void { var byte: u8 = 255; byte = if (math.add(u8, byte, 1)) |result| result else |err| { - warn("unable to add one: {}\n", @errorName(err)); + warn("unable to add one: {}\n", .{@errorName(err)}); return err; }; - warn("result: {}\n", byte); + warn("result: {}\n", .{byte}); } {#code_end#} {#header_close#} @@ -8597,9 +8736,9 @@ pub fn main() void { var result: u8 = undefined; if (@addWithOverflow(u8, byte, 10, &result)) { - warn("overflowed result: {}\n", result); + warn("overflowed result: {}\n", .{result}); } else { - warn("result: {}\n", result); + warn("result: {}\n", .{result}); } } {#code_end#} @@ -8634,7 +8773,7 @@ test "wraparound addition and subtraction" {

    At compile-time:

    {#code_begin|test_err|operation caused overflow#} comptime { - const x = @shlExact(u8(0b01010101), 2); + const x = @shlExact(@as(u8, 0b01010101), 2); } {#code_end#}

    At runtime:

    @@ -8644,7 +8783,7 @@ const std = @import("std"); pub fn main() void { var x: u8 = 0b01010101; var y = @shlExact(x, 2); - std.debug.warn("value: {}\n", y); + std.debug.warn("value: {}\n", .{y}); } {#code_end#} {#header_close#} @@ -8652,7 +8791,7 @@ pub fn main() void {

    At compile-time:

    {#code_begin|test_err|exact shift shifted out 1 bits#} comptime { - const x = @shrExact(u8(0b10101010), 2); + const x = @shrExact(@as(u8, 0b10101010), 2); } {#code_end#}

    At runtime:

    @@ -8662,7 +8801,7 @@ const std = @import("std"); pub fn main() void { var x: u8 = 0b10101010; var y = @shrExact(x, 2); - std.debug.warn("value: {}\n", y); + std.debug.warn("value: {}\n", .{y}); } {#code_end#} {#header_close#} @@ -8683,7 +8822,7 @@ pub fn main() void { var a: u32 = 1; var b: u32 = 0; var c = a / b; - std.debug.warn("value: {}\n", c); + std.debug.warn("value: {}\n", .{c}); } {#code_end#} {#header_close#} @@ -8704,7 +8843,7 @@ pub fn main() void { var a: u32 = 10; var b: u32 = 0; var c = a % b; - std.debug.warn("value: {}\n", c); + std.debug.warn("value: {}\n", .{c}); } {#code_end#} {#header_close#} @@ -8725,7 +8864,7 @@ pub fn main() void { var a: u32 = 10; var b: u32 = 3; var c = @divExact(a, b); - std.debug.warn("value: {}\n", c); + std.debug.warn("value: {}\n", .{c}); } {#code_end#} {#header_close#} @@ -8734,7 +8873,7 @@ pub fn main() void { {#code_begin|test_err|unable to convert#} comptime { var bytes = [5]u8{ 1, 2, 3, 4, 5 }; - var slice = @bytesToSlice(u32, bytes); + var slice = @bytesToSlice(u32, bytes[0..]); } {#code_end#}

    At runtime:

    @@ -8744,7 +8883,7 @@ const std = @import("std"); pub fn main() void { var bytes = [5]u8{ 1, 2, 3, 4, 5 }; var slice = @bytesToSlice(u32, bytes[0..]); - std.debug.warn("value: {}\n", slice[0]); + std.debug.warn("value: {}\n", .{slice[0]}); } {#code_end#} {#header_close#} @@ -8763,7 +8902,7 @@ const std = @import("std"); pub fn main() void { var optional_number: ?i32 = null; var number = optional_number.?; - std.debug.warn("value: {}\n", number); + std.debug.warn("value: {}\n", .{number}); } {#code_end#}

    One way to avoid this crash is to test for null instead of assuming non-null, with @@ -8774,9 +8913,9 @@ pub fn main() void { const optional_number: ?i32 = null; if (optional_number) |number| { - warn("got number: {}\n", number); + warn("got number: {}\n", .{number}); } else { - warn("it's null\n"); + warn("it's null\n", .{}); } } {#code_end#} @@ -8799,7 +8938,7 @@ const std = @import("std"); pub fn main() void { const number = getNumberOrFail() catch unreachable; - std.debug.warn("value: {}\n", number); + std.debug.warn("value: {}\n", .{number}); } fn getNumberOrFail() !i32 { @@ -8815,9 +8954,9 @@ pub fn main() void { const result = getNumberOrFail(); if (result) |number| { - warn("got number: {}\n", number); + warn("got number: {}\n", .{number}); } else |err| { - warn("got error: {}\n", @errorName(err)); + warn("got error: {}\n", .{@errorName(err)}); } } @@ -8844,7 +8983,7 @@ pub fn main() void { var err = error.AnError; var number = @errorToInt(err) + 500; var invalid_err = @intToError(number); - std.debug.warn("value: {}\n", number); + std.debug.warn("value: {}\n", .{number}); } {#code_end#} {#header_close#} @@ -8874,7 +9013,7 @@ const Foo = enum { pub fn main() void { var a: u2 = 3; var b = @intToEnum(Foo, a); - std.debug.warn("value: {}\n", @tagName(b)); + std.debug.warn("value: {}\n", .{@tagName(b)}); } {#code_end#} {#header_close#} @@ -8911,7 +9050,7 @@ pub fn main() void { } fn foo(set1: Set1) void { const x = @errSetCast(Set2, set1); - std.debug.warn("value: {}\n", x); + std.debug.warn("value: {}\n", .{x}); } {#code_end#} {#header_close#} @@ -8920,7 +9059,7 @@ fn foo(set1: Set1) void {

    At compile-time:

    {#code_begin|test_err|pointer address 0x1 is not aligned to 4 bytes#} comptime { - const ptr = @intToPtr(*i32, 0x1); + const ptr = @intToPtr(*align(1) i32, 0x1); const aligned = @alignCast(4, ptr); } {#code_end#} @@ -8967,7 +9106,7 @@ pub fn main() void { fn bar(f: *Foo) void { f.float = 12.34; - std.debug.warn("value: {}\n", f.float); + std.debug.warn("value: {}\n", .{f.float}); } {#code_end#}

    @@ -8991,7 +9130,7 @@ pub fn main() void { fn bar(f: *Foo) void { f.* = Foo{ .float = 12.34 }; - std.debug.warn("value: {}\n", f.float); + std.debug.warn("value: {}\n", .{f.float}); } {#code_end#}

    @@ -9010,7 +9149,7 @@ pub fn main() void { var f = Foo{ .int = 42 }; f = Foo{ .float = undefined }; bar(&f); - std.debug.warn("value: {}\n", f.float); + std.debug.warn("value: {}\n", .{f.float}); } fn bar(f: *Foo) void { @@ -9110,8 +9249,6 @@ fn concat(allocator: *Allocator, a: []const u8, b: []const u8) ![]u8 {

  • Are you linking libc? In this case, {#syntax#}std.heap.c_allocator{#endsyntax#} is likely the right choice, at least for your main allocator.
  • -
  • Are you building for WebAssembly? In this case, {#syntax#}std.heap.wasm_allocator{#endsyntax#} is likely - the right choice for your main allocator as it uses WebAssembly's memory instructions.
  • Is the maximum number of bytes that you will need bounded by a number known at {#link|comptime#}? In this case, use {#syntax#}std.heap.FixedBufferAllocator{#endsyntax#} or @@ -9127,13 +9264,13 @@ fn concat(allocator: *Allocator, a: []const u8, b: []const u8) ![]u8 { const std = @import("std"); pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = &arena.allocator; const ptr = try allocator.create(i32); - std.debug.warn("ptr={*}\n", ptr); + std.debug.warn("ptr={*}\n", .{ptr}); } {#code_end#} When using this kind of allocator, there is no need to free anything manually. Everything @@ -9198,7 +9335,7 @@ test "string literal to constant slice" { The location of memory allocated with {#syntax#}allocator.alloc{#endsyntax#} or {#syntax#}allocator.create{#endsyntax#} is determined by the allocator's implementation.

    -

    TODO: thread local variables

    +

    TODO: thread local variables

    {#header_close#} {#header_open|Implementing an Allocator#} @@ -9435,22 +9572,6 @@ test "assert in release fast mode" { {#see_also|Primitive Types#} {#header_close#} - {#header_open|C String Literals#} - {#code_begin|exe#} - {#link_libc#} -extern fn puts([*]const u8) void; - -pub fn main() void { - puts(c"this has a null terminator"); - puts( - c\\and so - c\\does this - c\\multiline C string literal - ); -} - {#code_end#} - {#see_also|String Literals and Character Literals#} - {#header_close#} {#header_open|Import from C Header File#}

    @@ -9465,7 +9586,7 @@ const c = @cImport({ @cInclude("stdio.h"); }); pub fn main() void { - _ = c.printf(c"hello\n"); + _ = c.printf("hello\n"); } {#code_end#}

    @@ -9504,8 +9625,8 @@ const c = @cImport({

    {#syntax#}[*c]T{#endsyntax#} - C pointer.

    -

    The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possbly the last line of the file).

    +

    The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possibly the last line of the file).

    For some discussion on the rationale behind these design decisions, see issue #663

    {#header_close#} @@ -10087,18 +10208,18 @@ ContainerMembers <- TestDecl ContainerMembers / TopLevelComptime ContainerMembers / KEYWORD_pub? TopLevelDecl ContainerMembers - / KEYWORD_pub? ContainerField COMMA ContainerMembers - / KEYWORD_pub? ContainerField + / ContainerField COMMA ContainerMembers + / ContainerField / -TestDecl <- KEYWORD_test STRINGLITERAL Block +TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block TopLevelComptime <- KEYWORD_comptime BlockExpr TopLevelDecl - <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block) - / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl - / KEYWORD_use Expr SEMICOLON + <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline)? FnProto (SEMICOLON / Block) + / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl + / KEYWORD_usingnamespace Expr SEMICOLON FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) @@ -10203,6 +10324,7 @@ PrimaryTypeExpr / CHAR_LITERAL / ContainerDecl / DOT IDENTIFIER + / DOT InitList / ErrorSetDecl / FLOAT / FnProto @@ -10268,9 +10390,7 @@ LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN # Fn specific FnCC - <- KEYWORD_nakedcc - / KEYWORD_stdcallcc - / KEYWORD_extern + <- KEYWORD_extern / KEYWORD_async ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType @@ -10428,7 +10548,6 @@ string_char line_comment <- '//'[^\n]* line_string <- ("\\\\" [^\n]* [ \n]*)+ -line_cstring <- ("c\\\\" [^\n]* [ \n]*)+ skip <- ([ \n] / line_comment)* CHAR_LITERAL <- "'" char_char "'" skip @@ -10442,12 +10561,12 @@ INTEGER / "0o" [0-7]+ skip / "0x" hex+ skip / [0-9]+ skip +STRINGLITERALSINGLE <- "\"" string_char* "\"" skip STRINGLITERAL - <- "c"? "\"" string_char* "\"" skip + <- STRINGLITERALSINGLE / line_string skip - / line_cstring skip IDENTIFIER - <- !keyword ("c" !["\\] / [A-Zabd-z_]) [A-Za-z0-9_]* skip + <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip / "@\"" string_char* "\"" skip BUILTINIDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip @@ -10534,7 +10653,6 @@ KEYWORD_fn <- 'fn' end_of_word KEYWORD_for <- 'for' end_of_word KEYWORD_if <- 'if' end_of_word KEYWORD_inline <- 'inline' end_of_word -KEYWORD_nakedcc <- 'nakedcc' end_of_word KEYWORD_noalias <- 'noalias' end_of_word KEYWORD_null <- 'null' end_of_word KEYWORD_or <- 'or' end_of_word @@ -10545,7 +10663,6 @@ KEYWORD_pub <- 'pub' end_of_word KEYWORD_resume <- 'resume' end_of_word KEYWORD_return <- 'return' end_of_word KEYWORD_linksection <- 'linksection' end_of_word -KEYWORD_stdcallcc <- 'stdcallcc' end_of_word KEYWORD_struct <- 'struct' end_of_word KEYWORD_suspend <- 'suspend' end_of_word KEYWORD_switch <- 'switch' end_of_word @@ -10567,10 +10684,10 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline - / KEYWORD_nakedcc / KEYWORD_noalias / KEYWORD_null / KEYWORD_or + / KEYWORD_noalias / KEYWORD_null / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection - / KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend + / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try / KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while @@ -10591,6 +10708,7 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm
  • Together we serve end users.
  • {#header_close#} + diff --git a/lib/libc/glibc/LICENSES b/lib/libc/glibc/LICENSES new file mode 100644 index 000000000..0e3a9fe39 --- /dev/null +++ b/lib/libc/glibc/LICENSES @@ -0,0 +1,391 @@ +This file contains the copying permission notices for various files in the +GNU C Library distribution that have copyright owners other than the Free +Software Foundation. These notices all require that a copy of the notice +be included in the accompanying documentation and be distributed with +binary distributions of the code, so be sure to include this file along +with any binary distributions derived from the GNU C Library. + + +All code incorporated from 4.4 BSD is distributed under the following +license: + +Copyright (C) 1991 Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. [This condition was removed.] +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The DNS resolver code, taken from BIND 4.9.5, is copyrighted by UC +Berkeley, by Digital Equipment Corporation and by Internet Software +Consortium. The DEC portions are under the following license: + +Portions Copyright (C) 1993 by Digital Equipment Corporation. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies, and +that the name of Digital Equipment Corporation not be used in +advertising or publicity pertaining to distribution of the document or +software without specific, written prior permission. + +THE SOFTWARE IS PROVIDED ``AS IS'' AND DIGITAL EQUIPMENT CORP. +DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +The ISC portions are under the following license: + +Portions Copyright (c) 1996-1999 by Internet Software Consortium. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +The Sun RPC support (from rpcsrc-4.0) is covered by the following +license: + +Copyright (c) 2010, Oracle America, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the "Oracle America, Inc." nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +The following CMU license covers some of the support code for Mach, +derived from Mach 3.0: + +Mach Operating System +Copyright (C) 1991,1990,1989 Carnegie Mellon University +All Rights Reserved. + +Permission to use, copy, modify and distribute this software and its +documentation is hereby granted, provided that both the copyright +notice and this permission notice appear in all copies of the +software, derivative works or modified versions, and any portions +thereof, and that both notices appear in supporting documentation. + +CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS ``AS IS'' +CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + +Carnegie Mellon requests users of this software to return to + + Software Distribution Coordinator + School of Computer Science + Carnegie Mellon University + Pittsburgh PA 15213-3890 + +or Software.Distribution@CS.CMU.EDU any improvements or +extensions that they make and grant Carnegie Mellon the rights to +redistribute these changes. + +The file if_ppp.h is under the following CMU license: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY AND + CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The following license covers the files from Intel's "Highly Optimized +Mathematical Functions for Itanium" collection: + +Intel License Agreement + +Copyright (c) 2000, Intel Corporation + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* The name of Intel Corporation may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The files inet/getnameinfo.c and sysdeps/posix/getaddrinfo.c are copyright +(C) by Craig Metz and are distributed under the following license: + +/* The Inner Net License, Version 2.00 + + The author(s) grant permission for redistribution and use in source and +binary forms, with or without modification, of the software and documentation +provided that the following conditions are met: + +0. If you receive a version of the software that is specifically labelled + as not being for redistribution (check the version message and/or README), + you are not permitted to redistribute that version of the software in any + way or form. +1. All terms of the all other applicable copyrights and licenses must be + followed. +2. Redistributions of source code must retain the authors' copyright + notice(s), this list of conditions, and the following disclaimer. +3. Redistributions in binary form must reproduce the authors' copyright + notice(s), this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. +4. [The copyright holder has authorized the removal of this clause.] +5. Neither the name(s) of the author(s) nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + If these license terms cause you a real problem, contact the author. */ + +The file sunrpc/des_impl.c is copyright Eric Young: + +Copyright (C) 1992 Eric Young +Collected from libdes and modified for SECURE RPC by Martin Kuck 1994 +This file is distributed under the terms of the GNU Lesser General +Public License, version 2.1 or later - see the file COPYING.LIB for details. +If you did not receive a copy of the license with this program, please +see to obtain a copy. + +The file inet/rcmd.c is under a UCB copyright and the following: + +Copyright (C) 1998 WIDE Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The file posix/runtests.c is copyright Tom Lord: + +Copyright 1995 by Tom Lord + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of the copyright holder not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +Tom Lord DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL TOM LORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +The posix/rxspencer tests are copyright Henry Spencer: + +Copyright 1992, 1993, 1994, 1997 Henry Spencer. All rights reserved. +This software is not subject to any license of the American Telephone +and Telegraph Company or of the Regents of the University of California. + +Permission is granted to anyone to use this software for any purpose on +any computer system, and to alter it and redistribute it, subject +to the following restrictions: + +1. The author is not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + +4. This notice may not be removed or altered. + +The file posix/PCRE.tests is copyright University of Cambridge: + +Copyright (c) 1997-2003 University of Cambridge + +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. In practice, this means that if you use + PCRE in software that you distribute to others, commercially or + otherwise, you must put a sentence like this + + Regular expression support is provided by the PCRE library package, + which is open source software, written by Philip Hazel, and copyright + by the University of Cambridge, England. + + somewhere reasonably visible in your documentation and in any relevant + files or online help data or similar. A reference to the ftp site for + the source, that is, to + + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ + + should also be given in the documentation. However, this condition is not + intended to apply to whole chains of software. If package A includes PCRE, + it must acknowledge it, but if package B is software that includes package + A, the condition is not imposed on package B (unless it uses PCRE + independently). + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL), + then the terms of that licence shall supersede any condition above with + which it is incompatible. + +Files from Sun fdlibm are copyright Sun Microsystems, Inc.: + +Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunPro, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. + +Various long double libm functions are copyright Stephen L. Moshier: + +Copyright 2001 by Stephen L. Moshier + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see + . */ diff --git a/lib/libc/include/aarch64-linux-musl/bits/hwcap.h b/lib/libc/include/aarch64-linux-musl/bits/hwcap.h index 3f1b9de3b..83abee266 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/hwcap.h +++ b/lib/libc/include/aarch64-linux-musl/bits/hwcap.h @@ -29,4 +29,12 @@ #define HWCAP_SSBS (1 << 28) #define HWCAP_SB (1 << 29) #define HWCAP_PACA (1 << 30) -#define HWCAP_PACG (1UL << 31) \ No newline at end of file +#define HWCAP_PACG (1UL << 31) + +#define HWCAP2_DCPODP (1 << 0) +#define HWCAP2_SVE2 (1 << 1) +#define HWCAP2_SVEAES (1 << 2) +#define HWCAP2_SVEPMULL (1 << 3) +#define HWCAP2_SVEBITPERM (1 << 4) +#define HWCAP2_SVESHA3 (1 << 5) +#define HWCAP2_SVESM4 (1 << 6) \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/ipc.h b/lib/libc/include/aarch64-linux-musl/bits/ipc.h deleted file mode 100644 index 37561db60..000000000 --- a/lib/libc/include/aarch64-linux-musl/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __ipc_perm_seq; - - unsigned long __pad1; - unsigned long __pad2; -}; - -#define IPC_64 0 \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/posix.h b/lib/libc/include/aarch64-linux-musl/bits/posix.h deleted file mode 100644 index 9fcc205cc..000000000 --- a/lib/libc/include/aarch64-linux-musl/bits/posix.h +++ /dev/null @@ -1,2 +0,0 @@ -#define _POSIX_V6_LP64_OFF64 1 -#define _POSIX_V7_LP64_OFF64 1 \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/reg.h b/lib/libc/include/aarch64-linux-musl/bits/reg.h deleted file mode 100644 index 0afb6bd06..000000000 --- a/lib/libc/include/aarch64-linux-musl/bits/reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#undef __WORDSIZE -#define __WORDSIZE 64 \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/syscall.h b/lib/libc/include/aarch64-linux-musl/bits/syscall.h index f1df93f0b..53f296e03 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/syscall.h +++ b/lib/libc/include/aarch64-linux-musl/bits/syscall.h @@ -281,6 +281,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_io_setup 0 #define SYS_io_destroy 1 @@ -564,4 +570,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/any-windows-any/intrin.h b/lib/libc/include/any-windows-any/intrin.h index 0b2343fb8..8fe46c20e 100644 --- a/lib/libc/include/any-windows-any/intrin.h +++ b/lib/libc/include/any-windows-any/intrin.h @@ -50,7 +50,7 @@ * On GCC 4.9 we may always include those headers. On older GCCs, we may do it only if CPU * features used by them are enabled, so we need to check macros like __SSE__ or __MMX__ first. */ -#if __MINGW_GNUC_PREREQ(4, 9) +#if __MINGW_GNUC_PREREQ(4, 9) || defined(__clang__) #define __MINGW_FORCE_SYS_INTRINS #endif diff --git a/lib/libc/include/powerpc64-linux-musl/bits/limits.h b/lib/libc/include/arm-linux-musl/bits/limits.h similarity index 59% rename from lib/libc/include/powerpc64-linux-musl/bits/limits.h rename to lib/libc/include/arm-linux-musl/bits/limits.h index 1be0deba7..ba9c3aa00 100644 --- a/lib/libc/include/powerpc64-linux-musl/bits/limits.h +++ b/lib/libc/include/arm-linux-musl/bits/limits.h @@ -1,7 +1,7 @@ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define LONG_BIT 64 +#define LONG_BIT 32 #endif -#define LONG_MAX 0x7fffffffffffffffL +#define LONG_MAX 0x7fffffffL #define LLONG_MAX 0x7fffffffffffffffLL \ No newline at end of file diff --git a/lib/libc/include/arm-linux-musl/bits/posix.h b/lib/libc/include/arm-linux-musl/bits/posix.h new file mode 100644 index 000000000..b0aba3e4a --- /dev/null +++ b/lib/libc/include/arm-linux-musl/bits/posix.h @@ -0,0 +1,2 @@ +#define _POSIX_V6_ILP32_OFFBIG 1 +#define _POSIX_V7_ILP32_OFFBIG 1 \ No newline at end of file diff --git a/lib/libc/include/arm-linux-musl/bits/reg.h b/lib/libc/include/arm-linux-musl/bits/reg.h new file mode 100644 index 000000000..e30a80ee5 --- /dev/null +++ b/lib/libc/include/arm-linux-musl/bits/reg.h @@ -0,0 +1,3 @@ +#undef __WORDSIZE +#define __WORDSIZE 32 +/* FIXME */ \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/sem.h b/lib/libc/include/arm-linux-musl/bits/sem.h similarity index 51% rename from lib/libc/include/aarch64-linux-musl/bits/sem.h rename to lib/libc/include/arm-linux-musl/bits/sem.h index bf8e59f52..9097f9ffd 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/sem.h +++ b/lib/libc/include/arm-linux-musl/bits/sem.h @@ -1,14 +1,16 @@ struct semid_ds { struct ipc_perm sem_perm; time_t sem_otime; + long __unused1; time_t sem_ctime; + long __unused2; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; #else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; unsigned short sem_nsems; #endif - time_t __unused3; - time_t __unused4; + long __unused3; + long __unused4; }; \ No newline at end of file diff --git a/lib/libc/include/arm-linux-musl/bits/stat.h b/lib/libc/include/arm-linux-musl/bits/stat.h new file mode 100644 index 000000000..78e91b402 --- /dev/null +++ b/lib/libc/include/arm-linux-musl/bits/stat.h @@ -0,0 +1,21 @@ +/* copied from kernel definition, but with padding replaced + * by the corresponding correctly-sized userspace types. */ + +struct stat { + dev_t st_dev; + int __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + int __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + ino_t st_ino; +}; \ No newline at end of file diff --git a/lib/libc/include/arm-linux-musl/bits/syscall.h b/lib/libc/include/arm-linux-musl/bits/syscall.h index 4bd7e623f..778430b87 100644 --- a/lib/libc/include/arm-linux-musl/bits/syscall.h +++ b/lib/libc/include/arm-linux-musl/bits/syscall.h @@ -381,6 +381,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define __ARM_NR_breakpoint 0x0f0001 #define __ARM_NR_cacheflush 0x0f0002 @@ -771,4 +777,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/ipc.h b/lib/libc/include/generic-musl/bits/ipc.h index f7952b4bc..2c0d4402c 100644 --- a/lib/libc/include/generic-musl/bits/ipc.h +++ b/lib/libc/include/generic-musl/bits/ipc.h @@ -8,6 +8,4 @@ struct ipc_perm { int __ipc_perm_seq; long __pad1; long __pad2; -}; - -#define IPC_64 0x100 \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/ipcstat.h b/lib/libc/include/generic-musl/bits/ipcstat.h new file mode 100644 index 000000000..36917ea92 --- /dev/null +++ b/lib/libc/include/generic-musl/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/limits.h b/lib/libc/include/generic-musl/bits/limits.h index ba9c3aa00..1be0deba7 100644 --- a/lib/libc/include/generic-musl/bits/limits.h +++ b/lib/libc/include/generic-musl/bits/limits.h @@ -1,7 +1,7 @@ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define LONG_BIT 32 +#define LONG_BIT 64 #endif -#define LONG_MAX 0x7fffffffL +#define LONG_MAX 0x7fffffffffffffffL #define LLONG_MAX 0x7fffffffffffffffLL \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/msg.h b/lib/libc/include/generic-musl/bits/msg.h index 2cb18356d..39bdf2a3d 100644 --- a/lib/libc/include/generic-musl/bits/msg.h +++ b/lib/libc/include/generic-musl/bits/msg.h @@ -8,6 +8,5 @@ struct msqid_ds { msglen_t msg_qbytes; pid_t msg_lspid; pid_t msg_lrpid; - unsigned long __pad1; - unsigned long __pad2; + unsigned long __unused[2]; }; \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/posix.h b/lib/libc/include/generic-musl/bits/posix.h index b0aba3e4a..9fcc205cc 100644 --- a/lib/libc/include/generic-musl/bits/posix.h +++ b/lib/libc/include/generic-musl/bits/posix.h @@ -1,2 +1,2 @@ -#define _POSIX_V6_ILP32_OFFBIG 1 -#define _POSIX_V7_ILP32_OFFBIG 1 \ No newline at end of file +#define _POSIX_V6_LP64_OFF64 1 +#define _POSIX_V7_LP64_OFF64 1 \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/reg.h b/lib/libc/include/generic-musl/bits/reg.h index e30a80ee5..0afb6bd06 100644 --- a/lib/libc/include/generic-musl/bits/reg.h +++ b/lib/libc/include/generic-musl/bits/reg.h @@ -1,3 +1,2 @@ #undef __WORDSIZE -#define __WORDSIZE 32 -/* FIXME */ \ No newline at end of file +#define __WORDSIZE 64 \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/sem.h b/lib/libc/include/generic-musl/bits/sem.h index a115313f3..25ae3b4a1 100644 --- a/lib/libc/include/generic-musl/bits/sem.h +++ b/lib/libc/include/generic-musl/bits/sem.h @@ -1,16 +1,14 @@ struct semid_ds { struct ipc_perm sem_perm; time_t sem_otime; - time_t __unused1; time_t sem_ctime; - time_t __unused2; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; #else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; unsigned short sem_nsems; #endif - time_t __unused3; - time_t __unused4; + long __unused3; + long __unused4; }; \ No newline at end of file diff --git a/lib/libc/include/generic-musl/bits/stat.h b/lib/libc/include/generic-musl/bits/stat.h index 78e91b402..6ce9af119 100644 --- a/lib/libc/include/generic-musl/bits/stat.h +++ b/lib/libc/include/generic-musl/bits/stat.h @@ -1,21 +1,18 @@ -/* copied from kernel definition, but with padding replaced - * by the corresponding correctly-sized userspace types. */ - struct stat { dev_t st_dev; - int __st_dev_padding; - long __st_ino_truncated; + ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; - int __st_rdev_padding; + unsigned long __pad; off_t st_size; blksize_t st_blksize; + int __pad2; blkcnt_t st_blocks; struct timespec st_atim; struct timespec st_mtim; struct timespec st_ctim; - ino_t st_ino; + unsigned __unused[2]; }; \ No newline at end of file diff --git a/lib/libc/include/generic-musl/fcntl.h b/lib/libc/include/generic-musl/fcntl.h index 2723d0cf0..8c418170d 100644 --- a/lib/libc/include/generic-musl/fcntl.h +++ b/lib/libc/include/generic-musl/fcntl.h @@ -100,6 +100,11 @@ int posix_fallocate(int, off_t, off_t); #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) #define AT_NO_AUTOMOUNT 0x800 #define AT_EMPTY_PATH 0x1000 +#define AT_STATX_SYNC_TYPE 0x6000 +#define AT_STATX_SYNC_AS_STAT 0x0000 +#define AT_STATX_FORCE_SYNC 0x2000 +#define AT_STATX_DONT_SYNC 0x4000 +#define AT_RECURSIVE 0x8000 #define FAPPEND O_APPEND #define FFSYNC O_SYNC diff --git a/lib/libc/include/generic-musl/glob.h b/lib/libc/include/generic-musl/glob.h index 1828889e9..1c596c9fe 100644 --- a/lib/libc/include/generic-musl/glob.h +++ b/lib/libc/include/generic-musl/glob.h @@ -31,6 +31,9 @@ void globfree(glob_t *); #define GLOB_NOESCAPE 0x40 #define GLOB_PERIOD 0x80 +#define GLOB_TILDE 0x1000 +#define GLOB_TILDE_CHECK 0x4000 + #define GLOB_NOSPACE 1 #define GLOB_ABORTED 2 #define GLOB_NOMATCH 3 diff --git a/lib/libc/include/generic-musl/netinet/if_ether.h b/lib/libc/include/generic-musl/netinet/if_ether.h index 567cbeb7c..37d81698e 100644 --- a/lib/libc/include/generic-musl/netinet/if_ether.h +++ b/lib/libc/include/generic-musl/netinet/if_ether.h @@ -76,6 +76,7 @@ #define ETH_P_QINQ2 0x9200 #define ETH_P_QINQ3 0x9300 #define ETH_P_EDSA 0xDADA +#define ETH_P_DSA_8021Q 0xDADB #define ETH_P_IFE 0xED3E #define ETH_P_AF_IUCV 0xFBFB diff --git a/lib/libc/include/generic-musl/sched.h b/lib/libc/include/generic-musl/sched.h index c0f322dd0..7963785f6 100644 --- a/lib/libc/include/generic-musl/sched.h +++ b/lib/libc/include/generic-musl/sched.h @@ -18,10 +18,12 @@ extern "C" { struct sched_param { int sched_priority; - int sched_ss_low_priority; - struct timespec sched_ss_repl_period; - struct timespec sched_ss_init_budget; - int sched_ss_max_repl; + int __reserved1; + struct { + time_t __reserved1; + long __reserved2; + } __reserved2[2]; + int __reserved3; }; int sched_get_priority_max(int); @@ -47,6 +49,7 @@ int sched_yield(void); #define CLONE_FS 0x00000200 #define CLONE_FILES 0x00000400 #define CLONE_SIGHAND 0x00000800 +#define CLONE_PIDFD 0x00001000 #define CLONE_PTRACE 0x00002000 #define CLONE_VFORK 0x00004000 #define CLONE_PARENT 0x00008000 diff --git a/lib/libc/include/generic-musl/spawn.h b/lib/libc/include/generic-musl/spawn.h index eb14a4c7c..399945bda 100644 --- a/lib/libc/include/generic-musl/spawn.h +++ b/lib/libc/include/generic-musl/spawn.h @@ -71,6 +71,11 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict, int int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, int); int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int); +#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) +int posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *__restrict, const char *__restrict); +int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *, int); +#endif + #ifdef __cplusplus } #endif diff --git a/lib/libc/include/generic-musl/stdlib.h b/lib/libc/include/generic-musl/stdlib.h index 123a65d02..f7c4f9d8d 100644 --- a/lib/libc/include/generic-musl/stdlib.h +++ b/lib/libc/include/generic-musl/stdlib.h @@ -152,6 +152,7 @@ int ptsname_r(int, char *, size_t); char *ecvt(double, int, int *, int *); char *fcvt(double, int, int *, int *); char *gcvt(double, int, char *); +char *secure_getenv(const char *); struct __locale_struct; float strtof_l(const char *__restrict, char **__restrict, struct __locale_struct *); double strtod_l(const char *__restrict, char **__restrict, struct __locale_struct *); diff --git a/lib/libc/include/generic-musl/sys/ipc.h b/lib/libc/include/generic-musl/sys/ipc.h index e4dc9c257..99a4f0154 100644 --- a/lib/libc/include/generic-musl/sys/ipc.h +++ b/lib/libc/include/generic-musl/sys/ipc.h @@ -22,6 +22,7 @@ extern "C" { #endif #include +#include #define IPC_CREAT 01000 #define IPC_EXCL 02000 @@ -29,7 +30,6 @@ extern "C" { #define IPC_RMID 0 #define IPC_SET 1 -#define IPC_STAT 2 #define IPC_INFO 3 #define IPC_PRIVATE ((key_t) 0) diff --git a/lib/libc/include/generic-musl/sys/msg.h b/lib/libc/include/generic-musl/sys/msg.h index c7f1967b3..7d3ec96af 100644 --- a/lib/libc/include/generic-musl/sys/msg.h +++ b/lib/libc/include/generic-musl/sys/msg.h @@ -25,9 +25,9 @@ typedef unsigned long msglen_t; #define MSG_NOERROR 010000 #define MSG_EXCEPT 020000 -#define MSG_STAT 11 +#define MSG_STAT (11 | (IPC_STAT & 0x100)) #define MSG_INFO 12 -#define MSG_STAT_ANY 13 +#define MSG_STAT_ANY (13 | (IPC_STAT & 0x100)) struct msginfo { int msgpool, msgmap, msgmax, msgmnb, msgmni, msgssz, msgtql; diff --git a/lib/libc/include/generic-musl/sys/sem.h b/lib/libc/include/generic-musl/sys/sem.h index 621e5dfbe..95e5d9433 100644 --- a/lib/libc/include/generic-musl/sys/sem.h +++ b/lib/libc/include/generic-musl/sys/sem.h @@ -31,9 +31,9 @@ extern "C" { #define _SEM_SEMUN_UNDEFINED 1 -#define SEM_STAT 18 +#define SEM_STAT (18 | (IPC_STAT & 0x100)) #define SEM_INFO 19 -#define SEM_STAT_ANY 20 +#define SEM_STAT_ANY (20 | (IPC_STAT & 0x100)) struct seminfo { int semmap; diff --git a/lib/libc/include/generic-musl/sys/shm.h b/lib/libc/include/generic-musl/sys/shm.h index 70ccd644c..e0bcf2557 100644 --- a/lib/libc/include/generic-musl/sys/shm.h +++ b/lib/libc/include/generic-musl/sys/shm.h @@ -33,9 +33,9 @@ extern "C" { #define SHM_LOCK 11 #define SHM_UNLOCK 12 -#define SHM_STAT 13 +#define SHM_STAT (13 | (IPC_STAT & 0x100)) #define SHM_INFO 14 -#define SHM_STAT_ANY 15 +#define SHM_STAT_ANY (15 | (IPC_STAT & 0x100)) #define SHM_DEST 01000 #define SHM_LOCKED 02000 #define SHM_HUGETLB 04000 diff --git a/lib/libc/include/generic-musl/unistd.h b/lib/libc/include/generic-musl/unistd.h index f40a0032d..c6b3f1a2a 100644 --- a/lib/libc/include/generic-musl/unistd.h +++ b/lib/libc/include/generic-musl/unistd.h @@ -176,6 +176,7 @@ long syscall(long, ...); int execvpe(const char *, char *const [], char *const []); int issetugid(void); int getentropy(void *, size_t); +extern int optreset; #endif #ifdef _GNU_SOURCE @@ -188,6 +189,7 @@ char *get_current_dir_name(void); int syncfs(int); int euidaccess(const char *, int); int eaccess(const char *, int); +ssize_t copy_file_range(int, off_t *, int, off_t *, size_t, unsigned); #endif #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) diff --git a/lib/libc/include/i386-linux-musl/bits/posix.h b/lib/libc/include/i386-linux-musl/bits/posix.h new file mode 100644 index 000000000..b0aba3e4a --- /dev/null +++ b/lib/libc/include/i386-linux-musl/bits/posix.h @@ -0,0 +1,2 @@ +#define _POSIX_V6_ILP32_OFFBIG 1 +#define _POSIX_V7_ILP32_OFFBIG 1 \ No newline at end of file diff --git a/lib/libc/include/i386-linux-musl/bits/sem.h b/lib/libc/include/i386-linux-musl/bits/sem.h new file mode 100644 index 000000000..e63d0732b --- /dev/null +++ b/lib/libc/include/i386-linux-musl/bits/sem.h @@ -0,0 +1,11 @@ +struct semid_ds { + struct ipc_perm sem_perm; + time_t sem_otime; + long __unused1; + time_t sem_ctime; + long __unused2; + unsigned short sem_nsems; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; + long __unused3; + long __unused4; +}; \ No newline at end of file diff --git a/lib/libc/include/i386-linux-musl/bits/stat.h b/lib/libc/include/i386-linux-musl/bits/stat.h new file mode 100644 index 000000000..78e91b402 --- /dev/null +++ b/lib/libc/include/i386-linux-musl/bits/stat.h @@ -0,0 +1,21 @@ +/* copied from kernel definition, but with padding replaced + * by the corresponding correctly-sized userspace types. */ + +struct stat { + dev_t st_dev; + int __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + int __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + ino_t st_ino; +}; \ No newline at end of file diff --git a/lib/libc/include/i386-linux-musl/bits/syscall.h b/lib/libc/include/i386-linux-musl/bits/syscall.h index a6109c1a6..482f1d1d1 100644 --- a/lib/libc/include/i386-linux-musl/bits/syscall.h +++ b/lib/libc/include/i386-linux-musl/bits/syscall.h @@ -418,6 +418,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -836,4 +842,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/limits.h b/lib/libc/include/mips-linux-musl/bits/limits.h similarity index 59% rename from lib/libc/include/aarch64-linux-musl/bits/limits.h rename to lib/libc/include/mips-linux-musl/bits/limits.h index 1be0deba7..ba9c3aa00 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/limits.h +++ b/lib/libc/include/mips-linux-musl/bits/limits.h @@ -1,7 +1,7 @@ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define LONG_BIT 64 +#define LONG_BIT 32 #endif -#define LONG_MAX 0x7fffffffffffffffL +#define LONG_MAX 0x7fffffffL #define LLONG_MAX 0x7fffffffffffffffLL \ No newline at end of file diff --git a/lib/libc/include/mips-linux-musl/bits/posix.h b/lib/libc/include/mips-linux-musl/bits/posix.h new file mode 100644 index 000000000..b0aba3e4a --- /dev/null +++ b/lib/libc/include/mips-linux-musl/bits/posix.h @@ -0,0 +1,2 @@ +#define _POSIX_V6_ILP32_OFFBIG 1 +#define _POSIX_V7_ILP32_OFFBIG 1 \ No newline at end of file diff --git a/lib/libc/include/mips-linux-musl/bits/sem.h b/lib/libc/include/mips-linux-musl/bits/sem.h deleted file mode 100644 index bf8e59f52..000000000 --- a/lib/libc/include/mips-linux-musl/bits/sem.h +++ /dev/null @@ -1,14 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; -#if __BYTE_ORDER == __LITTLE_ENDIAN - unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; -#else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; - unsigned short sem_nsems; -#endif - time_t __unused3; - time_t __unused4; -}; \ No newline at end of file diff --git a/lib/libc/include/mips-linux-musl/bits/syscall.h b/lib/libc/include/mips-linux-musl/bits/syscall.h index 7093a0cd7..67bd95fa5 100644 --- a/lib/libc/include/mips-linux-musl/bits/syscall.h +++ b/lib/libc/include/mips-linux-musl/bits/syscall.h @@ -400,6 +400,12 @@ #define __NR_io_uring_setup 4425 #define __NR_io_uring_enter 4426 #define __NR_io_uring_register 4427 +#define __NR_open_tree 4428 +#define __NR_move_mount 4429 +#define __NR_fsopen 4430 +#define __NR_fsconfig 4431 +#define __NR_fsmount 4432 +#define __NR_fspick 4433 #define SYS_syscall 4000 #define SYS_exit 4001 @@ -802,4 +808,10 @@ #define SYS_pidfd_send_signal 4424 #define SYS_io_uring_setup 4425 #define SYS_io_uring_enter 4426 -#define SYS_io_uring_register 4427 \ No newline at end of file +#define SYS_io_uring_register 4427 +#define SYS_open_tree 4428 +#define SYS_move_mount 4429 +#define SYS_fsopen 4430 +#define SYS_fsconfig 4431 +#define SYS_fsmount 4432 +#define SYS_fspick 4433 \ No newline at end of file diff --git a/lib/libc/include/mips64-linux-musl/bits/ipc.h b/lib/libc/include/mips64-linux-musl/bits/ipc.h index 83ebc69b7..bfcc76a1b 100644 --- a/lib/libc/include/mips64-linux-musl/bits/ipc.h +++ b/lib/libc/include/mips64-linux-musl/bits/ipc.h @@ -9,6 +9,4 @@ struct ipc_perm { int __pad1; unsigned long __unused1; unsigned long __unused2; -}; - -#define IPC_64 0x100 \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/libc/include/mips64-linux-musl/bits/sem.h b/lib/libc/include/mips64-linux-musl/bits/sem.h deleted file mode 100644 index bf8e59f52..000000000 --- a/lib/libc/include/mips64-linux-musl/bits/sem.h +++ /dev/null @@ -1,14 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; -#if __BYTE_ORDER == __LITTLE_ENDIAN - unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; -#else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; - unsigned short sem_nsems; -#endif - time_t __unused3; - time_t __unused4; -}; \ No newline at end of file diff --git a/lib/libc/include/mips64-linux-musl/bits/stat.h b/lib/libc/include/mips64-linux-musl/bits/stat.h index 57b02c688..d464618b9 100644 --- a/lib/libc/include/mips64-linux-musl/bits/stat.h +++ b/lib/libc/include/mips64-linux-musl/bits/stat.h @@ -1,6 +1,3 @@ -#include -#include - struct stat { dev_t st_dev; int __pad1[3]; diff --git a/lib/libc/include/mips64-linux-musl/bits/syscall.h b/lib/libc/include/mips64-linux-musl/bits/syscall.h index 1c60f07f2..e14881035 100644 --- a/lib/libc/include/mips64-linux-musl/bits/syscall.h +++ b/lib/libc/include/mips64-linux-musl/bits/syscall.h @@ -330,6 +330,12 @@ #define __NR_io_uring_setup 5425 #define __NR_io_uring_enter 5426 #define __NR_io_uring_register 5427 +#define __NR_open_tree 5428 +#define __NR_move_mount 5429 +#define __NR_fsopen 5430 +#define __NR_fsconfig 5431 +#define __NR_fsmount 5432 +#define __NR_fspick 5433 #define SYS_read 5000 #define SYS_write 5001 @@ -662,4 +668,10 @@ #define SYS_pidfd_send_signal 5424 #define SYS_io_uring_setup 5425 #define SYS_io_uring_enter 5426 -#define SYS_io_uring_register 5427 \ No newline at end of file +#define SYS_io_uring_register 5427 +#define SYS_open_tree 5428 +#define SYS_move_mount 5429 +#define SYS_fsopen 5430 +#define SYS_fsconfig 5431 +#define SYS_fsmount 5432 +#define SYS_fspick 5433 \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/alltypes.h b/lib/libc/include/powerpc-linux-musl/bits/alltypes.h index 94e2c7a8b..f94f3e2f4 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/alltypes.h +++ b/lib/libc/include/powerpc-linux-musl/bits/alltypes.h @@ -14,11 +14,19 @@ typedef __builtin_va_list __isoc_va_list; #ifndef __cplusplus +#ifdef __WCHAR_TYPE__ +#if defined(__NEED_wchar_t) && !defined(__DEFINED_wchar_t) +typedef __WCHAR_TYPE__ wchar_t; +#define __DEFINED_wchar_t +#endif + +#else #if defined(__NEED_wchar_t) && !defined(__DEFINED_wchar_t) typedef long wchar_t; #define __DEFINED_wchar_t #endif +#endif #endif #if defined(__NEED_float_t) && !defined(__DEFINED_float_t) diff --git a/lib/libc/include/powerpc-linux-musl/bits/ipc.h b/lib/libc/include/powerpc-linux-musl/bits/ipc.h index b83c02675..4c4aedd26 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/ipc.h +++ b/lib/libc/include/powerpc-linux-musl/bits/ipc.h @@ -9,6 +9,4 @@ struct ipc_perm { int __pad1; long long __pad2; long long __pad3; -}; - -#define IPC_64 0x100 \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/limits.h b/lib/libc/include/powerpc-linux-musl/bits/limits.h similarity index 59% rename from lib/libc/include/riscv64-linux-musl/bits/limits.h rename to lib/libc/include/powerpc-linux-musl/bits/limits.h index 1be0deba7..ba9c3aa00 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/limits.h +++ b/lib/libc/include/powerpc-linux-musl/bits/limits.h @@ -1,7 +1,7 @@ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) -#define LONG_BIT 64 +#define LONG_BIT 32 #endif -#define LONG_MAX 0x7fffffffffffffffL +#define LONG_MAX 0x7fffffffL #define LLONG_MAX 0x7fffffffffffffffLL \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/posix.h b/lib/libc/include/powerpc-linux-musl/bits/posix.h new file mode 100644 index 000000000..b0aba3e4a --- /dev/null +++ b/lib/libc/include/powerpc-linux-musl/bits/posix.h @@ -0,0 +1,2 @@ +#define _POSIX_V6_ILP32_OFFBIG 1 +#define _POSIX_V7_ILP32_OFFBIG 1 \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/reg.h b/lib/libc/include/powerpc-linux-musl/bits/reg.h new file mode 100644 index 000000000..e30a80ee5 --- /dev/null +++ b/lib/libc/include/powerpc-linux-musl/bits/reg.h @@ -0,0 +1,3 @@ +#undef __WORDSIZE +#define __WORDSIZE 32 +/* FIXME */ \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/syscall.h b/lib/libc/include/powerpc-linux-musl/bits/syscall.h index 93beb3d1a..888868ad5 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/syscall.h +++ b/lib/libc/include/powerpc-linux-musl/bits/syscall.h @@ -407,6 +407,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -816,4 +822,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/user.h b/lib/libc/include/powerpc-linux-musl/bits/user.h index 16282f821..9aec1b4c2 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/user.h +++ b/lib/libc/include/powerpc-linux-musl/bits/user.h @@ -1,10 +1,8 @@ -struct pt_regs { - unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, mq; - unsigned long trap, dar, dsisr, result; -}; - struct user { - struct pt_regs regs; + struct { + unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, mq; + unsigned long trap, dar, dsisr, result; + } regs; unsigned long u_tsize, u_dsize, u_ssize; unsigned long start_code, start_data, start_stack; long signal; diff --git a/lib/libc/include/powerpc64-linux-musl/bits/ipc.h b/lib/libc/include/powerpc64-linux-musl/bits/ipc.h index b83c02675..4c4aedd26 100644 --- a/lib/libc/include/powerpc64-linux-musl/bits/ipc.h +++ b/lib/libc/include/powerpc64-linux-musl/bits/ipc.h @@ -9,6 +9,4 @@ struct ipc_perm { int __pad1; long long __pad2; long long __pad3; -}; - -#define IPC_64 0x100 \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/msg.h b/lib/libc/include/powerpc64-linux-musl/bits/msg.h deleted file mode 100644 index 39bdf2a3d..000000000 --- a/lib/libc/include/powerpc64-linux-musl/bits/msg.h +++ /dev/null @@ -1,12 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __unused[2]; -}; \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/posix.h b/lib/libc/include/powerpc64-linux-musl/bits/posix.h deleted file mode 100644 index 9fcc205cc..000000000 --- a/lib/libc/include/powerpc64-linux-musl/bits/posix.h +++ /dev/null @@ -1,2 +0,0 @@ -#define _POSIX_V6_LP64_OFF64 1 -#define _POSIX_V7_LP64_OFF64 1 \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/sem.h b/lib/libc/include/powerpc64-linux-musl/bits/sem.h deleted file mode 100644 index fde97e13c..000000000 --- a/lib/libc/include/powerpc64-linux-musl/bits/sem.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned short __pad[3], sem_nsems; -#else - unsigned short sem_nsems, __pad[3]; -#endif - unsigned long __unused[2]; -}; \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/syscall.h b/lib/libc/include/powerpc64-linux-musl/bits/syscall.h index f48379d8d..38b826314 100644 --- a/lib/libc/include/powerpc64-linux-musl/bits/syscall.h +++ b/lib/libc/include/powerpc64-linux-musl/bits/syscall.h @@ -379,6 +379,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -760,4 +766,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/user.h b/lib/libc/include/powerpc64-linux-musl/bits/user.h index cedbed6df..25d86ab55 100644 --- a/lib/libc/include/powerpc64-linux-musl/bits/user.h +++ b/lib/libc/include/powerpc64-linux-musl/bits/user.h @@ -1,10 +1,8 @@ -struct pt_regs { - unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, softe; - unsigned long trap, dar, dsisr, result; -}; - struct user { - struct pt_regs regs; + struct { + unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, softe; + unsigned long trap, dar, dsisr, result; + } regs; unsigned long u_tsize, u_dsize, u_ssize; unsigned long start_code, start_data, start_stack; long signal; diff --git a/lib/libc/include/riscv64-linux-musl/bits/ipc.h b/lib/libc/include/riscv64-linux-musl/bits/ipc.h deleted file mode 100644 index 37561db60..000000000 --- a/lib/libc/include/riscv64-linux-musl/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __ipc_perm_seq; - - unsigned long __pad1; - unsigned long __pad2; -}; - -#define IPC_64 0 \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/sem.h b/lib/libc/include/riscv64-linux-musl/bits/sem.h deleted file mode 100644 index d1734344c..000000000 --- a/lib/libc/include/riscv64-linux-musl/bits/sem.h +++ /dev/null @@ -1,9 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; - unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; - time_t __unused3; - time_t __unused4; -}; \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/shm.h b/lib/libc/include/riscv64-linux-musl/bits/shm.h deleted file mode 100644 index 10041f9bf..000000000 --- a/lib/libc/include/riscv64-linux-musl/bits/shm.h +++ /dev/null @@ -1,25 +0,0 @@ -#define SHMLBA 4096 - -struct shmid_ds -{ - struct ipc_perm shm_perm; - size_t shm_segsz; - time_t shm_atime; - time_t shm_dtime; - time_t shm_ctime; - pid_t shm_cpid; - pid_t shm_lpid; - unsigned long shm_nattch; - unsigned long __pad1; - unsigned long __pad2; -}; - -struct shminfo { - unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; -}; - -struct shm_info { - int __used_ids; - unsigned long shm_tot, shm_rss, shm_swp; - unsigned long __swap_attempts, __swap_successes; -}; \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/signal.h b/lib/libc/include/riscv64-linux-musl/bits/signal.h index 2e3110d7d..5eddfd18b 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/signal.h +++ b/lib/libc/include/riscv64-linux-musl/bits/signal.h @@ -6,46 +6,43 @@ # define SIGSTKSZ 8192 #endif -/* gregs[0] holds the program counter. */ +typedef unsigned long __riscv_mc_gp_state[32]; + +struct __riscv_mc_f_ext_state { + unsigned int __f[32]; + unsigned int __fcsr; +}; + +struct __riscv_mc_d_ext_state { + unsigned long long __f[32]; + unsigned int __fcsr; +}; + +struct __riscv_mc_q_ext_state { + unsigned long long __f[64] __attribute__((aligned(16))); + unsigned int __fcsr; + unsigned int __reserved[3]; +}; + +union __riscv_mc_fp_state { + struct __riscv_mc_f_ext_state __f; + struct __riscv_mc_d_ext_state __d; + struct __riscv_mc_q_ext_state __q; +}; + +typedef struct mcontext_t { + __riscv_mc_gp_state __gregs; + union __riscv_mc_fp_state __fpregs; +} mcontext_t; #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) typedef unsigned long greg_t; typedef unsigned long gregset_t[32]; - -struct __riscv_f_ext_state { - unsigned int f[32]; - unsigned int fcsr; -}; - -struct __riscv_d_ext_state { - unsigned long long f[32]; - unsigned int fcsr; -}; - -struct __riscv_q_ext_state { - unsigned long long f[64] __attribute__((aligned(16))); - unsigned int fcsr; - unsigned int reserved[3]; -}; - -union __riscv_fp_state { - struct __riscv_f_ext_state f; - struct __riscv_d_ext_state d; - struct __riscv_q_ext_state q; -}; - -typedef union __riscv_fp_state fpregset_t; - -typedef struct sigcontext { +typedef union __riscv_mc_fp_state fpregset_t; +struct sigcontext { gregset_t gregs; fpregset_t fpregs; -} mcontext_t; - -#else -typedef struct { - unsigned long gregs[32]; - unsigned long long fpregs[66]; -} mcontext_t; +}; #endif struct sigaltstack { @@ -54,10 +51,10 @@ struct sigaltstack { size_t ss_size; }; -typedef struct __ucontext +typedef struct ucontext_t { unsigned long uc_flags; - struct __ucontext *uc_link; + struct ucontext_t *uc_link; stack_t uc_stack; sigset_t uc_sigmask; mcontext_t uc_mcontext; diff --git a/lib/libc/include/riscv64-linux-musl/bits/syscall.h b/lib/libc/include/riscv64-linux-musl/bits/syscall.h index 94dcafb69..423de02ab 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/syscall.h +++ b/lib/libc/include/riscv64-linux-musl/bits/syscall.h @@ -273,6 +273,21 @@ #define __NR_pkey_mprotect 288 #define __NR_pkey_alloc 289 #define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_io_pgetevents 292 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 + #define __NR_sysriscv __NR_arch_specific_syscall #define __NR_riscv_flush_icache (__NR_sysriscv + 15) #define SYS_io_setup 0 @@ -550,5 +565,19 @@ #define SYS_pkey_mprotect 288 #define SYS_pkey_alloc 289 #define SYS_pkey_free 290 +#define SYS_statx 291 +#define SYS_io_pgetevents 292 +#define SYS_rseq 293 +#define SYS_kexec_file_load 294 +#define SYS_pidfd_send_signal 424 +#define SYS_io_uring_setup 425 +#define SYS_io_uring_enter 426 +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 #define SYS_sysriscv __NR_arch_specific_syscall #define SYS_riscv_flush_icache (__NR_sysriscv + 15) \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/user.h b/lib/libc/include/riscv64-linux-musl/bits/user.h index 2f23ff4a7..ffeab50d0 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/user.h +++ b/lib/libc/include/riscv64-linux-musl/bits/user.h @@ -1,43 +1,5 @@ -struct user_regs_struct { - unsigned long pc; - unsigned long ra; - unsigned long sp; - unsigned long gp; - unsigned long tp; - unsigned long t0; - unsigned long t1; - unsigned long t2; - unsigned long s0; - unsigned long s1; - unsigned long a0; - unsigned long a1; - unsigned long a2; - unsigned long a3; - unsigned long a4; - unsigned long a5; - unsigned long a6; - unsigned long a7; - unsigned long s2; - unsigned long s3; - unsigned long s4; - unsigned long s5; - unsigned long s6; - unsigned long s7; - unsigned long s8; - unsigned long s9; - unsigned long s10; - unsigned long s11; - unsigned long t3; - unsigned long t4; - unsigned long t5; - unsigned long t6; -}; - -struct user_fpregs_struct { - double f[32]; - unsigned int fcsr; -}; +#include #define ELF_NGREG 32 typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NGREG]; -typedef struct user_fpregs_struct elf_fpregset_t; \ No newline at end of file +typedef union __riscv_mc_fp_state elf_fpregset_t; \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/ipc.h b/lib/libc/include/s390x-linux-musl/bits/ipc.h deleted file mode 100644 index 7aaaf25c0..000000000 --- a/lib/libc/include/s390x-linux-musl/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __pad1; - unsigned short __ipc_perm_seq; - unsigned long __pad2; - unsigned long __pad3; -}; - -#define IPC_64 0x100 \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/msg.h b/lib/libc/include/s390x-linux-musl/bits/msg.h deleted file mode 100644 index 39bdf2a3d..000000000 --- a/lib/libc/include/s390x-linux-musl/bits/msg.h +++ /dev/null @@ -1,12 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __unused[2]; -}; \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/posix.h b/lib/libc/include/s390x-linux-musl/bits/posix.h deleted file mode 100644 index 9fcc205cc..000000000 --- a/lib/libc/include/s390x-linux-musl/bits/posix.h +++ /dev/null @@ -1,2 +0,0 @@ -#define _POSIX_V6_LP64_OFF64 1 -#define _POSIX_V7_LP64_OFF64 1 \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/reg.h b/lib/libc/include/s390x-linux-musl/bits/reg.h deleted file mode 100644 index 0afb6bd06..000000000 --- a/lib/libc/include/s390x-linux-musl/bits/reg.h +++ /dev/null @@ -1,2 +0,0 @@ -#undef __WORDSIZE -#define __WORDSIZE 64 \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/sem.h b/lib/libc/include/s390x-linux-musl/bits/sem.h deleted file mode 100644 index e19b05592..000000000 --- a/lib/libc/include/s390x-linux-musl/bits/sem.h +++ /dev/null @@ -1,7 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; - unsigned short __pad[3], sem_nsems; - unsigned long __unused[2]; -}; \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/syscall.h b/lib/libc/include/s390x-linux-musl/bits/syscall.h index ee654f930..e2436d95c 100644 --- a/lib/libc/include/s390x-linux-musl/bits/syscall.h +++ b/lib/libc/include/s390x-linux-musl/bits/syscall.h @@ -344,6 +344,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_exit 1 #define SYS_fork 2 @@ -690,4 +696,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/ipc.h b/lib/libc/include/x86_64-linux-musl/bits/ipc.h deleted file mode 100644 index 2d01152c7..000000000 --- a/lib/libc/include/x86_64-linux-musl/bits/ipc.h +++ /dev/null @@ -1,13 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - int __ipc_perm_seq; - long __pad1; - long __pad2; -}; - -#define IPC_64 0 \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/msg.h b/lib/libc/include/x86_64-linux-musl/bits/msg.h deleted file mode 100644 index 39bdf2a3d..000000000 --- a/lib/libc/include/x86_64-linux-musl/bits/msg.h +++ /dev/null @@ -1,12 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __unused[2]; -}; \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/posix.h b/lib/libc/include/x86_64-linux-musl/bits/posix.h deleted file mode 100644 index 9fcc205cc..000000000 --- a/lib/libc/include/x86_64-linux-musl/bits/posix.h +++ /dev/null @@ -1,2 +0,0 @@ -#define _POSIX_V6_LP64_OFF64 1 -#define _POSIX_V7_LP64_OFF64 1 \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/sem.h b/lib/libc/include/x86_64-linux-musl/bits/sem.h new file mode 100644 index 000000000..e63d0732b --- /dev/null +++ b/lib/libc/include/x86_64-linux-musl/bits/sem.h @@ -0,0 +1,11 @@ +struct semid_ds { + struct ipc_perm sem_perm; + time_t sem_otime; + long __unused1; + time_t sem_ctime; + long __unused2; + unsigned short sem_nsems; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; + long __unused3; + long __unused4; +}; \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/syscall.h b/lib/libc/include/x86_64-linux-musl/bits/syscall.h index 8e0379afa..fed8f0384 100644 --- a/lib/libc/include/x86_64-linux-musl/bits/syscall.h +++ b/lib/libc/include/x86_64-linux-musl/bits/syscall.h @@ -337,6 +337,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define SYS_read 0 #define SYS_write 1 @@ -676,4 +682,10 @@ #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 -#define SYS_io_uring_register 427 \ No newline at end of file +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 \ No newline at end of file diff --git a/lib/libc/mingw/COPYING b/lib/libc/mingw/COPYING new file mode 100644 index 000000000..28fa9514c --- /dev/null +++ b/lib/libc/mingw/COPYING @@ -0,0 +1,43 @@ +With exception of certain parts that are prominently marked as being +in the Public Domain, BSD, or LGPL this Software is provided under the +Zope Public License (ZPL) Version 2.1. + +Copyright (c) 2009 - 2013 by the mingw-w64 project + +See the AUTHORS file for the list of contributors to the mingw-w64 project. + +This license has been certified as open source. It has also been designated +as GPL compatible by the Free Software Foundation (FSF). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions in source code must retain the accompanying copyright + notice, this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + 3. Names of the copyright holders must not be used to endorse or promote + products derived from this software without prior written permission + from the copyright holders. + 4. The right to distribute this software or to use it for any purpose does + not give you the right to use Servicemarks (sm) or Trademarks (tm) of + the copyright holders. Use of them is covered by separate agreement + with the copyright holders. + 5. If any files are modified, you must cause the modified files to carry + prominent notices stating that you changed the files and the date of + any change. + +Disclaimer + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/libc/mingw/lib-common/bcrypt.def b/lib/libc/mingw/lib-common/bcrypt.def new file mode 100644 index 000000000..5415bad63 --- /dev/null +++ b/lib/libc/mingw/lib-common/bcrypt.def @@ -0,0 +1,65 @@ +LIBRARY "bcrypt.dll" +EXPORTS +BCryptAddContextFunction +BCryptAddContextFunctionProvider +BCryptCloseAlgorithmProvider +BCryptConfigureContext +BCryptConfigureContextFunction +BCryptCreateContext +BCryptCreateHash +BCryptCreateMultiHash +BCryptDecrypt +BCryptDeleteContext +BCryptDeriveKey +BCryptDeriveKeyCapi +BCryptDeriveKeyPBKDF2 +BCryptDestroyHash +BCryptDestroyKey +BCryptDestroySecret +BCryptDuplicateHash +BCryptDuplicateKey +BCryptEncrypt +BCryptEnumAlgorithms +BCryptEnumContextFunctionProviders +BCryptEnumContextFunctions +BCryptEnumContexts +BCryptEnumProviders +BCryptEnumRegisteredProviders +BCryptExportKey +BCryptFinalizeKeyPair +BCryptFinishHash +BCryptFreeBuffer +BCryptGenRandom +BCryptGenerateKeyPair +BCryptGenerateSymmetricKey +BCryptGetFipsAlgorithmMode +BCryptGetProperty +BCryptHashData +BCryptImportKey +BCryptImportKeyPair +BCryptKeyDerivation +BCryptOpenAlgorithmProvider +BCryptProcessMultiOperations +BCryptQueryContextConfiguration +BCryptQueryContextFunctionConfiguration +BCryptQueryContextFunctionProperty +BCryptQueryProviderRegistration +BCryptRegisterConfigChangeNotify +BCryptRegisterProvider +BCryptRemoveContextFunction +BCryptRemoveContextFunctionProvider +BCryptResolveProviders +BCryptSecretAgreement +BCryptSetAuditingInterface +BCryptSetContextFunctionProperty +BCryptSetProperty +BCryptSignHash +BCryptUnregisterConfigChangeNotify +BCryptUnregisterProvider +BCryptVerifySignature +GetAsymmetricEncryptionInterface +GetCipherInterface +GetHashInterface +GetRngInterface +GetSecretAgreementInterface +GetSignatureInterface diff --git a/lib/libc/mingw/lib-common/comctl32.def b/lib/libc/mingw/lib-common/comctl32.def new file mode 100644 index 000000000..6683f72f2 --- /dev/null +++ b/lib/libc/mingw/lib-common/comctl32.def @@ -0,0 +1,125 @@ +LIBRARY COMCTL32.dll +EXPORTS +MenuHelp +ShowHideMenuCtl +GetEffectiveClientRect +DrawStatusTextA +CreateStatusWindowA +CreateToolbar +CreateMappedBitmap +DPA_LoadStream +DPA_SaveStream +DPA_Merge +CreatePropertySheetPage +MakeDragList +LBItemFromPt +DrawInsert +CreateUpDownControl +InitCommonControls +CreatePropertySheetPageA +CreatePropertySheetPageW +CreateStatusWindow +CreateStatusWindowW +CreateToolbarEx +DestroyPropertySheetPage +DllGetVersion +DllInstall +DrawStatusText +DrawStatusTextW +FlatSB_EnableScrollBar +FlatSB_GetScrollInfo +FlatSB_GetScrollPos +FlatSB_GetScrollProp +FlatSB_GetScrollPropPtr +FlatSB_GetScrollRange +FlatSB_SetScrollInfo +FlatSB_SetScrollPos +FlatSB_SetScrollProp +FlatSB_SetScrollRange +FlatSB_ShowScrollBar +GetMUILanguage +ImageList_Add +ImageList_AddIcon +ImageList_AddMasked +ImageList_BeginDrag +ImageList_Copy +ImageList_Create +ImageList_Destroy +ImageList_DragEnter +ImageList_DragLeave +ImageList_DragMove +ImageList_DragShowNolock +ImageList_Draw +ImageList_DrawEx +ImageList_DrawIndirect +ImageList_Duplicate +ImageList_EndDrag +ImageList_GetBkColor +ImageList_GetDragImage +ImageList_GetFlags +ImageList_GetIcon +ImageList_GetIconSize +ImageList_GetImageCount +ImageList_GetImageInfo +ImageList_GetImageRect +ImageList_LoadImage +ImageList_LoadImageA +ImageList_LoadImageW +ImageList_Merge +ImageList_Read +ImageList_Remove +ImageList_Replace +ImageList_ReplaceIcon +ImageList_SetBkColor +ImageList_SetDragCursorImage +ImageList_SetFilter +ImageList_SetFlags +ImageList_SetIconSize +ImageList_SetImageCount +ImageList_SetOverlayImage +ImageList_Write +InitCommonControlsEx +InitMUILanguage +InitializeFlatSB +LoadIconMetric +PropertySheet +PropertySheetA +PropertySheetW +RegisterClassNameW +UninitializeFlatSB +_TrackMouseEvent +FreeMRUList +Str_SetPtrW +DSA_Create +DSA_Destroy +DSA_GetItem +DSA_GetItemPtr +DSA_InsertItem +DSA_SetItem +DSA_DeleteItem +DSA_DeleteAllItems +DPA_Create +DPA_Destroy +DPA_Grow +DPA_Clone +DPA_GetPtr +DPA_GetPtrIndex +DPA_InsertPtr +DPA_SetPtr +DPA_DeletePtr +DPA_DeleteAllPtrs +DPA_Sort +DPA_Search +DPA_CreateEx +DPA_EnumCallback +DPA_DestroyCallback +DSA_EnumCallback +DSA_DestroyCallback +CreateMRUListW +AddMRUStringW +EnumMRUListW +SetWindowSubclass +RemoveWindowSubclass +DefSubclassProc +TaskDialog +TaskDialogIndirect diff --git a/lib/libc/mingw/lib-common/comdlg32.def b/lib/libc/mingw/lib-common/comdlg32.def new file mode 100644 index 000000000..3ba3d4fdb --- /dev/null +++ b/lib/libc/mingw/lib-common/comdlg32.def @@ -0,0 +1,34 @@ +; +; Exports of file comdlg32.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY comdlg32.dll +EXPORTS +ChooseColorA +ChooseColorW +ChooseFontA +ChooseFontW +CommDlgExtendedError +FindTextA +FindTextW +GetFileTitleA +GetFileTitleW +GetOpenFileNameA +GetOpenFileNameW +GetSaveFileNameA +GetSaveFileNameW +LoadAlterBitmap +PageSetupDlgA +PageSetupDlgW +PrintDlgA +PrintDlgExA +PrintDlgExW +PrintDlgW +ReplaceTextA +ReplaceTextW +Ssync_ANSI_UNICODE_Struct_For_WOW +WantArrows +dwLBSubclass +dwOKSubclass diff --git a/lib/libc/mingw/lib-common/crypt32.def b/lib/libc/mingw/lib-common/crypt32.def new file mode 100644 index 000000000..c03a1fbbf --- /dev/null +++ b/lib/libc/mingw/lib-common/crypt32.def @@ -0,0 +1,300 @@ +LIBRARY "CRYPT32.dll" +EXPORTS +ChainWlxLogoffEvent +CloseCertPerformanceData +CollectCertPerformanceData +OpenCertPerformanceData +CryptObjectLocatorFree +CryptObjectLocatorGet +CryptObjectLocatorGetContent +CryptObjectLocatorGetUpdated +CryptObjectLocatorInitialize +CryptObjectLocatorIsChanged +CryptObjectLocatorRelease +I_PFXImportCertStoreEx +CertAddCRLContextToStore +CertAddCRLLinkToStore +CertAddCTLContextToStore +CertAddCTLLinkToStore +CertAddCertificateContextToStore +CertAddCertificateLinkToStore +CertAddEncodedCRLToStore +CertAddEncodedCTLToStore +CertAddEncodedCertificateToStore +CertAddEncodedCertificateToSystemStoreA +CertAddEncodedCertificateToSystemStoreW +CertAddEnhancedKeyUsageIdentifier +CertAddRefServerOcspResponse +CertAddRefServerOcspResponseContext +CertAddSerializedElementToStore +CertAddStoreToCollection +CertAlgIdToOID +CertCloseServerOcspResponse +CertCloseStore +CertCompareCertificate +CertCompareCertificateName +CertCompareIntegerBlob +CertComparePublicKeyInfo +CertControlStore +CertCreateCRLContext +CertCreateCTLContext +CertCreateCTLEntryFromCertificateContextProperties +CertCreateCertificateChainEngine +CertCreateCertificateContext +CertCreateContext +CertCreateSelfSignCertificate +CertDeleteCRLFromStore +CertDeleteCTLFromStore +CertDeleteCertificateFromStore +CertDuplicateCRLContext +CertDuplicateCTLContext +CertDuplicateCertificateChain +CertDuplicateCertificateContext +CertDuplicateStore +CertEnumCRLContextProperties +CertEnumCRLsInStore +CertEnumCTLContextProperties +CertEnumCTLsInStore +CertEnumCertificateContextProperties +CertEnumCertificatesInStore +CertEnumPhysicalStore +CertEnumSubjectInSortedCTL +CertEnumSystemStore +CertEnumSystemStoreLocation +CertFindAttribute +CertFindCRLInStore +CertFindCTLInStore +CertFindCertificateInCRL +CertFindCertificateInStore +CertFindChainInStore +CertFindExtension +CertFindRDNAttr +CertFindSubjectInCTL +CertFindSubjectInSortedCTL +CertFreeCRLContext +CertFreeCTLContext +CertFreeCertificateChain +CertFreeCertificateChainEngine +CertFreeCertificateChainList +CertFreeCertificateContext +CertFreeServerOcspResponseContext +CertGetCRLContextProperty +CertGetCRLFromStore +CertGetCTLContextProperty +CertGetCertificateChain +CertGetCertificateContextProperty +CertGetEnhancedKeyUsage +CertGetIntendedKeyUsage +CertGetIssuerCertificateFromStore +CertGetNameStringA +CertGetNameStringW +CertGetPublicKeyLength +CertGetServerOcspResponseContext +CertGetStoreProperty +CertGetSubjectCertificateFromStore +CertGetValidUsages +CertIsRDNAttrsInCertificateName +CertIsStrongHashToSign +CertIsValidCRLForCertificate +CertIsWeakHash +CertNameToStrA +CertNameToStrW +CertOIDToAlgId +CertOpenServerOcspResponse +CertOpenStore +CertOpenSystemStoreA +CertOpenSystemStoreW +CertRDNValueToStrA +CertRDNValueToStrW +CertRegisterPhysicalStore +CertRegisterSystemStore +CertRemoveEnhancedKeyUsageIdentifier +CertRemoveStoreFromCollection +CertResyncCertificateChainEngine +CertRetrieveLogoOrBiometricInfo +CertSaveStore +CertSelectCertificateChains +CertSerializeCRLStoreElement +CertSerializeCTLStoreElement +CertSerializeCertificateStoreElement +CertSetCRLContextProperty +CertSetCTLContextProperty +CertSetCertificateContextPropertiesFromCTLEntry +CertSetCertificateContextProperty +CertSetEnhancedKeyUsage +CertSetStoreProperty +CertStrToNameA +CertStrToNameW +CertUnregisterPhysicalStore +CertUnregisterSystemStore +CertVerifyCRLRevocation +CertVerifyCRLTimeValidity +CertVerifyCTLUsage +CertVerifyCertificateChainPolicy +CertVerifyRevocation +CertVerifySubjectCertificateContext +CertVerifyTimeValidity +CertVerifyValidityNesting +CryptAcquireCertificatePrivateKey +CryptBinaryToStringA +CryptBinaryToStringW +CryptCloseAsyncHandle +CryptCreateAsyncHandle +CryptCreateKeyIdentifierFromCSP +CryptDecodeMessage +CryptDecodeObject +CryptDecodeObjectEx +CryptDecryptAndVerifyMessageSignature +CryptDecryptMessage +CryptEncodeObject +CryptEncodeObjectEx +CryptEncryptMessage +CryptEnumKeyIdentifierProperties +CryptEnumOIDFunction +CryptEnumOIDInfo +CryptExportPKCS8 +CryptExportPublicKeyInfo +CryptExportPublicKeyInfoEx +CryptExportPublicKeyInfoFromBCryptKeyHandle +CryptFindCertificateKeyProvInfo +CryptFindLocalizedName +CryptFindOIDInfo +CryptFormatObject +CryptFreeOIDFunctionAddress +CryptGetAsyncParam +CryptGetDefaultOIDDllList +CryptGetDefaultOIDFunctionAddress +CryptGetKeyIdentifierProperty +CryptGetMessageCertificates +CryptGetMessageSignerCount +CryptGetOIDFunctionAddress +CryptGetOIDFunctionValue +CryptGetDefaultProviderA +CryptGetDefaultProviderW +CryptHashCertificate +CryptHashCertificate2 +CryptHashMessage +CryptHashPublicKeyInfo +CryptHashToBeSigned +CryptImportPKCS8 +CryptImportPublicKeyInfo +CryptImportPublicKeyInfoEx +CryptImportPublicKeyInfoEx2 +CryptInitOIDFunctionSet +CryptInstallDefaultContext +CryptInstallOIDFunctionAddress +CryptLoadSip +CryptMemAlloc +CryptMemFree +CryptMemRealloc +CryptMsgCalculateEncodedLength +CryptMsgClose +CryptMsgControl +CryptMsgCountersign +CryptMsgCountersignEncoded +CryptMsgDuplicate +CryptMsgEncodeAndSignCTL +CryptMsgGetAndVerifySigner +CryptMsgGetParam +CryptMsgOpenToDecode +CryptMsgOpenToEncode +CryptMsgSignCTL +CryptMsgUpdate +CryptMsgVerifyCountersignatureEncoded +CryptMsgVerifyCountersignatureEncodedEx +CryptProtectData +CryptProtectMemory +CryptQueryObject +CryptRegisterDefaultOIDFunction +CryptRegisterOIDFunction +CryptRegisterOIDInfo +CryptRetrieveTimeStamp +CryptSIPAddProvider +CryptSIPCreateIndirectData +CryptSIPGetCaps +CryptSIPGetSealedDigest +CryptSIPGetSignedDataMsg +CryptSIPLoad +CryptSIPPutSignedDataMsg +CryptSIPRemoveProvider +CryptSIPRemoveSignedDataMsg +CryptSIPRetrieveSubjectGuid +CryptSIPRetrieveSubjectGuidForCatalogFile +CryptSIPVerifyIndirectData +CryptSetAsyncParam +CryptSetKeyIdentifierProperty +CryptSetOIDFunctionValue +CryptSignAndEncodeCertificate +CryptSignAndEncryptMessage +CryptSignCertificate +CryptSignMessage +CryptSignMessageWithKey +CryptStringToBinaryA +CryptStringToBinaryW +CryptUninstallDefaultContext +CryptUnprotectData +CryptUnprotectMemory +CryptUnregisterDefaultOIDFunction +CryptUnregisterOIDFunction +CryptUnregisterOIDInfo +CryptUpdateProtectedState +CryptVerifyCertificateSignature +CryptVerifyCertificateSignatureEx +CryptVerifyDetachedMessageHash +CryptVerifyDetachedMessageSignature +CryptVerifyMessageHash +CryptVerifyMessageSignature +CryptVerifyMessageSignatureWithKey +CryptVerifyTimeStampSignature +I_CertChainEngineIsDisallowedCertificate +I_CertDiagControl +I_CertFinishSslHandshake +I_CertProcessSslHandshake +I_CertProtectFunction +I_CertSrvProtectFunction +I_CertSyncStore +I_CertUpdateStore +I_CryptAddRefLruEntry +I_CryptAddSmartCardCertToStore +I_CryptAllocTls +I_CryptCreateLruCache +I_CryptCreateLruEntry +I_CryptDetachTls +I_CryptDisableLruOfEntries +I_CryptEnableLruOfEntries +I_CryptEnumMatchingLruEntries +I_CryptFindLruEntry +I_CryptFindLruEntryData +I_CryptFindSmartCardCertInStore +I_CryptFlushLruCache +I_CryptFreeLruCache +I_CryptFreeTls +I_CryptGetAsn1Decoder +I_CryptGetAsn1Encoder +I_CryptGetDefaultCryptProv +I_CryptGetDefaultCryptProvForEncrypt +I_CryptGetFileVersion +I_CryptGetLruEntryData +I_CryptGetLruEntryIdentifier +I_CryptGetOssGlobal +I_CryptGetTls +I_CryptAllocTlsEx +I_CryptInsertLruEntry +I_CryptInstallAsn1Module +I_CryptInstallOssGlobal +I_CryptReadTrustedPublisherDWORDValueFromRegistry +I_CryptRegisterSmartCardStore +I_CryptReleaseLruEntry +I_CryptRemoveLruEntry +I_CryptSetTls +I_CryptTouchLruEntry +I_CryptUninstallAsn1Module +I_CryptUninstallOssGlobal +I_CryptUnregisterSmartCardStore +I_CryptWalkAllLruCacheEntries +PFXExportCertStore +PFXExportCertStore2 +PFXExportCertStoreEx +PFXImportCertStore +PFXIsPFXBlob +PFXVerifyPassword diff --git a/lib/libc/mingw/lib-common/cryptnet.def b/lib/libc/mingw/lib-common/cryptnet.def new file mode 100644 index 000000000..4946bcc74 --- /dev/null +++ b/lib/libc/mingw/lib-common/cryptnet.def @@ -0,0 +1,23 @@ +LIBRARY "CRYPTNET.dll" +EXPORTS +CertDllVerifyCTLUsage +CertDllVerifyRevocation +CryptnetWlxLogoffEvent +I_CryptConvertIriToAsciiOrUnicode +I_CryptConvertIriToAsciiOrUnicodeWithFlags +LdapProvOpenStore +CryptCancelAsyncRetrieval +CryptFlushTimeValidObject +CryptGetObjectUrl +CryptGetTimeValidObject +CryptInstallCancelRetrieval +CryptRetrieveObjectByUrlA +CryptRetrieveObjectByUrlW +CryptUninstallCancelRetrieval +I_CryptNetEnumUrlCacheEntry +I_CryptNetGetConnectivity +I_CryptNetGetHostNameFromUrl +I_CryptNetGetUserDsStoreUrl +I_CryptNetIsConnected +I_CryptNetSetUrlCacheFlushInfo +I_CryptNetSetUrlCachePreFetchInfo diff --git a/lib/libc/mingw/lib-common/lz32.def b/lib/libc/mingw/lib-common/lz32.def new file mode 100644 index 000000000..8b375309b --- /dev/null +++ b/lib/libc/mingw/lib-common/lz32.def @@ -0,0 +1,22 @@ +; +; Exports of file LZ32.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY LZ32.dll +EXPORTS +CopyLZFile +GetExpandedNameA +GetExpandedNameW +LZClose +LZCloseFile +LZCopy +LZCreateFileW +LZDone +LZInit +LZOpenFileA +LZOpenFileW +LZRead +LZSeek +LZStart diff --git a/lib/libc/mingw/lib-common/mpr.def b/lib/libc/mingw/lib-common/mpr.def new file mode 100644 index 000000000..0baa1a316 --- /dev/null +++ b/lib/libc/mingw/lib-common/mpr.def @@ -0,0 +1,94 @@ +; +; Definition file of MPR.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "MPR.dll" +EXPORTS +DoBroadcastSystemMessage +DoCommandLinePrompt +DoPasswordDialog +DoProfileErrorDialog +ShowReconnectDialog +ShowReconnectDialogEnd +ShowReconnectDialogUI +WNetConnectionDialog2 +WNetDisconnectDialog2 +I_MprSaveConn +MultinetGetConnectionPerformanceA +MultinetGetConnectionPerformanceW +MultinetGetErrorTextA +MultinetGetErrorTextW +RestoreConnectionA0 +WNetAddConnection2A +WNetAddConnection2W +WNetAddConnection3A +WNetAddConnection3W +WNetAddConnectionA +WNetAddConnectionW +WNetCancelConnection2A +WNetCancelConnection2W +WNetCancelConnectionA +WNetCancelConnectionW +WNetClearConnections +WNetCloseEnum +WNetConnectionDialog +WNetConnectionDialog1A +WNetConnectionDialog1W +WNetDirectoryNotifyA +WNetDirectoryNotifyW +WNetDisconnectDialog +WNetDisconnectDialog1A +WNetDisconnectDialog1W +WNetEnumResourceA +WNetEnumResourceW +WNetFMXEditPerm +WNetFMXGetPermCaps +WNetFMXGetPermHelp +WNetFormatNetworkNameA +WNetFormatNetworkNameW +WNetGetConnection2A +WNetGetConnection2W +WNetGetConnection3A +WNetGetConnection3W +WNetGetConnectionA +WNetGetConnectionW +WNetGetDirectoryTypeA +WNetGetDirectoryTypeW +WNetGetHomeDirectoryW +WNetGetLastErrorA +WNetGetLastErrorW +WNetGetNetworkInformationA +WNetGetNetworkInformationW +WNetGetPropertyTextA +WNetGetPropertyTextW +WNetGetProviderNameA +WNetGetProviderNameW +WNetGetProviderTypeA +WNetGetProviderTypeW +WNetGetResourceInformationA +WNetGetResourceInformationW +WNetGetResourceParentA +WNetGetResourceParentW +WNetGetSearchDialog +WNetGetUniversalNameA +WNetGetUniversalNameW +WNetGetUserA +WNetGetUserW +WNetLogonNotify +WNetOpenEnumA +WNetOpenEnumW +WNetPasswordChangeNotify +WNetPropertyDialogA +WNetPropertyDialogW +WNetRestoreAllConnectionsW +WNetRestoreConnection2W +WNetRestoreConnectionW +WNetRestoreSingleConnectionW +WNetSetConnectionA +WNetSetConnectionW +WNetSetLastErrorA +WNetSetLastErrorW +WNetSupportGlobalEnum +WNetUseConnectionA +WNetUseConnectionW diff --git a/lib/libc/mingw/lib-common/ncrypt.def b/lib/libc/mingw/lib-common/ncrypt.def new file mode 100644 index 000000000..735cc4578 --- /dev/null +++ b/lib/libc/mingw/lib-common/ncrypt.def @@ -0,0 +1,137 @@ +; +; Definition file of ncrypt.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "ncrypt.dll" +EXPORTS +BCryptAddContextFunction +BCryptAddContextFunctionProvider +BCryptCloseAlgorithmProvider +BCryptConfigureContext +BCryptConfigureContextFunction +BCryptCreateContext +BCryptCreateHash +BCryptDecrypt +BCryptDeleteContext +BCryptDeriveKey +BCryptDeriveKeyCapi +BCryptDeriveKeyPBKDF2 +BCryptDestroyHash +BCryptDestroyKey +BCryptDestroySecret +BCryptDuplicateHash +BCryptDuplicateKey +BCryptEncrypt +BCryptEnumAlgorithms +BCryptEnumContextFunctionProviders +BCryptEnumContextFunctions +BCryptEnumContexts +BCryptEnumProviders +BCryptEnumRegisteredProviders +BCryptExportKey +BCryptFinalizeKeyPair +BCryptFinishHash +BCryptFreeBuffer +BCryptGenRandom +BCryptGenerateKeyPair +BCryptGenerateSymmetricKey +BCryptGetFipsAlgorithmMode +BCryptGetProperty +BCryptHashData +BCryptImportKey +BCryptImportKeyPair +BCryptKeyDerivation +BCryptOpenAlgorithmProvider +BCryptQueryContextConfiguration +BCryptQueryContextFunctionConfiguration +BCryptQueryContextFunctionProperty +BCryptQueryProviderRegistration +BCryptRegisterConfigChangeNotify +BCryptRegisterProvider +BCryptRemoveContextFunction +BCryptRemoveContextFunctionProvider +BCryptResolveProviders +BCryptSecretAgreement +BCryptSetAuditingInterface +BCryptSetContextFunctionProperty +BCryptSetProperty +BCryptSignHash +BCryptUnregisterConfigChangeNotify +BCryptUnregisterProvider +BCryptVerifySignature +GetIsolationServerInterface +GetKeyStorageInterface +GetSChannelInterface +NCryptCloseKeyProtector +NCryptCloseProtectionDescriptor +NCryptCreatePersistedKey +NCryptCreateProtectionDescriptor +NCryptDecrypt +NCryptDeleteKey +NCryptDeriveKey +NCryptDuplicateKeyProtectorHandle +NCryptEncrypt +NCryptEnumAlgorithms +NCryptEnumKeys +NCryptEnumStorageProviders +NCryptExportKey +NCryptFinalizeKey +NCryptFreeBuffer +NCryptFreeObject +NCryptGetProperty +NCryptGetProtectionDescriptorInfo +NCryptImportKey +NCryptIsAlgSupported +NCryptIsKeyHandle +NCryptKeyDerivation +NCryptNotifyChangeKey +NCryptOpenKey +NCryptOpenKeyProtector +NCryptOpenStorageProvider +NCryptProtectKey +NCryptProtectSecret +NCryptQueryProtectionDescriptorName +NCryptRegisterProtectionDescriptorName +NCryptSecretAgreement +NCryptSetAuditingInterface +NCryptSetProperty +NCryptSignHash +NCryptStreamClose +NCryptStreamOpenToProtect +NCryptStreamOpenToUnprotect +NCryptStreamUpdate +NCryptTranslateHandle +NCryptUnprotectKey +NCryptUnprotectSecret +NCryptVerifySignature +SslChangeNotify +SslComputeClientAuthHash +SslComputeEapKeyBlock +SslComputeFinishedHash +SslCreateClientAuthHash +SslCreateEphemeralKey +SslCreateHandshakeHash +SslDecrementProviderReferenceCount +SslDecryptPacket +SslEncryptPacket +SslEnumCipherSuites +SslEnumProtocolProviders +SslExportKey +SslFreeBuffer +SslFreeObject +SslGenerateMasterKey +SslGenerateSessionKeys +SslGetCipherSuitePRFHashAlgorithm +SslGetKeyProperty +SslGetProviderProperty +SslHashHandshake +SslImportKey +SslImportMasterKey +SslIncrementProviderReferenceCount +SslLookupCipherLengths +SslLookupCipherSuiteInfo +SslOpenPrivateKey +SslOpenProvider +SslSignHash +SslVerifySignature diff --git a/lib/libc/mingw/lib-common/netapi32.def b/lib/libc/mingw/lib-common/netapi32.def new file mode 100644 index 000000000..5131c87be --- /dev/null +++ b/lib/libc/mingw/lib-common/netapi32.def @@ -0,0 +1,386 @@ +LIBRARY "NETAPI32.dll" +EXPORTS +CredpValidateTargetName +DavAddConnection +DavDeleteConnection +DavFlushFile +DavGetExtendedError +DavGetHTTPFromUNCPath +DavGetUNCFromHTTPPath +DsAddressToSiteNamesA +DsAddressToSiteNamesExA +DsAddressToSiteNamesExW +DsAddressToSiteNamesW +DsDeregisterDnsHostRecordsA +DsDeregisterDnsHostRecordsW +DsEnumerateDomainTrustsA +DsEnumerateDomainTrustsW +DsGetDcCloseW +DsGetDcNameA +DsGetDcNameW +DsGetDcNameWithAccountA +DsGetDcNameWithAccountW +DsGetDcNextA +DsGetDcNextW +DsGetDcOpenA +DsGetDcOpenW +DsGetDcSiteCoverageA +DsGetDcSiteCoverageW +DsGetForestTrustInformationW +DsGetSiteNameA +DsGetSiteNameW +DsMergeForestTrustInformationW +DsRoleAbortDownlevelServerUpgrade +DsRoleCancel +DsRoleDcAsDc +DsRoleDcAsReplica +DsRoleDemoteDc +DsRoleDnsNameToFlatName +DsRoleFreeMemory +DsRoleGetDatabaseFacts +DsRoleGetDcOperationProgress +DsRoleGetDcOperationResults +DsRoleGetPrimaryDomainInformation +DsRoleIfmHandleFree +DsRoleServerSaveStateForUpgrade +DsRoleUpgradeDownlevelServer +DsValidateSubnetNameA +DsValidateSubnetNameW +I_BrowserDebugCall +I_BrowserDebugTrace +I_BrowserQueryEmulatedDomains +I_BrowserQueryOtherDomains +I_BrowserQueryStatistics +I_BrowserResetNetlogonState +I_BrowserResetStatistics +I_BrowserServerEnum +I_BrowserSetNetlogonState +I_DsUpdateReadOnlyServerDnsRecords +I_NetAccountDeltas +I_NetAccountSync +I_NetChainSetClientAttributes +I_NetChainSetClientAttributes2 +I_NetDatabaseDeltas +I_NetDatabaseRedo +I_NetDatabaseSync +I_NetDatabaseSync2 +I_NetDfsCreateExitPoint +I_NetDfsCreateLocalPartition +I_NetDfsDeleteExitPoint +I_NetDfsDeleteLocalPartition +I_NetDfsFixLocalVolume +I_NetDfsGetFtServers +I_NetDatabaseDeltas +I_NetDatabaseRedo +I_NetDatabaseSync +I_NetDatabaseSync2 +I_NetDfsGetVersion +I_NetDfsIsThisADomainName +I_NetDfsManagerReportSiteInfo +I_NetDfsModifyPrefix +I_NetDfsSetLocalVolumeState +I_NetDfsSetServerInfo +I_NetGetDCList +I_NetGetForestTrustInformation +I_NetListCanonicalize +I_NetListTraverse +I_NetLogonControl +I_NetLogonControl2 +I_NetLogonGetDomainInfo +I_NetLogonSamLogoff +I_NetLogonSamLogon +I_NetLogonSamLogonEx +I_NetLogonSamLogonWithFlags +I_NetLogonSendToSam +I_NetLogonUasLogoff +I_NetLogonUasLogon +I_NetNameCanonicalize +I_NetNameCompare +I_NetNameValidate +I_NetPathCanonicalize +I_NetPathCompare +I_NetPathType +I_NetLogonSamLogonEx +I_NetLogonSamLogonWithFlags +I_NetLogonSendToSam +I_NetLogonUasLogoff +I_NetLogonUasLogon +I_NetServerAuthenticate +I_NetServerAuthenticate2 +I_NetServerAuthenticate3 +I_NetServerGetTrustInfo +I_NetServerPasswordGet +I_NetServerPasswordSet +I_NetServerPasswordSet2 +I_NetServerReqChallenge +I_NetServerSetServiceBits +I_NetServerSetServiceBitsEx +I_NetServerTrustPasswordsGet +I_NetlogonComputeClientDigest +I_NetlogonComputeServerDigest +I_NetlogonGetTrustRid +NetAccessAdd +NetAccessDel +NetAccessEnum +NetAccessGetInfo +NetAccessGetUserPerms +NetAccessSetInfo +NetAddAlternateComputerName +NetAddServiceAccount +NetAlertRaise +NetAlertRaiseEx +NetApiBufferAllocate +NetApiBufferFree +NetApiBufferReallocate +NetApiBufferSize +NetAuditClear +NetAuditRead +NetAuditWrite +NetBrowserStatisticsGet +NetConfigGet +NetConfigGetAll +NetConfigSet +NetConnectionEnum +NetCreateProvisioningPackage +NetDfsAdd +NetDfsAddFtRoot +NetDfsAddRootTarget +NetDfsAddStdRoot +NetDfsAddStdRootForced +NetDfsEnum +NetDfsGetClientInfo +NetDfsGetDcAddress +NetDfsGetFtContainerSecurity +NetDfsGetInfo +NetDfsGetSecurity +NetDfsGetStdContainerSecurity +NetDfsGetSupportedNamespaceVersion +NetDfsManagerGetConfigInfo +NetDfsManagerInitialize +NetDfsManagerSendSiteInfo +NetDfsMove +NetDfsRemove +NetDfsRemoveFtRoot +NetDfsRemoveFtRootForced +NetDfsRemoveRootTarget +NetDfsRemoveStdRoot +NetDfsRename +NetDfsSetClientInfo +NetDfsSetFtContainerSecurity +NetDfsSetInfo +NetDfsSetSecurity +NetDfsSetStdContainerSecurity +NetEnumerateComputerNames +NetEnumerateServiceAccounts +NetEnumerateTrustedDomains +NetErrorLogClear +NetErrorLogRead +NetErrorLogWrite +NetFileClose +NetFileEnum +NetFileGetInfo +NetGetAnyDCName +NetGetDCName +NetGetDisplayInformationIndex +NetGetJoinInformation +NetGetJoinableOUs +NetGroupAdd +NetGroupAddUser +NetGroupDel +NetGroupDelUser +NetGroupEnum +NetGroupGetInfo +NetGroupGetUsers +NetGroupSetInfo +NetGroupSetUsers +NetIsServiceAccount +NetJoinDomain +NetLocalGroupAdd +NetLocalGroupAddMember +NetLocalGroupAddMembers +NetLocalGroupDel +NetLocalGroupDelMember +NetLocalGroupDelMembers +NetLocalGroupEnum +NetLocalGroupGetInfo +NetLocalGroupGetMembers +NetLocalGroupSetInfo +NetLocalGroupSetMembers +NetLogonGetTimeServiceParentDomain +NetLogonSetServiceBits +NetMessageBufferSend +NetMessageNameAdd +NetMessageNameDel +NetMessageNameEnum +NetMessageNameGetInfo +NetProvisionComputerAccount +NetQueryDisplayInformation +NetQueryServiceAccount +NetRegisterDomainNameChangeNotification +NetRemoteComputerSupports +NetRemoteTOD +NetRemoveAlternateComputerName +NetRemoveServiceAccount +NetRenameMachineInDomain +NetReplExportDirAdd +NetReplExportDirDel +NetReplExportDirEnum +NetReplExportDirGetInfo +NetReplExportDirLock +NetReplExportDirSetInfo +NetReplExportDirUnlock +NetReplGetInfo +NetReplImportDirAdd +NetReplImportDirDel +NetReplImportDirEnum +NetReplImportDirGetInfo +NetReplImportDirLock +NetReplImportDirUnlock +NetReplSetInfo +NetRequestOfflineDomainJoin +NetRequestProvisioningPackageInstall +NetScheduleJobAdd +NetScheduleJobDel +NetScheduleJobEnum +NetScheduleJobGetInfo +NetServerAliasAdd +NetServerAliasDel +NetServerAliasEnum +NetServerComputerNameAdd +NetServerComputerNameDel +NetServerDiskEnum +NetServerEnum +NetServerEnumEx +NetServerGetInfo +NetServerSetInfo +NetServerTransportAdd +NetServerTransportAddEx +NetServerTransportDel +NetServerTransportEnum +NetServiceControl +NetServiceEnum +NetServiceGetInfo +NetServiceInstall +NetSessionDel +NetSessionEnum +NetSessionGetInfo +NetSetPrimaryComputerName +NetShareAdd +NetShareCheck +NetShareDel +NetShareDelEx +NetShareDelSticky +NetShareEnum +NetShareEnumSticky +NetShareGetInfo +NetShareSetInfo +NetStatisticsGet +NetUnjoinDomain +NetUnregisterDomainNameChangeNotification +NetUseAdd +NetUseDel +NetUseEnum +NetUseGetInfo +NetUserAdd +NetUserChangePassword +NetUserDel +NetUserEnum +NetUserGetGroups +NetUserGetInfo +NetUserGetLocalGroups +NetUserModalsGet +NetUserModalsSet +NetUserSetGroups +NetUserSetInfo +NetValidateName +NetValidatePasswordPolicy +NetValidatePasswordPolicyFree +NetWkstaGetInfo +NetWkstaSetInfo +NetWkstaTransportAdd +NetWkstaTransportDel +NetWkstaTransportEnum +NetWkstaUserEnum +NetWkstaUserGetInfo +NetWkstaUserSetInfo +NetapipBufferAllocate +Netbios +NetpAccessCheck +NetpAccessCheckAndAudit +NetpAccessCheckAndAuditEx +NetpAddTlnFtinfoEntry +NetpAllocConfigName +NetpAllocFtinfoEntry +NetpAllocStrFromWStr +NetpAllocWStrFromStr +NetpAllocWStrFromWStr +NetpApiStatusToNtStatus +NetpAssertFailed +NetpCleanFtinfoContext +NetpCloseConfigData +NetpCopyFtinfoContext +NetpCopyStringToBuffer +NetpCreateSecurityObject +NetpDbgPrint +NetpDeleteSecurityObject +NetpEventlogClose +NetpEventlogOpen +NetpEventlogWriteEx +NetpGetComputerName +NetpGetConfigBool +NetpGetConfigDword +NetpGetConfigTStrArray +NetpGetConfigValue +NetpGetDomainName +NetpGetFileSecurity +NetpGetPrivilege +NetpHexDump +NetpInitFtinfoContext +NetpInitOemString +NetpIsRemote +NetpIsUncComputerNameValid +NetpLocalTimeZoneOffset +NetpLogonPutUnicodeString +NetpMergeFtinfo +NetpNetBiosAddName +NetpNetBiosCall +NetpNetBiosDelName +NetpNetBiosGetAdapterNumbers +NetpNetBiosHangup +NetpNetBiosReceive +NetpNetBiosReset +NetpNetBiosSend +NetpNetBiosStatusToApiStatus +NetpNtStatusToApiStatus +NetpOpenConfigData +NetpPackString +NetpParmsQueryUserProperty +NetpParmsQueryUserPropertyWithLength +NetpParmsSetUserProperty +NetpParmsSetUserPropertyWithLength +NetpParmsUserPropertyFree +NetpReleasePrivilege +NetpSetFileSecurity +NetpSmbCheck +NetpStoreIntialDcRecord +NetpStringToNetBiosName +NetpTStrArrayEntryCount +NetpUpgradePreNT5JoinInfo +NetpwNameCanonicalize +NetpwNameCompare +NetpwNameValidate +NetpwPathCanonicalize +NetpwPathCompare +NetpwPathType +NlBindingAddServerToCache +NlBindingRemoveServerFromCache +NlBindingSetAuthInfo +RxNetAccessAdd +RxNetAccessDel +RxNetAccessEnum +RxNetAccessGetInfo +RxNetAccessGetUserPerms +RxNetAccessSetInfo +RxNetServerEnum +RxNetUserPasswordSet +RxRemoteApi diff --git a/lib/libc/mingw/lib-common/rpcns4.def b/lib/libc/mingw/lib-common/rpcns4.def new file mode 100644 index 000000000..12fead346 --- /dev/null +++ b/lib/libc/mingw/lib-common/rpcns4.def @@ -0,0 +1,71 @@ +; +; Exports of file RPCNS4.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY RPCNS4.dll +EXPORTS +I_GetDefaultEntrySyntax +I_RpcNsGetBuffer +I_RpcNsNegotiateTransferSyntax +I_RpcNsRaiseException +I_RpcNsSendReceive +I_RpcReBindBuffer +RpcIfIdVectorFree +RpcNsBindingExportA +RpcNsBindingExportPnPA +RpcNsBindingExportPnPW +RpcNsBindingExportW +RpcNsBindingImportBeginA +RpcNsBindingImportBeginW +RpcNsBindingImportDone +RpcNsBindingImportNext +RpcNsBindingLookupBeginA +RpcNsBindingLookupBeginW +RpcNsBindingLookupDone +RpcNsBindingLookupNext +RpcNsBindingSelect +RpcNsBindingUnexportA +RpcNsBindingUnexportPnPA +RpcNsBindingUnexportPnPW +RpcNsBindingUnexportW +RpcNsEntryExpandNameA +RpcNsEntryExpandNameW +RpcNsEntryObjectInqBeginA +RpcNsEntryObjectInqBeginW +RpcNsEntryObjectInqDone +RpcNsEntryObjectInqNext +RpcNsGroupDeleteA +RpcNsGroupDeleteW +RpcNsGroupMbrAddA +RpcNsGroupMbrAddW +RpcNsGroupMbrInqBeginA +RpcNsGroupMbrInqBeginW +RpcNsGroupMbrInqDone +RpcNsGroupMbrInqNextA +RpcNsGroupMbrInqNextW +RpcNsGroupMbrRemoveA +RpcNsGroupMbrRemoveW +RpcNsMgmtBindingUnexportA +RpcNsMgmtBindingUnexportW +RpcNsMgmtEntryCreateA +RpcNsMgmtEntryCreateW +RpcNsMgmtEntryDeleteA +RpcNsMgmtEntryDeleteW +RpcNsMgmtEntryInqIfIdsA +RpcNsMgmtEntryInqIfIdsW +RpcNsMgmtHandleSetExpAge +RpcNsMgmtInqExpAge +RpcNsMgmtSetExpAge +RpcNsProfileDeleteA +RpcNsProfileDeleteW +RpcNsProfileEltAddA +RpcNsProfileEltAddW +RpcNsProfileEltInqBeginA +RpcNsProfileEltInqBeginW +RpcNsProfileEltInqDone +RpcNsProfileEltInqNextA +RpcNsProfileEltInqNextW +RpcNsProfileEltRemoveA +RpcNsProfileEltRemoveW diff --git a/lib/libc/mingw/lib-common/rpcrt4.def b/lib/libc/mingw/lib-common/rpcrt4.def new file mode 100644 index 000000000..4be11bc6f --- /dev/null +++ b/lib/libc/mingw/lib-common/rpcrt4.def @@ -0,0 +1,577 @@ +LIBRARY "RPCRT4.dll" +EXPORTS +CreateProxyFromTypeInfo +CreateStubFromTypeInfo +I_RpcFixTransferSyntax +I_RpcBindingInqCurrentModifiedId +I_RpcFwThisIsTheManager +I_RpcInitFwImports +I_RpcInitHttpImports +I_RpcInitImports +I_RpcInitNdrImports +I_RpcMgmtQueryDedicatedThreadPool +I_RpcOpenClientProcess +I_RpcOpenClientThread +I_RpcServerTurnOnOffKeepalives +I_RpcVerifierCorruptionExpected +NdrFullPointerFree +NdrFullPointerInsertRefId +NdrFullPointerQueryPointer +NdrFullPointerQueryRefId +NdrGetBaseInterfaceFromStub +NdrpClientCall2 +RpcCertMatchPrincipalName +pfnFreeRoutines DATA +pfnMarshallRoutines DATA +pfnSizeRoutines DATA +pfnUnmarshallRoutines DATA +CStdStubBuffer_AddRef +CStdStubBuffer_Connect +CStdStubBuffer_CountRefs +CStdStubBuffer_DebugServerQueryInterface +CStdStubBuffer_DebugServerRelease +CStdStubBuffer_Disconnect +CStdStubBuffer_Invoke +CStdStubBuffer_IsIIDSupported +CStdStubBuffer_QueryInterface +CreateProxyFromTypeInfo +CreateStubFromTypeInfo +DceErrorInqTextA +DceErrorInqTextW +DllGetClassObject +DllInstall +DllRegisterServer +GlobalMutexClearExternal +GlobalMutexRequestExternal +IUnknown_AddRef_Proxy +IUnknown_QueryInterface_Proxy +IUnknown_Release_Proxy +I_RpcAbortAsyncCall +I_RpcAllocate +I_RpcAsyncAbortCall +I_RpcAsyncSetHandle +I_RpcBCacheAllocate +I_RpcBCacheFree +I_RpcBindingCopy +I_RpcBindingCreateNP +I_RpcBindingHandleToAsyncHandle +I_RpcBindingInqClientTokenAttributes +I_RpcBindingInqConnId +I_RpcBindingInqDynamicEndpoint +I_RpcBindingInqDynamicEndpointA +I_RpcBindingInqDynamicEndpointW +I_RpcBindingInqLocalClientPID +I_RpcBindingInqMarshalledTargetInfo +I_RpcBindingInqSecurityContext +I_RpcBindingInqSecurityContextKeyInfo +I_RpcBindingInqTransportType +I_RpcBindingInqWireIdForSnego +I_RpcBindingIsClientLocal +I_RpcBindingIsServerLocal +I_RpcBindingSetPrivateOption +I_RpcBindingToStaticStringBindingW +I_RpcCertProcessAndProvision +I_RpcClearMutex +I_RpcCompleteAndFree +I_RpcConnectionInqSockBuffSize +I_RpcConnectionSetSockBuffSize +I_RpcCompleteAndFree +I_RpcDeleteMutex +I_RpcEnableWmiTrace +I_RpcExceptionFilter +I_RpcFilterDCOMActivation +I_RpcFree +I_RpcFreeBuffer +I_RpcFreePipeBuffer +I_RpcGetBuffer +I_RpcGetBufferWithObject +I_RpcGetCurrentCallHandle +I_RpcGetDefaultSD +I_RpcGetExtendedError +I_RpcGetPortAllocationData +I_RpcIfInqTransferSyntaxes +I_RpcLogEvent +I_RpcMapWin32Status +I_RpcMarshalBindingHandleAndInterfaceForNDF +I_RpcMgmtEnableDedicatedThreadPool +I_RpcNDRCGetWireRepresentation +I_RpcNDRSContextEmergencyCleanup +I_RpcNegotiateTransferSyntax +I_RpcNsBindingSetEntryName +I_RpcNsBindingSetEntryNameA +I_RpcNsBindingSetEntryNameW +I_RpcNsInterfaceExported +I_RpcNsInterfaceUnexported +I_RpcOpenClientProcess +I_RpcParseSecurity +I_RpcPauseExecution +I_RpcProxyNewConnection +I_RpcReallocPipeBuffer +I_RpcReceive +I_RpcRecordCalloutFailure +I_RpcReplyToClientWithStatus +I_RpcRequestMutex +I_RpcSNCHOption +I_RpcSend +I_RpcSendReceive +I_RpcServerAllocateIpPort +I_RpcServerCheckClientRestriction +I_RpcServerDisableExceptionFilter +I_RpcServerGetAssociationID +I_RpcServerInqAddressChangeFn +I_RpcServerInqLocalConnAddress +I_RpcServerInqRemoteConnAddress +I_RpcServerInqTransportType +I_RpcServerIsClientDisconnected +I_RpcServerRegisterForwardFunction +I_RpcServerSetAddressChangeFn +I_RpcServerStartService +I_RpcServerSubscribeForDisconnectNotification +I_RpcServerUseProtseq2A +I_RpcServerUseProtseq2W +I_RpcServerUseProtseqEp2A +I_RpcServerUseProtseqEp2W +I_RpcSessionStrictContextHandle +I_RpcSetAsyncHandle +I_RpcSetDCOMAppId +I_RpcSsDontSerializeContext +I_RpcSystemFunction001 +I_RpcTransConnectionAllocatePacket +I_RpcTransConnectionFreePacket +I_RpcTransConnectionReallocPacket +I_RpcTransDatagramAllocate +I_RpcTransDatagramAllocate2 +I_RpcTransDatagramFree +I_RpcTransGetThreadEvent +I_RpcTransGetThreadEventThreadOptional +I_RpcTransIoCancelled +I_RpcTransServerNewConnection +I_RpcTurnOnEEInfoPropagation +I_UuidCreate +MesBufferHandleReset +MesDecodeBufferHandleCreate +MesDecodeIncrementalHandleCreate +MesEncodeDynBufferHandleCreate +MesEncodeFixedBufferHandleCreate +MesEncodeIncrementalHandleCreate +MesHandleFree +MesIncrementalHandleReset +MesInqProcEncodingId +NDRCContextBinding +NDRCContextMarshall +NDRCContextUnmarshall +NDRSContextMarshall +NDRSContextMarshall2 +NDRSContextMarshallEx +NDRSContextUnmarshall +NDRSContextUnmarshall2 +NDRSContextUnmarshallEx +Ndr64AsyncClientCall +Ndr64AsyncServerCall64 +Ndr64AsyncServerCallAll +Ndr64DcomAsyncClientCall +Ndr64DcomAsyncStubCall +NdrAllocate +NdrAsyncClientCall +NdrAsyncServerCall +NdrByteCountPointerBufferSize +NdrByteCountPointerFree +NdrByteCountPointerMarshall +NdrByteCountPointerUnmarshall +NdrCStdStubBuffer2_Release +NdrCStdStubBuffer_Release +NdrClearOutParameters +NdrClientCall2 +NdrClientCall3 +NdrClientContextMarshall +NdrClientContextUnmarshall +NdrClientInitialize +NdrClientInitializeNew +NdrComplexArrayBufferSize +NdrComplexArrayFree +NdrComplexArrayMarshall +NdrComplexArrayMemorySize +NdrComplexArrayUnmarshall +NdrComplexStructBufferSize +NdrComplexStructFree +NdrComplexStructMarshall +NdrComplexStructMemorySize +NdrComplexStructUnmarshall +NdrConformantArrayBufferSize +NdrConformantArrayFree +NdrConformantArrayMarshall +NdrConformantArrayMemorySize +NdrConformantArrayUnmarshall +NdrConformantStringBufferSize +NdrConformantStringMarshall +NdrConformantStringMemorySize +NdrConformantStringUnmarshall +NdrConformantStructBufferSize +NdrConformantStructFree +NdrConformantStructMarshall +NdrConformantStructMemorySize +NdrConformantStructUnmarshall +NdrConformantVaryingArrayBufferSize +NdrConformantVaryingArrayFree +NdrConformantVaryingArrayMarshall +NdrConformantVaryingArrayMemorySize +NdrConformantVaryingArrayUnmarshall +NdrConformantVaryingStructBufferSize +NdrConformantVaryingStructFree +NdrConformantVaryingStructMarshall +NdrConformantVaryingStructMemorySize +NdrConformantVaryingStructUnmarshall +NdrContextHandleInitialize +NdrContextHandleSize +NdrConvert +NdrConvert2 +NdrCorrelationFree +NdrCorrelationInitialize +NdrCorrelationPass +NdrCreateServerInterfaceFromStub +NdrDcomAsyncClientCall +NdrDcomAsyncStubCall +NdrDllCanUnloadNow +NdrDllGetClassObject +NdrDllRegisterProxy +NdrDllUnregisterProxy +NdrEncapsulatedUnionBufferSize +NdrEncapsulatedUnionFree +NdrEncapsulatedUnionMarshall +NdrEncapsulatedUnionMemorySize +NdrEncapsulatedUnionUnmarshall +NdrFixedArrayBufferSize +NdrFixedArrayFree +NdrFixedArrayMarshall +NdrFixedArrayMemorySize +NdrFixedArrayUnmarshall +NdrFreeBuffer +NdrFullPointerFree +NdrFullPointerInsertRefId +NdrFullPointerQueryPointer +NdrFullPointerQueryRefId +NdrFullPointerXlatFree +NdrFullPointerXlatInit +NdrGetBaseInterfaceFromStub +NdrGetBuffer +NdrGetDcomProtocolVersion +NdrGetSimpleTypeBufferAlignment +NdrGetSimpleTypeBufferSize +NdrGetSimpleTypeMemorySize +NdrGetTypeFlags +NdrGetUserMarshalInfo +NdrInterfacePointerBufferSize +NdrInterfacePointerFree +NdrInterfacePointerMarshall +NdrInterfacePointerMemorySize +NdrInterfacePointerUnmarshall +NdrMapCommAndFaultStatus +NdrMesProcEncodeDecode +NdrMesProcEncodeDecode2 +NdrMesProcEncodeDecode3 +NdrMesSimpleTypeAlignSize +NdrMesSimpleTypeAlignSizeAll +NdrMesSimpleTypeDecode +NdrMesSimpleTypeDecodeAll +NdrMesSimpleTypeEncode +NdrMesSimpleTypeEncodeAll +NdrMesTypeAlignSize +NdrMesTypeAlignSize2 +NdrMesTypeAlignSize3 +NdrMesTypeDecode +NdrMesTypeDecode2 +NdrMesTypeDecode3 +NdrMesTypeEncode +NdrMesTypeEncode2 +NdrMesTypeEncode3 +NdrMesTypeFree2 +NdrMesTypeFree3 +NdrNonConformantStringBufferSize +NdrNonConformantStringMarshall +NdrNonConformantStringMemorySize +NdrNonConformantStringUnmarshall +NdrNonEncapsulatedUnionBufferSize +NdrNonEncapsulatedUnionFree +NdrNonEncapsulatedUnionMarshall +NdrNonEncapsulatedUnionMemorySize +NdrNonEncapsulatedUnionUnmarshall +NdrNsGetBuffer +NdrNsSendReceive +NdrOleAllocate +NdrOleFree +NdrOutInit +NdrPartialIgnoreClientBufferSize +NdrPartialIgnoreClientMarshall +NdrPartialIgnoreServerInitialize +NdrPartialIgnoreServerUnmarshall +NdrPointerBufferSize +NdrPointerFree +NdrPointerMarshall +NdrPointerMemorySize +NdrPointerUnmarshall +NdrProxyErrorHandler +NdrProxyFreeBuffer +NdrProxyGetBuffer +NdrProxyInitialize +NdrProxySendReceive +NdrRangeUnmarshall +NdrRpcSmClientAllocate +NdrRpcSmClientFree +NdrRpcSmSetClientToOsf +NdrRpcSsDefaultAllocate +NdrRpcSsDefaultFree +NdrRpcSsDisableAllocate +NdrRpcSsEnableAllocate +NdrSendReceive +NdrServerCall2 +NdrServerCallAll +NdrServerCallNdr64 +NdrServerContextMarshall +NdrServerContextNewMarshall +NdrServerContextNewUnmarshall +NdrServerContextUnmarshall +NdrServerInitialize +NdrServerInitializeMarshall +NdrServerInitializeNew +NdrServerInitializePartial +NdrServerInitializeUnmarshall +NdrSimpleStructBufferSize +NdrSimpleStructFree +NdrSimpleStructMarshall +NdrSimpleStructMemorySize +NdrSimpleStructUnmarshall +NdrSimpleTypeMarshall +NdrSimpleTypeUnmarshall +NdrStubCall2 +NdrStubCall3 +NdrStubForwardingFunction +NdrStubGetBuffer +NdrStubInitialize +NdrStubInitializeMarshall +NdrTypeFlags DATA +NdrTypeFree +NdrTypeMarshall +NdrTypeSize +NdrTypeUnmarshall +NdrUnmarshallBasetypeInline +NdrUserMarshalBufferSize +NdrUserMarshalFree +NdrUserMarshalMarshall +NdrUserMarshalMemorySize +NdrUserMarshalSimpleTypeConvert +NdrUserMarshalUnmarshall +NdrVaryingArrayBufferSize +NdrVaryingArrayFree +NdrVaryingArrayMarshall +NdrVaryingArrayMemorySize +NdrVaryingArrayUnmarshall +NdrXmitOrRepAsBufferSize +NdrXmitOrRepAsFree +NdrXmitOrRepAsMarshall +NdrXmitOrRepAsMemorySize +NdrXmitOrRepAsUnmarshall +NdrpCreateProxy +NdrpCreateStub +NdrpGetProcFormatString +NdrpGetTypeFormatString +NdrpGetTypeGenCookie +NdrpMemoryIncrement +NdrpReleaseTypeFormatString +NdrpReleaseTypeGenCookie +NdrpSetRpcSsDefaults +NdrpVarVtOfTypeDesc +RpcAbortAsyncCall +RpcAsyncAbortCall +RpcAsyncCancelCall +RpcAsyncCompleteCall +RpcAsyncGetCallStatus +RpcAsyncInitializeHandle +RpcAsyncRegisterInfo +RpcBindingBind +RpcBindingCopy +RpcBindingCreateA +RpcBindingCreateW +RpcBindingFree +RpcBindingFromStringBindingA +RpcBindingFromStringBindingW +RpcBindingInqAuthClientA +RpcBindingInqAuthClientExA +RpcBindingInqAuthClientExW +RpcBindingInqAuthClientW +RpcBindingInqAuthInfoA +RpcBindingInqAuthInfoExA +RpcBindingInqAuthInfoExW +RpcBindingInqAuthInfoW +RpcBindingInqObject +RpcBindingInqOption +RpcBindingReset +RpcBindingServerFromClient +RpcBindingSetAuthInfoA +RpcBindingSetAuthInfoExA +RpcBindingSetAuthInfoExW +RpcBindingSetAuthInfoW +RpcBindingSetObject +RpcBindingSetOption +RpcBindingToStringBindingA +RpcBindingToStringBindingW +RpcBindingUnbind +RpcBindingVectorFree +RpcCancelAsyncCall +RpcCancelThread +RpcCancelThreadEx +RpcCertGeneratePrincipalNameA +RpcCertGeneratePrincipalNameW +RpcCompleteAsyncCall +RpcEpRegisterA +RpcEpRegisterNoReplaceA +RpcEpRegisterNoReplaceW +RpcEpRegisterW +RpcEpResolveBinding +RpcEpUnregister +RpcErrorAddRecord +RpcErrorClearInformation +RpcErrorEndEnumeration +RpcErrorGetNextRecord +RpcErrorGetNumberOfRecords +RpcErrorLoadErrorInfo +RpcErrorResetEnumeration +RpcErrorSaveErrorInfo +RpcErrorStartEnumeration +RpcExceptionFilter +RpcFreeAuthorizationContext +RpcGetAsyncCallStatus +RpcGetAuthorizationContextForClient +RpcIfIdVectorFree +RpcIfInqId +RpcImpersonateClient +RpcImpersonateClient2 +RpcInitializeAsyncHandle +RpcMgmtEnableIdleCleanup +RpcMgmtEpEltInqBegin +RpcMgmtEpEltInqDone +RpcMgmtEpEltInqNextA +RpcMgmtEpEltInqNextW +RpcMgmtEpUnregister +RpcMgmtInqComTimeout +RpcMgmtInqDefaultProtectLevel +RpcMgmtInqIfIds +RpcMgmtInqServerPrincNameA +RpcMgmtInqServerPrincNameW +RpcMgmtInqStats +RpcMgmtIsServerListening +RpcMgmtSetAuthorizationFn +RpcMgmtSetCancelTimeout +RpcMgmtSetComTimeout +RpcMgmtSetServerStackSize +RpcMgmtStatsVectorFree +RpcMgmtStopServerListening +RpcMgmtWaitServerListen +RpcNetworkInqProtseqsA +RpcNetworkInqProtseqsW +RpcNetworkIsProtseqValidA +RpcNetworkIsProtseqValidW +RpcNsBindingInqEntryNameA +RpcNsBindingInqEntryNameW +RpcObjectInqType +RpcObjectSetInqFn +RpcObjectSetType +RpcProtseqVectorFreeA +RpcProtseqVectorFreeW +RpcRaiseException +RpcRegisterAsyncInfo +RpcRevertToSelf +RpcRevertToSelfEx +RpcServerCompleteSecurityCallback +RpcServerInqBindingHandle +RpcServerInqBindings +RpcServerInqCallAttributesA +RpcServerInqCallAttributesW +RpcServerInqDefaultPrincNameA +RpcServerInqDefaultPrincNameW +RpcServerInqIf +RpcServerInterfaceGroupActivate +RpcServerInterfaceGroupClose +RpcServerInterfaceGroupCreateA +RpcServerInterfaceGroupCreateW +RpcServerInterfaceGroupDeactivate +RpcServerInterfaceGroupInqBindings +RpcServerListen +RpcServerRegisterAuthInfoA +RpcServerRegisterAuthInfoW +RpcServerRegisterIf +RpcServerRegisterIf2 +RpcServerRegisterIf3 +RpcServerRegisterIfEx +RpcServerSubscribeForNotification +RpcServerTestCancel +RpcServerUnregisterIf +RpcServerUnregisterIfEx +RpcServerUnsubscribeForNotification +RpcServerUseAllProtseqs +RpcServerUseAllProtseqsEx +RpcServerUseAllProtseqsIf +RpcServerUseAllProtseqsIfEx +RpcServerUseProtseqA +RpcServerUseProtseqEpA +RpcServerUseProtseqEpExA +RpcServerUseProtseqEpExW +RpcServerUseProtseqEpW +RpcServerUseProtseqExA +RpcServerUseProtseqExW +RpcServerUseProtseqIfA +RpcServerUseProtseqIfExA +RpcServerUseProtseqIfExW +RpcServerUseProtseqIfW +RpcServerUseProtseqW +RpcServerYield +RpcSmAllocate +RpcSmClientFree +RpcSmDestroyClientContext +RpcSmDisableAllocate +RpcSmEnableAllocate +RpcSmFree +RpcSmGetThreadHandle +RpcSmSetClientAllocFree +RpcSmSetThreadHandle +RpcSmSwapClientAllocFree +RpcSsAllocate +RpcSsContextLockExclusive +RpcSsContextLockShared +RpcSsDestroyClientContext +RpcSsDisableAllocate +RpcSsDontSerializeContext +RpcSsEnableAllocate +RpcSsFree +RpcSsGetContextBinding +RpcSsGetThreadHandle +RpcSsSetClientAllocFree +RpcSsSetThreadHandle +RpcSsSwapClientAllocFree +RpcStringBindingComposeA +RpcStringBindingComposeW +RpcStringBindingParseA +RpcStringBindingParseW +RpcStringFreeA +RpcStringFreeW +RpcTestCancel +RpcUserFree +SimpleTypeAlignment DATA +SimpleTypeBufferSize DATA +SimpleTypeMemorySize DATA +TowerConstruct +TowerExplode +UuidCompare +UuidCreate +UuidCreateNil +UuidCreateSequential +UuidEqual +UuidFromStringA +UuidFromStringW +UuidHash +UuidIsNil +UuidToStringA +UuidToStringW +pfnFreeRoutines DATA +pfnMarshallRoutines DATA +pfnSizeRoutines DATA +pfnUnmarshallRoutines DATA diff --git a/lib/libc/mingw/lib-common/scarddlg.def b/lib/libc/mingw/lib-common/scarddlg.def new file mode 100644 index 000000000..ef251d86d --- /dev/null +++ b/lib/libc/mingw/lib-common/scarddlg.def @@ -0,0 +1,15 @@ +; +; Exports of file SCARDDLG.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY SCARDDLG.dll +EXPORTS +GetOpenCardNameA +GetOpenCardNameW +SCardDlgExtendedError +SCardUIDlgSelectCardA +SCardUIDlgSelectCardW +SCardUIDlgGetPINA +SCardUIDlgChangePINA diff --git a/lib/libc/mingw/lib-common/winscard.def b/lib/libc/mingw/lib-common/winscard.def new file mode 100644 index 000000000..cdc4b4e80 --- /dev/null +++ b/lib/libc/mingw/lib-common/winscard.def @@ -0,0 +1,84 @@ +; +; Definition file of WinSCard.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "WinSCard.dll" +EXPORTS +ClassInstall32 +SCardAccessNewReaderEvent +SCardReleaseAllEvents +SCardReleaseNewReaderEvent +SCardAccessStartedEvent +SCardAddReaderToGroupA +SCardAddReaderToGroupW +SCardAudit +SCardBeginTransaction +SCardCancel +SCardConnectA +SCardConnectW +SCardControl +SCardDisconnect +SCardEndTransaction +SCardEstablishContext +SCardForgetCardTypeA +SCardForgetCardTypeW +SCardForgetReaderA +SCardForgetReaderGroupA +SCardForgetReaderGroupW +SCardForgetReaderW +SCardFreeMemory +SCardGetAttrib +SCardGetCardTypeProviderNameA +SCardGetCardTypeProviderNameW +SCardGetDeviceTypeIdA +SCardGetDeviceTypeIdW +SCardGetProviderIdA +SCardGetProviderIdW +SCardGetReaderDeviceInstanceIdA +SCardGetReaderDeviceInstanceIdW +SCardGetReaderIconA +SCardGetReaderIconW +SCardGetStatusChangeA +SCardGetStatusChangeW +SCardGetTransmitCount +SCardIntroduceCardTypeA +SCardIntroduceCardTypeW +SCardIntroduceReaderA +SCardIntroduceReaderGroupA +SCardIntroduceReaderGroupW +SCardIntroduceReaderW +SCardIsValidContext +SCardListCardsA +SCardListCardsW +SCardListInterfacesA +SCardListInterfacesW +SCardListReaderGroupsA +SCardListReaderGroupsW +SCardListReadersA +SCardListReadersW +SCardListReadersWithDeviceInstanceIdA +SCardListReadersWithDeviceInstanceIdW +SCardLocateCardsA +SCardLocateCardsByATRA +SCardLocateCardsByATRW +SCardLocateCardsW +SCardReadCacheA +SCardReadCacheW +SCardReconnect +SCardReleaseContext +SCardReleaseStartedEvent +SCardRemoveReaderFromGroupA +SCardRemoveReaderFromGroupW +SCardSetAttrib +SCardSetCardTypeProviderNameA +SCardSetCardTypeProviderNameW +SCardState +SCardStatusA +SCardStatusW +SCardTransmit +SCardWriteCacheA +SCardWriteCacheW +g_rgSCardRawPci DATA +g_rgSCardT0Pci DATA +g_rgSCardT1Pci DATA diff --git a/lib/libc/mingw/lib-common/winspool.def b/lib/libc/mingw/lib-common/winspool.def new file mode 100644 index 000000000..9509b52f8 --- /dev/null +++ b/lib/libc/mingw/lib-common/winspool.def @@ -0,0 +1,212 @@ +; +; Definition file of WINSPOOL.DRV +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "WINSPOOL.DRV" +EXPORTS +ADVANCEDSETUPDIALOG +AdvancedSetupDialog +ConvertAnsiDevModeToUnicodeDevmode +ConvertUnicodeDevModeToAnsiDevmode +DEVICEMODE +DeviceMode +DocumentEvent +PerfClose +PerfCollect +PerfOpen +QueryColorProfile +QueryRemoteFonts +QuerySpoolMode +SpoolerDevQueryPrintW +StartDocDlgW +AbortPrinter +AddFormA +AddFormW +AddJobA +AddJobW +AddMonitorA +AddMonitorW +AddPortA +AddPortExA +AddPortExW +AddPortW +AddPrintProcessorA +AddPrintProcessorW +AddPrintProvidorA +AddPrintProvidorW +AddPrinterA +AddPrinterConnection2A +AddPrinterConnection2W +AddPrinterConnectionA +AddPrinterConnectionW +AddPrinterDriverA +AddPrinterDriverExA +AddPrinterDriverExW +AddPrinterDriverW +AddPrinterW +AdvancedDocumentPropertiesA +AdvancedDocumentPropertiesW +ClosePrinter +CloseSpoolFileHandle +CommitSpoolData +ConfigurePortA +ConfigurePortW +ConnectToPrinterDlg +CorePrinterDriverInstalledA +CorePrinterDriverInstalledW +CreatePrintAsyncNotifyChannel +CreatePrinterIC +DEVICECAPABILITIES +DeleteFormA +DeleteFormW +DeleteJobNamedProperty +DeleteMonitorA +DeleteMonitorW +DeletePortA +DeletePortW +DeletePrintProcessorA +DeletePrintProcessorW +DeletePrintProvidorA +DeletePrintProvidorW +DeletePrinter +DeletePrinterConnectionA +DeletePrinterConnectionW +DeletePrinterDataA +DeletePrinterDataExA +DeletePrinterDataExW +DeletePrinterDataW +DeletePrinterDriverA +DeletePrinterDriverExA +DeletePrinterDriverExW +DeletePrinterDriverPackageA +DeletePrinterDriverPackageW +DeletePrinterDriverW +DeletePrinterIC +DeletePrinterKeyA +DeletePrinterKeyW +DevQueryPrint +DevQueryPrintEx +DeviceCapabilities +DeviceCapabilitiesA +DeviceCapabilitiesW +DevicePropertySheets +DocumentEvent +DocumentPropertiesA +DocumentPropertiesW +DocumentPropertySheets +EXTDEVICEMODE +EndDocPrinter +EndPagePrinter +EnumFormsA +EnumFormsW +EnumJobNamedProperties +EnumJobsA +EnumJobsW +EnumMonitorsA +EnumMonitorsW +EnumPortsA +GetDefaultPrinterA +SetDefaultPrinterA +GetDefaultPrinterW +SetDefaultPrinterW +EnumPortsW +EnumPrintProcessorDatatypesA +EnumPrintProcessorDatatypesW +EnumPrintProcessorsA +EnumPrintProcessorsW +EnumPrinterDataA +EnumPrinterDataExA +EnumPrinterDataExW +EnumPrinterDataW +EnumPrinterDriversA +EnumPrinterDriversW +EnumPrinterKeyA +EnumPrinterKeyW +EnumPrintersA +EnumPrintersW +ExtDeviceMode +FindClosePrinterChangeNotification +FindFirstPrinterChangeNotification +FindNextPrinterChangeNotification +FlushPrinter +FreePrintNamedPropertyArray +FreePrintPropertyValue +FreePrinterNotifyInfo +GetCorePrinterDriversA +GetCorePrinterDriversW +GetFormA +GetFormW +GetJobA +GetJobNamedPropertyValue +GetJobW +GetPrintExecutionData +GetPrintOutputInfo +GetPrintProcessorDirectoryA +GetPrintProcessorDirectoryW +GetPrinterA +GetPrinterDataA +GetPrinterDataExA +GetPrinterDataExW +GetPrinterDataW +GetPrinterDriver2A +GetPrinterDriver2W +GetPrinterDriverA +GetPrinterDriverDirectoryA +GetPrinterDriverDirectoryW +GetPrinterDriverPackagePathA +GetPrinterDriverPackagePathW +GetPrinterDriverW +GetPrinterW +GetSpoolFileHandle +InstallPrinterDriverFromPackageA +InstallPrinterDriverFromPackageW +IsValidDevmodeA +IsValidDevmodeW +OpenPrinter2A +OpenPrinter2W +OpenPrinterA +OpenPrinterW +PlayGdiScriptOnPrinterIC +PrinterMessageBoxA +PrinterMessageBoxW +PrinterProperties +QueryColorProfile +QueryRemoteFonts +QuerySpoolMode +ReadPrinter +RegisterForPrintAsyncNotifications +ReportJobProcessingProgress +ResetPrinterA +ResetPrinterW +ScheduleJob +SeekPrinter +SetAllocFailCount +SetFormA +SetFormW +SetJobA +SetJobNamedProperty +SetJobW +SetPortA +SetPortW +SetPrinterA +SetPrinterDataA +SetPrinterDataExA +SetPrinterDataExW +SetPrinterDataW +SetPrinterW +SplDriverUnloadComplete +SpoolerDevQueryPrintW +SpoolerInit +SpoolerPrinterEvent +StartDocDlgA +StartDocDlgW +StartDocPrinterA +StartDocPrinterW +StartPagePrinter +UnRegisterForPrintAsyncNotifications +UploadPrinterDriverPackageA +UploadPrinterDriverPackageW +WaitForPrinterChange +WritePrinter +XcvDataW diff --git a/lib/libc/mingw/lib-common/ws2_32.def.in b/lib/libc/mingw/lib-common/ws2_32.def.in new file mode 100644 index 000000000..4789b77d8 --- /dev/null +++ b/lib/libc/mingw/lib-common/ws2_32.def.in @@ -0,0 +1,200 @@ +#include "func.def.in" + +LIBRARY "WS2_32.dll" +EXPORTS +accept +bind +closesocket +connect +getpeername +getsockname +getsockopt +htonl +htons +ioctlsocket +inet_addr +inet_ntoa +listen +ntohl +ntohs +recv +recvfrom +select +send +sendto +setsockopt +shutdown +socket +WSApSetPostRoutine +FreeAddrInfoEx +FreeAddrInfoExW +FreeAddrInfoW +GetAddrInfoExA +GetAddrInfoExCancel +GetAddrInfoExOverlappedResult +GetAddrInfoExW +GetAddrInfoW +GetHostNameW +GetNameInfoW +InetNtopW +InetPtonW +SetAddrInfoExA +SetAddrInfoExW +WPUCompleteOverlappedRequest +WPUGetProviderPathEx +WSAAccept +WSAAddressToStringA +WSAAddressToStringW +WSAAdvertiseProvider +WSACloseEvent +WSAConnect +WSAConnectByList +WSAConnectByNameA +WSAConnectByNameW +WSACreateEvent +gethostbyaddr +gethostbyname +getprotobyname +getprotobynumber +getservbyname +getservbyport +gethostname +WSADuplicateSocketA +WSADuplicateSocketW +WSAEnumNameSpaceProvidersA +WSAEnumNameSpaceProvidersExA +WSAEnumNameSpaceProvidersExW +WSAEnumNameSpaceProvidersW +WSAEnumNetworkEvents +WSAEnumProtocolsA +WSAEnumProtocolsW +WSAEventSelect +WSAGetOverlappedResult +WSAGetQOSByName +WSAGetServiceClassInfoA +WSAGetServiceClassInfoW +WSAGetServiceClassNameByClassIdA +WSAGetServiceClassNameByClassIdW +WSAHtonl +WSAHtons +WSAInstallServiceClassA +WSAInstallServiceClassW +WSAIoctl +WSAJoinLeaf +WSALookupServiceBeginA +WSALookupServiceBeginW +WSALookupServiceEnd +WSALookupServiceNextA +WSALookupServiceNextW +WSANSPIoctl +WSANtohl +WSANtohs +WSAPoll +WSAProviderCompleteAsyncCall +WSAProviderConfigChange +WSARecv +WSARecvDisconnect +WSARecvFrom +WSARemoveServiceClass +WSAResetEvent +WSASend +WSASendDisconnect +WSASendMsg +WSASendTo +WSASetEvent +WSAAsyncSelect +WSAAsyncGetHostByAddr +WSAAsyncGetHostByName +WSAAsyncGetProtoByNumber +WSAAsyncGetProtoByName +WSAAsyncGetServByPort +WSAAsyncGetServByName +WSACancelAsyncRequest +WSASetBlockingHook +WSAUnhookBlockingHook +WSAGetLastError +WSASetLastError +WSACancelBlockingCall +WSAIsBlocking +WSAStartup +WSACleanup +WSASetServiceA +WSASetServiceW +WSASocketA +WSASocketW +WSAStringToAddressA +WSAStringToAddressW +WSAUnadvertiseProvider +WSAWaitForMultipleEvents +WSCDeinstallProvider +F64(WSCDeinstallProvider32) +WSCDeinstallProviderEx +WSCEnableNSProvider +F64(WSCEnableNSProvider32) +F64(WSCEnumNameSpaceProviders32) +F64(WSCEnumNameSpaceProvidersEx32) +WSCEnumProtocols +WSCEnumProtocolsEx +F64(WSCEnumProtocols32) +WSCGetApplicationCategory +WSCGetApplicationCategoryEx +WSCGetProviderInfo +F64(WSCGetProviderInfo32) +WSCGetProviderPath +F64(WSCGetProviderPath32) +WSCInstallNameSpace +F64(WSCInstallNameSpace32) +WSCInstallNameSpaceEx +WSCInstallNameSpaceEx2 +F64(WSCInstallNameSpaceEx32) +WSCInstallProvider +F64(WSCInstallProvider64_32) +WSCInstallProviderAndChains +F64(WSCInstallProviderAndChains64_32) +WSCInstallProviderEx +WSCSetApplicationCategory +WSCSetApplicationCategoryEx +WSCSetProviderInfo +F64(WSCSetProviderInfo32) +WSCUnInstallNameSpace +F64(WSCUnInstallNameSpace32) +WSCUnInstallNameSpaceEx2 +WSCUpdateProvider +F64(WSCUpdateProvider32) +WSCUpdateProviderEx +WSCWriteNameSpaceOrder +F64(WSCWriteNameSpaceOrder32) +WSCWriteProviderOrder +F64(WSCWriteProviderOrder32) +WSCWriteProviderOrderEx +WahCloseApcHelper +__WSAFDIsSet +WahCloseHandleHelper +WahCloseNotificationHandleHelper +WahCloseSocketHandle +WahCloseThread +WahCompleteRequest +WahCreateHandleContextTable +WahCreateNotificationHandle +WahCreateSocketHandle +WahDestroyHandleContextTable +WahDisableNonIFSHandleSupport +WahEnableNonIFSHandleSupport +WahEnumerateHandleContexts +WahInsertHandleContext +WahNotifyAllProcesses +WahOpenApcHelper +WahOpenCurrentThread +WahOpenHandleHelper +WahOpenNotificationHandleHelper +WahQueueUserApc +WahReferenceContextByHandle +WahRemoveHandleContext +WahWaitForNotification +WahWriteLSPEvent +freeaddrinfo +getaddrinfo +getnameinfo +inet_ntop +inet_pton +WEP diff --git a/lib/libc/mingw/lib32/advapi32.def b/lib/libc/mingw/lib32/advapi32.def new file mode 100644 index 000000000..91076556d --- /dev/null +++ b/lib/libc/mingw/lib32/advapi32.def @@ -0,0 +1,823 @@ +; +; Definition file of ADVAPI32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "ADVAPI32.dll" +EXPORTS +ord_1000@8 @1000 +I_ScGetCurrentGroupStateW@12 +A_SHAFinal@8 +A_SHAInit@4 +A_SHAUpdate@12 +AbortSystemShutdownA@4 +AbortSystemShutdownW@4 +AccessCheck@32 +AccessCheckAndAuditAlarmA@44 +AccessCheckAndAuditAlarmW@44 +AccessCheckByType@44 +AccessCheckByTypeAndAuditAlarmA@64 +AccessCheckByTypeAndAuditAlarmW@64 +AccessCheckByTypeResultList@44 +AccessCheckByTypeResultListAndAuditAlarmA@64 +AccessCheckByTypeResultListAndAuditAlarmByHandleA@68 +AccessCheckByTypeResultListAndAuditAlarmByHandleW@68 +AccessCheckByTypeResultListAndAuditAlarmW@64 +AddAccessAllowedAce@16 +AddAccessAllowedAceEx@20 +AddAccessAllowedObjectAce@28 +AddAccessDeniedAce@16 +AddAccessDeniedAceEx@20 +AddAccessDeniedObjectAce@28 +AddAce@20 +AddAuditAccessAce@24 +AddAuditAccessAceEx@28 +AddAuditAccessObjectAce@36 +AddConditionalAce@32 +AddMandatoryAce@20 +AddUsersToEncryptedFile@8 +AddUsersToEncryptedFileEx@16 +AdjustTokenGroups@24 +AdjustTokenPrivileges@24 +AllocateAndInitializeSid@44 +AllocateLocallyUniqueId@4 +AreAllAccessesGranted@8 +AreAnyAccessesGranted@8 +AuditComputeEffectivePolicyBySid@16 +AuditComputeEffectivePolicyByToken@16 +AuditEnumerateCategories@8 +AuditEnumeratePerUserPolicy@4 +AuditEnumerateSubCategories@16 +AuditFree@4 +AuditLookupCategoryGuidFromCategoryId@8 +AuditLookupCategoryIdFromCategoryGuid@8 +AuditLookupCategoryNameA@8 +AuditLookupCategoryNameW@8 +AuditLookupSubCategoryNameA@8 +AuditLookupSubCategoryNameW@8 +AuditQueryGlobalSaclA@8 +AuditQueryGlobalSaclW@8 +AuditQueryPerUserPolicy@16 +AuditQuerySecurity@8 +AuditQuerySystemPolicy@12 +AuditSetGlobalSaclA@8 +AuditSetGlobalSaclW@8 +AuditSetPerUserPolicy@12 +AuditSetSecurity@8 +AuditSetSystemPolicy@8 +BackupEventLogA@8 +BackupEventLogW@8 +BuildExplicitAccessWithNameA@20 +BuildExplicitAccessWithNameW@20 +BuildImpersonateExplicitAccessWithNameA@24 +BuildImpersonateExplicitAccessWithNameW@24 +BuildImpersonateTrusteeA@8 +BuildImpersonateTrusteeW@8 +BuildSecurityDescriptorA@36 +BuildSecurityDescriptorW@36 +BuildTrusteeWithNameA@8 +BuildTrusteeWithNameW@8 +BuildTrusteeWithObjectsAndNameA@24 +BuildTrusteeWithObjectsAndNameW@24 +BuildTrusteeWithObjectsAndSidA@20 +BuildTrusteeWithObjectsAndSidW@20 +BuildTrusteeWithSidA@8 +BuildTrusteeWithSidW@8 +CancelOverlappedAccess@4 +ChangeServiceConfig2A@12 +ChangeServiceConfig2W@12 +ChangeServiceConfigA@44 +ChangeServiceConfigW@44 +CheckTokenMembership@12 +ClearEventLogA@8 +ClearEventLogW@8 +CloseCodeAuthzLevel@4 +CloseEncryptedFileRaw@4 +CloseEventLog@4 +CloseServiceHandle@4 +CloseThreadWaitChainSession@4 +CloseTrace@8 +CommandLineFromMsiDescriptor@12 +ComputeAccessTokenFromCodeAuthzLevel@20 +ControlService@12 +ControlServiceExA@16 +ControlServiceExW@16 +ControlTraceA@20 +ControlTraceW@20 +ConvertAccessToSecurityDescriptorA@20 +ConvertAccessToSecurityDescriptorW@20 +ConvertSDToStringSDRootDomainA@24 +ConvertSDToStringSDRootDomainW@24 +ConvertSecurityDescriptorToAccessA@28 +ConvertSecurityDescriptorToAccessNamedA@28 +ConvertSecurityDescriptorToAccessNamedW@28 +ConvertSecurityDescriptorToAccessW@28 +ConvertSecurityDescriptorToStringSecurityDescriptorA@20 +ConvertSecurityDescriptorToStringSecurityDescriptorW@20 +ConvertSidToStringSidA@8 +ConvertSidToStringSidW@8 +ConvertStringSDToSDDomainA@24 +ConvertStringSDToSDDomainW@24 +ConvertStringSDToSDRootDomainA@20 +ConvertStringSDToSDRootDomainW@20 +ConvertStringSecurityDescriptorToSecurityDescriptorA@16 +ConvertStringSecurityDescriptorToSecurityDescriptorW@16 +ConvertStringSidToSidA@8 +ConvertStringSidToSidW@8 +ConvertToAutoInheritPrivateObjectSecurity@24 +CopySid@12 +CreateCodeAuthzLevel@20 +CreatePrivateObjectSecurity@24 +CreatePrivateObjectSecurityEx@32 +CreatePrivateObjectSecurityWithMultipleInheritance@36 +CreateProcessAsUserA@44 +CreateProcessAsUserW@44 +CreateProcessWithLogonW@44 +CreateProcessWithTokenW@36 +CreateRestrictedToken@36 +CreateServiceA@52 +CreateServiceW@52 +CreateTraceInstanceId@8 +CreateWellKnownSid@16 +CredBackupCredentials@20 +CredDeleteA@12 +CredDeleteW@12 +CredEncryptAndMarshalBinaryBlob@12 +CredEnumerateA@16 +CredEnumerateW@16 +CredFindBestCredentialA@16 +CredFindBestCredentialW@16 +CredFree@4 +CredGetSessionTypes@8 +CredGetTargetInfoA@12 +CredGetTargetInfoW@12 +CredIsMarshaledCredentialA@4 +CredIsMarshaledCredentialW@4 +CredIsProtectedA@8 +CredIsProtectedW@8 +CredMarshalCredentialA@12 +CredMarshalCredentialW@12 +CredProfileLoaded@0 +CredProfileUnloaded@0 +CredProtectA@24 +CredProtectW@24 +CredReadA@16 +CredReadByTokenHandle@20 +CredReadDomainCredentialsA@16 +CredReadDomainCredentialsW@16 +CredReadW@16 +CredRenameA@16 +CredRenameW@16 +CredRestoreCredentials@16 +CredUnmarshalCredentialA@12 +CredUnmarshalCredentialW@12 +CredUnprotectA@20 +CredUnprotectW@20 +CredWriteA@8 +CredWriteDomainCredentialsA@12 +CredWriteDomainCredentialsW@12 +CredWriteW@8 +CredpConvertCredential@16 +CredpConvertOneCredentialSize@8 +CredpConvertTargetInfo@16 +CredpDecodeCredential@4 +CredpEncodeCredential@4 +CredpEncodeSecret@20 +CryptAcquireContextA@20 +CryptAcquireContextW@20 +CryptContextAddRef@12 +CryptCreateHash@20 +CryptDecrypt@24 +CryptDeriveKey@20 +CryptDestroyHash@4 +CryptDestroyKey@4 +CryptDuplicateHash@16 +CryptDuplicateKey@16 +CryptEncrypt@28 +CryptEnumProviderTypesA@24 +CryptEnumProviderTypesW@24 +CryptEnumProvidersA@24 +CryptEnumProvidersW@24 +CryptExportKey@24 +CryptGenKey@16 +CryptGenRandom@12 +CryptGetDefaultProviderA@20 +CryptGetDefaultProviderW@20 +CryptGetHashParam@20 +CryptGetKeyParam@20 +CryptGetProvParam@20 +CryptGetUserKey@12 +CryptHashData@16 +CryptHashSessionKey@12 +CryptImportKey@24 +CryptReleaseContext@8 +CryptSetHashParam@16 +CryptSetKeyParam@16 +CryptSetProvParam@16 +CryptSetProviderA@8 +CryptSetProviderExA@16 +CryptSetProviderExW@16 +CryptSetProviderW@8 +CryptSignHashA@24 +CryptSignHashW@24 +CryptVerifySignatureA@24 +CryptVerifySignatureW@24 +DecryptFileA@8 +DecryptFileW@8 +DeleteAce@8 +DeleteService@4 +DeregisterEventSource@4 +DestroyPrivateObjectSecurity@4 +DuplicateEncryptionInfoFile@20 +DuplicateToken@12 +DuplicateTokenEx@24 +ElfBackupEventLogFileA@8 +ElfBackupEventLogFileW@8 +ElfChangeNotify@8 +ElfClearEventLogFileA@8 +ElfClearEventLogFileW@8 +ElfCloseEventLog@4 +ElfDeregisterEventSource@4 +ElfFlushEventLog@4 +ElfNumberOfRecords@8 +ElfOldestRecord@8 +ElfOpenBackupEventLogA@12 +ElfOpenBackupEventLogW@12 +ElfOpenEventLogA@12 +ElfOpenEventLogW@12 +ElfReadEventLogA@28 +ElfReadEventLogW@28 +ElfRegisterEventSourceA@12 +ElfRegisterEventSourceW@12 +ElfReportEventA@48 +ElfReportEventAndSourceW@60 +ElfReportEventW@48 +EnableTrace@24 +EnableTraceEx2@44 +EnableTraceEx@48 +EncryptFileA@4 +EncryptFileW@4 +EncryptedFileKeyInfo@12 +EncryptionDisable@8 +EnumDependentServicesA@24 +EnumDependentServicesW@24 +EnumServiceGroupW@36 +EnumServicesStatusA@32 +EnumServicesStatusExA@40 +EnumServicesStatusExW@40 +EnumServicesStatusW@32 +EnumerateTraceGuids@12 +EnumerateTraceGuidsEx@24 +EqualDomainSid@12 +EqualPrefixSid@8 +EqualSid@8 +EventAccessControl@20 +EventAccessQuery@12 +EventAccessRemove@4 +EventActivityIdControl@8 +EventEnabled@12 +EventProviderEnabled@20 +EventRegister@16 +EventUnregister@8 +EventWrite@20 +EventWriteEndScenario@20 +EventWriteEx@40 +EventWriteStartScenario@20 +EventWriteString@24 +EventWriteTransfer@28 +FileEncryptionStatusA@8 +FileEncryptionStatusW@8 +FindFirstFreeAce@8 +FlushEfsCache@4 +FlushTraceA@16 +FlushTraceW@16 +FreeEncryptedFileKeyInfo@4 +FreeEncryptedFileMetadata@4 +FreeEncryptionCertificateHashList@4 +FreeInheritedFromArray@12 +FreeSid@4 +GetAccessPermissionsForObjectA@36 +GetAccessPermissionsForObjectW@36 +GetAce@12 +GetAclInformation@16 +GetAuditedPermissionsFromAclA@16 +GetAuditedPermissionsFromAclW@16 +GetCurrentHwProfileA@4 +GetCurrentHwProfileW@4 +GetEffectiveRightsFromAclA@12 +GetEffectiveRightsFromAclW@12 +GetEncryptedFileMetadata@12 +GetEventLogInformation@20 +GetExplicitEntriesFromAclA@12 +GetExplicitEntriesFromAclW@12 +GetFileSecurityA@20 +GetFileSecurityW@20 +GetInformationCodeAuthzLevelW@20 +GetInformationCodeAuthzPolicyW@24 +GetInheritanceSourceA@40 +GetInheritanceSourceW@40 +GetKernelObjectSecurity@20 +GetLengthSid@4 +GetLocalManagedApplicationData@12 +GetLocalManagedApplications@12 +GetManagedApplicationCategories@8 +GetManagedApplications@20 +GetMangledSiteSid@12 +GetMultipleTrusteeA@4 +GetMultipleTrusteeOperationA@4 +GetMultipleTrusteeOperationW@4 +GetMultipleTrusteeW@4 +GetNamedSecurityInfoA@32 +GetNamedSecurityInfoExA@36 +GetNamedSecurityInfoExW@36 +GetNamedSecurityInfoW@32 +GetNumberOfEventLogRecords@8 +GetOldestEventLogRecord@8 +GetOverlappedAccessResults@16 +GetPrivateObjectSecurity@20 +GetSecurityDescriptorControl@12 +GetSecurityDescriptorDacl@16 +GetSecurityDescriptorGroup@12 +GetSecurityDescriptorLength@4 +GetSecurityDescriptorOwner@12 +GetSecurityDescriptorRMControl@8 +GetSecurityDescriptorSacl@16 +GetSecurityInfo@32 +GetSecurityInfoExA@36 +GetSecurityInfoExW@36 +GetServiceDisplayNameA@16 +GetServiceDisplayNameW@16 +GetServiceKeyNameA@16 +GetServiceKeyNameW@16 +GetSidIdentifierAuthority@4 +GetSidLengthRequired@4 +GetSidSubAuthority@8 +GetSidSubAuthorityCount@4 +GetSiteDirectoryA@12 +GetSiteDirectoryW@12 +GetSiteNameFromSid@8 +GetSiteSidFromToken@4 +GetSiteSidFromUrl@4 +GetThreadWaitChain@28 +GetTokenInformation@20 +GetTraceEnableFlags@8 +GetTraceEnableLevel@8 +GetTraceLoggerHandle@4 +GetTrusteeFormA@4 +GetTrusteeFormW@4 +GetTrusteeNameA@4 +GetTrusteeNameW@4 +GetTrusteeTypeA@4 +GetTrusteeTypeW@4 +GetUserNameA@8 +GetUserNameW@8 +GetWindowsAccountDomainSid@12 +I_QueryTagInformation@12 +I_ScIsSecurityProcess@0 +I_ScPnPGetServiceName@12 +I_ScQueryServiceConfig@12 +I_ScSendPnPMessage@24 +I_ScSendTSMessage@16 +I_ScSetServiceBitsA@20 +I_ScSetServiceBitsW@20 +I_ScValidatePnPService@12 +IdentifyCodeAuthzLevelW@16 +ImpersonateAnonymousToken@4 +ImpersonateLoggedOnUser@4 +ImpersonateNamedPipeClient@4 +ImpersonateSelf@4 +InitializeAcl@12 +InitializeSecurityDescriptor@8 +InitializeSid@12 +InitiateShutdownA@20 +InitiateShutdownW@20 +InitiateSystemShutdownA@20 +InitiateSystemShutdownExA@24 +InitiateSystemShutdownExW@24 +InitiateSystemShutdownW@20 +InstallApplication@4 +IsProcessRestricted@0 +IsTextUnicode@12 +IsTokenRestricted@4 +IsTokenUntrusted@4 +IsValidAcl@4 +IsValidRelativeSecurityDescriptor@12 +IsValidSecurityDescriptor@4 +IsValidSid@4 +IsWellKnownSid@8 +LockServiceDatabase@4 +LogonUserA@24 +LogonUserExA@40 +LogonUserExExW@44 +LogonUserExW@40 +LogonUserW@24 +LookupAccountNameA@28 +LookupAccountNameW@28 +LookupAccountSidA@28 +LookupAccountSidW@28 +LookupPrivilegeDisplayNameA@20 +LookupPrivilegeDisplayNameW@20 +LookupPrivilegeNameA@16 +LookupPrivilegeNameW@16 +LookupPrivilegeValueA@12 +LookupPrivilegeValueW@12 +LookupSecurityDescriptorPartsA@28 +LookupSecurityDescriptorPartsW@28 +LsaAddAccountRights@16 +LsaAddPrivilegesToAccount@8 +LsaClearAuditLog@4 +LsaClose@4 +LsaCreateAccount@16 +LsaCreateSecret@16 +LsaCreateTrustedDomain@16 +LsaCreateTrustedDomainEx@20 +LsaDelete@4 +LsaDeleteTrustedDomain@8 +LsaEnumerateAccountRights@16 +LsaEnumerateAccounts@20 +LsaEnumerateAccountsWithUserRight@16 +LsaEnumeratePrivileges@20 +LsaEnumeratePrivilegesOfAccount@8 +LsaEnumerateTrustedDomains@20 +LsaEnumerateTrustedDomainsEx@20 +LsaFreeMemory@4 +LsaGetQuotasForAccount@8 +LsaGetRemoteUserName@12 +LsaGetSystemAccessAccount@8 +LsaGetUserName@8 +LsaICLookupNames@40 +LsaICLookupNamesWithCreds@48 +LsaICLookupSids@36 +LsaICLookupSidsWithCreds@48 +LsaLookupNames2@24 +LsaLookupNames@20 +LsaLookupPrivilegeDisplayName@16 +LsaLookupPrivilegeName@12 +LsaLookupPrivilegeValue@12 +LsaLookupSids@20 +LsaManageSidNameMapping@12 +LsaNtStatusToWinError@4 +LsaOpenAccount@16 +LsaOpenPolicy@16 +LsaOpenPolicySce@16 +LsaOpenSecret@16 +LsaOpenTrustedDomain@16 +LsaOpenTrustedDomainByName@16 +LsaQueryDomainInformationPolicy@12 +LsaQueryForestTrustInformation@12 +LsaQueryInfoTrustedDomain@12 +LsaQueryInformationPolicy@12 +LsaQuerySecret@20 +LsaQuerySecurityObject@12 +LsaQueryTrustedDomainInfo@16 +LsaQueryTrustedDomainInfoByName@16 +LsaRemoveAccountRights@20 +LsaRemovePrivilegesFromAccount@12 +LsaRetrievePrivateData@12 +LsaSetDomainInformationPolicy@12 +LsaSetForestTrustInformation@20 +LsaSetInformationPolicy@12 +LsaSetInformationTrustedDomain@12 +LsaSetQuotasForAccount@8 +LsaSetSecret@12 +LsaSetSecurityObject@12 +LsaSetSystemAccessAccount@8 +LsaSetTrustedDomainInfoByName@16 +LsaSetTrustedDomainInformation@16 +LsaStorePrivateData@12 +MD4Final@4 +MD4Init@4 +MD4Update@12 +MD5Final@4 +MD5Init@4 +MD5Update@12 +MSChapSrvChangePassword2@28 +MSChapSrvChangePassword@28 +MakeAbsoluteSD2@8 +MakeAbsoluteSD@44 +MakeSelfRelativeSD@12 +MapGenericMask@8 +NotifyBootConfigStatus@4 +NotifyChangeEventLog@8 +NotifyServiceStatusChange@12 +NotifyServiceStatusChangeA@12 +NotifyServiceStatusChangeW@12 +ObjectCloseAuditAlarmA@12 +ObjectCloseAuditAlarmW@12 +ObjectDeleteAuditAlarmA@12 +ObjectDeleteAuditAlarmW@12 +ObjectOpenAuditAlarmA@48 +ObjectOpenAuditAlarmW@48 +ObjectPrivilegeAuditAlarmA@24 +ObjectPrivilegeAuditAlarmW@24 +OpenBackupEventLogA@8 +OpenBackupEventLogW@8 +OpenEncryptedFileRawA@12 +OpenEncryptedFileRawW@12 +OpenEventLogA@8 +OpenEventLogW@8 +OpenProcessToken@12 +OpenSCManagerA@12 +OpenSCManagerW@12 +OpenServiceA@12 +OpenServiceW@12 +OpenThreadToken@16 +OpenThreadWaitChainSession@8 +OpenTraceA@4 +OpenTraceW@4 +PerfAddCounters@12 +PerfCloseQueryHandle@4 +PerfCreateInstance@16 +PerfDecrementULongCounterValue@16 +PerfDecrementULongLongCounterValue@20 +PerfDeleteCounters@12 +PerfDeleteInstance@8 +PerfEnumerateCounterSet@16 +PerfEnumerateCounterSetInstances@20 +PerfIncrementULongCounterValue@16 +PerfIncrementULongLongCounterValue@20 +PerfOpenQueryHandle@8 +PerfQueryCounterData@16 +PerfQueryCounterInfo@16 +PerfQueryCounterSetRegistrationInfo@28 +PerfQueryInstance@16 +PerfSetCounterRefValue@16 +PerfSetCounterSetInfo@12 +PerfSetULongCounterValue@16 +PerfSetULongLongCounterValue@20 +PerfStartProvider@12 +PerfStartProviderEx@12 +PerfStopProvider@4 +PrivilegeCheck@12 +PrivilegedServiceAuditAlarmA@20 +PrivilegedServiceAuditAlarmW@20 +ProcessIdleTasks@0 +ProcessIdleTasksW@16 +ProcessTrace@16 +QueryAllTracesA@12 +QueryAllTracesW@12 +QueryRecoveryAgentsOnEncryptedFile@8 +QuerySecurityAccessMask@8 +QueryServiceConfig2A@20 +QueryServiceConfig2W@20 +QueryServiceConfigA@16 +QueryServiceConfigW@16 +QueryServiceLockStatusA@16 +QueryServiceLockStatusW@16 +QueryServiceObjectSecurity@20 +QueryServiceStatus@8 +QueryServiceStatusEx@20 +QueryTraceA@16 +QueryTraceW@16 +QueryUsersOnEncryptedFile@8 +QueryWindows31FilesMigration@4 +ReadEncryptedFileRaw@12 +ReadEventLogA@28 +ReadEventLogW@28 +RegCloseKey@4 +RegConnectRegistryA@12 +RegConnectRegistryExA@16 +RegConnectRegistryExW@16 +RegConnectRegistryW@12 +RegCopyTreeA@12 +RegCopyTreeW@12 +RegCreateKeyA@12 +RegCreateKeyExA@36 +RegCreateKeyExW@36 +RegCreateKeyTransactedA@44 +RegCreateKeyTransactedW@44 +RegCreateKeyW@12 +RegDeleteKeyA@8 +RegDeleteKeyW@8 +RegDeleteKeyExA@16 +RegDeleteKeyExW@16 +RegDeleteKeyTransactedA@24 +RegDeleteKeyTransactedW@24 +RegDeleteKeyValueA@12 +RegDeleteKeyValueW@12 +RegDeleteKeyW@8 +RegDeleteTreeA@8 +RegDeleteTreeW@8 +RegDeleteValueA@8 +RegDeleteValueW@8 +RegDisablePredefinedCache@0 +RegDisablePredefinedCacheEx@0 +RegDisableReflectionKey@4 +RegEnableReflectionKey@4 +RegEnumKeyA@16 +RegEnumKeyExA@32 +RegEnumKeyExW@32 +RegEnumKeyW@16 +RegEnumValueA@32 +RegEnumValueW@32 +RegFlushKey@4 +RegGetKeySecurity@16 +RegGetValueA@28 +RegGetValueW@28 +RegLoadAppKeyA@20 +RegLoadAppKeyW@20 +RegLoadKeyA@12 +RegLoadKeyW@12 +RegLoadMUIStringA@28 +RegLoadMUIStringW@28 +RegNotifyChangeKeyValue@20 +RegOpenCurrentUser@8 +RegOpenKeyA@12 +RegOpenKeyExA@20 +RegOpenKeyExW@20 +RegOpenKeyTransactedA@28 +RegOpenKeyTransactedW@28 +RegOpenKeyW@12 +RegOpenUserClassesRoot@16 +RegOverridePredefKey@8 +RegQueryInfoKeyA@48 +RegQueryInfoKeyW@48 +RegQueryMultipleValuesA@20 +RegQueryMultipleValuesW@20 +RegQueryReflectionKey@8 +RegQueryValueA@16 +RegQueryValueExA@24 +RegQueryValueExW@24 +RegQueryValueW@16 +RegRenameKey@12 +RegReplaceKeyA@16 +RegReplaceKeyW@16 +RegRestoreKeyA@12 +RegRestoreKeyW@12 +RegSaveKeyA@12 +RegSaveKeyExA@16 +RegSaveKeyExW@16 +RegSaveKeyW@12 +RegSetKeySecurity@12 +RegSetKeyValueA@24 +RegSetKeyValueW@24 +RegSetValueA@20 +RegSetValueExA@24 +RegSetValueExW@24 +RegSetValueW@20 +RegUnLoadKeyA@8 +RegUnLoadKeyW@8 +RegisterEventSourceA@8 +RegisterEventSourceW@8 +RegisterIdleTask@16 +RegisterServiceCtrlHandlerA@8 +RegisterServiceCtrlHandlerExA@12 +RegisterServiceCtrlHandlerExW@12 +RegisterServiceCtrlHandlerW@8 +RegisterTraceGuidsA@32 +RegisterTraceGuidsW@32 +RegisterWaitChainCOMCallback@8 +RemoveTraceCallback@4 +RemoveUsersFromEncryptedFile@8 +ReportEventA@36 +ReportEventW@36 +RevertToSelf@0 +SaferCloseLevel@4 +SaferComputeTokenFromLevel@20 +SaferCreateLevel@20 +SaferGetLevelInformation@20 +SaferGetPolicyInformation@24 +SaferIdentifyLevel@16 +SaferRecordEventLogEntry@12 +SaferSetLevelInformation@16 +SaferSetPolicyInformation@20 +SaferiChangeRegistryScope@8 +SaferiCompareTokenLevels@12 +SaferiIsDllAllowed@12 +SaferiIsExecutableFileType@8 +SaferiPopulateDefaultsInRegistry@8 +SaferiRecordEventLogEntry@12 +SaferiSearchMatchingHashRules@24 +SetAclInformation@16 +SetEncryptedFileMetadata@24 +SetEntriesInAccessListA@24 +SetEntriesInAccessListW@24 +SetEntriesInAclA@16 +SetEntriesInAclW@16 +SetEntriesInAuditListA@24 +SetEntriesInAuditListW@24 +SetFileSecurityA@12 +SetFileSecurityW@12 +SetInformationCodeAuthzLevelW@16 +SetInformationCodeAuthzPolicyW@20 +SetKernelObjectSecurity@12 +SetNamedSecurityInfoA@28 +SetNamedSecurityInfoExA@36 +SetNamedSecurityInfoExW@36 +SetNamedSecurityInfoW@28 +SetPrivateObjectSecurity@20 +SetPrivateObjectSecurityEx@24 +SetSecurityAccessMask@8 +SetSecurityDescriptorControl@12 +SetSecurityDescriptorDacl@16 +SetSecurityDescriptorGroup@12 +SetSecurityDescriptorOwner@12 +SetSecurityDescriptorRMControl@8 +SetSecurityDescriptorSacl@16 +SetSecurityInfo@28 +SetSecurityInfoExA@36 +SetSecurityInfoExW@36 +SetServiceBits@16 +SetServiceObjectSecurity@12 +SetServiceStatus@8 +SetThreadToken@8 +SetTokenInformation@16 +SetTraceCallback@8 +SetUserFileEncryptionKey@4 +SetUserFileEncryptionKeyEx@16 +StartServiceA@12 +StartServiceCtrlDispatcherA@4 +StartServiceCtrlDispatcherW@4 +StartServiceW@12 +StartTraceA@12 +StartTraceW@12 +SynchronizeWindows31FilesAndWindowsNTRegistry@16 +StopTraceA@16 +StopTraceW@16 +SystemFunction001@12 +SystemFunction002@12 +SystemFunction003@8 +SystemFunction004@12 +SystemFunction005@12 +SystemFunction006@8 +SystemFunction007@8 +SystemFunction008@12 +SystemFunction009@12 +SystemFunction010@12 +SystemFunction011@12 +SystemFunction012@12 +SystemFunction013@12 +SystemFunction014@12 +SystemFunction015@12 +SystemFunction016@12 +SystemFunction017@12 +SystemFunction018@12 +SystemFunction019@12 +SystemFunction020@12 +SystemFunction021@12 +SystemFunction022@12 +SystemFunction023@12 +SystemFunction024@12 +SystemFunction025@12 +SystemFunction026@12 +SystemFunction027@12 +SystemFunction028@8 +SystemFunction029@8 +SystemFunction030@8 +SystemFunction031@8 +SystemFunction032@8 +SystemFunction033@8 +SystemFunction034@12 +SystemFunction035@4 +SystemFunction036@8 +SystemFunction040@12 +SystemFunction041@12 +TraceEvent@12 +TraceEventInstance@20 +TraceMessage +TraceMessageVa@24 +TraceSetInformation@20 +TreeResetNamedSecurityInfoA@44 +TreeResetNamedSecurityInfoW@44 +TreeSetNamedSecurityInfoA@44 +TreeSetNamedSecurityInfoW@44 +TrusteeAccessToObjectA@24 +TrusteeAccessToObjectW@24 +UninstallApplication@8 +UnlockServiceDatabase@4 +UnregisterIdleTask@12 +UnregisterTraceGuids@8 +UpdateTraceA@16 +UpdateTraceW@16 +UsePinForEncryptedFilesA@12 +UsePinForEncryptedFilesW@12 +WmiCloseBlock@4 +WmiDevInstToInstanceNameA@16 +WmiDevInstToInstanceNameW@16 +WmiEnumerateGuids@8 +WmiExecuteMethodA@28 +WmiExecuteMethodW@28 +WmiFileHandleToInstanceNameA@16 +WmiFileHandleToInstanceNameW@16 +WmiFreeBuffer@4 +WmiMofEnumerateResourcesA@12 +WmiMofEnumerateResourcesW@12 +WmiNotificationRegistrationA@20 +WmiNotificationRegistrationW@20 +WmiOpenBlock@12 +WmiQueryAllDataA@12 +WmiQueryAllDataMultipleA@16 +WmiQueryAllDataMultipleW@16 +WmiQueryAllDataW@12 +WmiQueryGuidInformation@8 +WmiQuerySingleInstanceA@16 +WmiQuerySingleInstanceMultipleA@20 +WmiQuerySingleInstanceMultipleW@20 +WmiQuerySingleInstanceW@16 +WmiReceiveNotificationsA@16 +WmiReceiveNotificationsW@16 +WmiSetSingleInstanceA@20 +WmiSetSingleInstanceW@20 +WmiSetSingleItemA@24 +WmiSetSingleItemW@24 +WriteEncryptedFileRaw@12 diff --git a/lib/libc/mingw/lib32/bcrypt.def b/lib/libc/mingw/lib32/bcrypt.def new file mode 100644 index 000000000..6d6be99ab --- /dev/null +++ b/lib/libc/mingw/lib32/bcrypt.def @@ -0,0 +1,67 @@ +; +; Definition file of bcrypt.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "bcrypt.dll" +EXPORTS +BCryptAddContextFunction@20 +BCryptAddContextFunctionProvider@24 +BCryptCloseAlgorithmProvider@8 +BCryptConfigureContext@12 +BCryptConfigureContextFunction@20 +BCryptCreateContext@12 +BCryptCreateHash@28 +BCryptDecrypt@40 +BCryptDeleteContext@8 +BCryptDeriveKey@28 +BCryptDeriveKeyCapi@20 +BCryptDeriveKeyPBKDF2@40 +BCryptDestroyHash@4 +BCryptDestroyKey@4 +BCryptDestroySecret@4 +BCryptDuplicateHash@20 +BCryptDuplicateKey@20 +BCryptEncrypt@40 +BCryptEnumAlgorithms@16 +BCryptEnumContextFunctionProviders@24 +BCryptEnumContextFunctions@20 +BCryptEnumContexts@12 +BCryptEnumProviders@16 +BCryptEnumRegisteredProviders@8 +BCryptExportKey@28 +BCryptFinalizeKeyPair@8 +BCryptFinishHash@16 +BCryptFreeBuffer@4 +BCryptGenRandom@16 +BCryptGenerateKeyPair@16 +BCryptGenerateSymmetricKey@28 +BCryptGetFipsAlgorithmMode@4 +BCryptGetProperty@24 +BCryptHashData@16 +BCryptImportKey@36 +BCryptImportKeyPair@28 +BCryptOpenAlgorithmProvider@16 +BCryptQueryContextConfiguration@16 +BCryptQueryContextFunctionConfiguration@24 +BCryptQueryContextFunctionProperty@28 +BCryptQueryProviderRegistration@20 +BCryptRegisterConfigChangeNotify@4 +BCryptRegisterProvider@12 +BCryptRemoveContextFunction@16 +BCryptRemoveContextFunctionProvider@20 +BCryptResolveProviders@32 +BCryptSecretAgreement@16 +BCryptSetAuditingInterface@4 +BCryptSetContextFunctionProperty@28 +BCryptSetProperty@20 +BCryptSignHash@32 +BCryptUnregisterConfigChangeNotify@4 +BCryptUnregisterProvider@4 +BCryptVerifySignature@28 +GetAsymmetricEncryptionInterface@16 +GetCipherInterface@16 +GetHashInterface@16 +GetRngInterface@12 +GetSecretAgreementInterface@16 +GetSignatureInterface@16 diff --git a/lib/libc/mingw/lib32/comctl32.def b/lib/libc/mingw/lib32/comctl32.def new file mode 100644 index 000000000..c7b8d6ffe --- /dev/null +++ b/lib/libc/mingw/lib32/comctl32.def @@ -0,0 +1,119 @@ +LIBRARY COMCTL32.DLL +EXPORTS +_TrackMouseEvent@4 +AddMRUData@12 +AddMRUStringA@8 +AddMRUStringW@8 +Alloc@4 +CreateMRUListA@4 +CreateMRUListW@4 +CreateMappedBitmap@20 +CreatePage@8 +CreatePropertySheetPage@4 +CreatePropertySheetPageA@4 +CreatePropertySheetPageW@4 +CreateProxyPage@8 +CreateStatusWindow@16 +CreateStatusWindowA@16 +CreateStatusWindowW@16 +CreateToolbar@32 +CreateToolbarEx@52 +CreateUpDownControl@48 +DPA_Clone@8 +DPA_Create@4 +DPA_CreateEx@8 +DPA_DeleteAllPtrs@4 +DPA_DeletePtr@8 +DPA_Destroy@4 +DPA_GetPtr@8 +DPA_GetPtrIndex@8 +DPA_Grow@8 +DPA_InsertPtr@12 +DPA_Search@24 +DPA_SetPtr@12 +DPA_Sort@12 +DSA_Create@8 +DSA_DeleteAllItems@4 +DSA_DeleteItem@8 +DSA_Destroy@4 +DSA_GetItem@12 +DSA_GetItemPtr@8 +DSA_InsertItem@12 +DSA_SetItem@12 +DefSubclassProc@16 +DelMRUString@8 +DestroyPropertySheetPage@4 +DrawInsert@12 +DrawStatusText@16 +DrawStatusTextA@16 +DrawStatusTextW@16 +EnumMRUListA@16 +EnumMRUListW@16 +FindMRUData@16 +FindMRUStringA@12 +FindMRUStringW@12 +Free@4 +FreeMRUList@4 +GetEffectiveClientRect@12 +GetMUILanguage@0 +GetSize@4 +GetWindowSubclass@16 +ImageList_Add@12 +ImageList_AddIcon@8 +ImageList_AddMasked@12 +ImageList_BeginDrag@16 +ImageList_Copy@20 +ImageList_Create@20 +ImageList_Destroy@4 +ImageList_DragEnter@12 +ImageList_DragLeave@4 +ImageList_DragMove@8 +ImageList_DragShowNolock@4 +ImageList_Draw@24 +ImageList_DrawEx@40 +ImageList_DrawIndirect@4 +ImageList_Duplicate@4 +ImageList_EndDrag@0 +ImageList_GetBkColor@4 +ImageList_GetDragImage@8 +ImageList_GetIcon@12 +ImageList_GetIconSize@12 +ImageList_GetImageCount@4 +ImageList_GetImageInfo@12 +ImageList_GetImageRect@12 +ImageList_LoadImage@28 +ImageList_LoadImageA@28 +ImageList_LoadImageW@28 +ImageList_Merge@24 +ImageList_Read@4 +ImageList_Remove@8 +ImageList_Replace@16 +ImageList_ReplaceIcon@12 +ImageList_SetBkColor@8 +ImageList_SetDragCursorImage@16 +ImageList_SetIconSize@12 +ImageList_SetImageCount@8 +ImageList_SetOverlayImage@12 +ImageList_Write@8 +InitCommonControls@0 +InitCommonControlsEx@4 +InitMUILanguage@4 +LBItemFromPt@16 +LoadIconMetric@16 +MakeDragList@4 +MenuHelp@28 +PropertySheet@4 +PropertySheetA@4 +PropertySheetW@4 +ReAlloc@8 +RemoveWindowSubclass@12 +SendNotify@16 +SendNotifyEx@20 +SetWindowSubclass@16 +ShowHideMenuCtl@12 +Str_GetPtrA@12 +Str_GetPtrW@12 +Str_SetPtrA@8 +Str_SetPtrW@8 +TaskDialog@32 +TaskDialogIndirect@16 diff --git a/lib/libc/mingw/lib32/comdlg32.def b/lib/libc/mingw/lib32/comdlg32.def new file mode 100644 index 000000000..82806aa03 --- /dev/null +++ b/lib/libc/mingw/lib32/comdlg32.def @@ -0,0 +1,27 @@ +LIBRARY COMDLG32.DLL +EXPORTS +ChooseColorA@4 +ChooseColorW@4 +ChooseFontA@4 +ChooseFontW@4 +CommDlgExtendedError@0 +FindTextA@4 +FindTextW@4 +GetFileTitleA@12 +GetFileTitleW@12 +GetOpenFileNameA@4 +GetOpenFileNameW@4 +GetSaveFileNameA@4 +GetSaveFileNameW@4 +LoadAlterBitmap@12 +PageSetupDlgA@4 +PageSetupDlgW@4 +PrintDlgA@4 +PrintDlgExA@4 +PrintDlgExW@4 +PrintDlgW@4 +ReplaceTextA@4 +ReplaceTextW@4 +WantArrows@16 +dwLBSubclass@16 +dwOKSubclass@16 diff --git a/lib/libc/mingw/lib32/crypt32.def b/lib/libc/mingw/lib32/crypt32.def new file mode 100644 index 000000000..6b30aff9f --- /dev/null +++ b/lib/libc/mingw/lib32/crypt32.def @@ -0,0 +1,314 @@ +; +; Definition file of CRYPT32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "CRYPT32.dll" +EXPORTS +CryptAcquireContextU@20 +CryptEnumProvidersU@24 +CryptObjectLocatorFree@4 +CryptObjectLocatorGet@12 +CryptObjectLocatorGetContent@4 +CryptObjectLocatorGetUpdated@4 +CryptObjectLocatorInitialize@20 +CryptObjectLocatorIsChanged@4 +CryptObjectLocatorRelease@8 +CryptSetProviderU@8 +CryptSignHashU@24 +CryptVerifySignatureU@24 +I_PFXImportCertStoreEx@20 +RegCreateKeyExU@36 +RegDeleteValueU@8 +RegEnumValueU@32 +RegOpenKeyExU@20 +RegQueryInfoKeyU@48 +RegQueryValueExU@24 +RegSetValueExU@24 +CertAddCRLContextToStore@16 +CertAddCRLLinkToStore@16 +CertAddCTLContextToStore@16 +CertAddCTLLinkToStore@16 +CertAddCertificateContextToStore@16 +CertAddCertificateLinkToStore@16 +CertAddEncodedCRLToStore@24 +CertAddEncodedCTLToStore@24 +CertAddEncodedCertificateToStore@24 +CertAddEncodedCertificateToSystemStoreA@12 +CertAddEncodedCertificateToSystemStoreW@12 +CertAddEnhancedKeyUsageIdentifier@8 +CertAddRefServerOcspResponse@4 +CertAddRefServerOcspResponseContext@4 +CertAddSerializedElementToStore@32 +CertAddStoreToCollection@16 +CertAlgIdToOID@4 +CertCloseServerOcspResponse@8 +CertCloseStore@8 +CertCompareCertificate@12 +CertCompareCertificateName@12 +CertCompareIntegerBlob@8 +CertComparePublicKeyInfo@12 +CertControlStore@16 +CertCreateCRLContext@12 +CertCreateCTLContext@12 +CertCreateCTLEntryFromCertificateContextProperties@28 +CertCreateCertificateChainEngine@8 +CertCreateCertificateContext@12 +CertCreateContext@24 +CertCreateSelfSignCertificate@32 +CertDeleteCRLFromStore@4 +CertDeleteCTLFromStore@4 +CertDeleteCertificateFromStore@4 +CertDuplicateCRLContext@4 +CertDuplicateCTLContext@4 +CertDuplicateCertificateChain@4 +CertDuplicateCertificateContext@4 +CertDuplicateStore@4 +CertEnumCRLContextProperties@8 +CertEnumCRLsInStore@8 +CertEnumCTLContextProperties@8 +CertEnumCTLsInStore@8 +CertEnumCertificateContextProperties@8 +CertEnumCertificatesInStore@8 +CertEnumPhysicalStore@16 +CertEnumSubjectInSortedCTL@16 +CertEnumSystemStore@16 +CertEnumSystemStoreLocation@12 +CertFindAttribute@12 +CertFindCRLInStore@24 +CertFindCTLInStore@24 +CertFindCertificateInCRL@20 +CertFindCertificateInStore@24 +CertFindChainInStore@24 +CertFindExtension@12 +CertFindRDNAttr@8 +CertFindSubjectInCTL@20 +CertFindSubjectInSortedCTL@20 +CertFreeCRLContext@4 +CertFreeCTLContext@4 +CertFreeCertificateChain@4 +CertFreeCertificateChainEngine@4 +CertFreeCertificateChainList@4 +CertFreeCertificateContext@4 +CertFreeServerOcspResponseContext@4 +CertGetCRLContextProperty@16 +CertGetCRLFromStore@16 +CertGetCTLContextProperty@16 +CertGetCertificateChain@32 +CertGetCertificateContextProperty@16 +CertGetEnhancedKeyUsage@16 +CertGetIntendedKeyUsage@16 +CertGetIssuerCertificateFromStore@16 +CertGetNameStringA@24 +CertGetNameStringW@24 +CertGetPublicKeyLength@8 +CertGetServerOcspResponseContext@12 +CertGetStoreProperty@16 +CertGetSubjectCertificateFromStore@12 +CertGetValidUsages@20 +CertIsRDNAttrsInCertificateName@16 +CertIsStrongHashToSign@12 +CertIsValidCRLForCertificate@16 +CertIsWeakHash@24 +CertNameToStrA@20 +CertNameToStrW@20 +CertOIDToAlgId@4 +CertOpenServerOcspResponse@12 +CertOpenStore@20 +CertOpenSystemStoreA@8 +CertOpenSystemStoreW@8 +CertRDNValueToStrA@16 +CertRDNValueToStrW@16 +CertRegisterPhysicalStore@20 +CertRegisterSystemStore@16 +CertRemoveEnhancedKeyUsageIdentifier@8 +CertRemoveStoreFromCollection@8 +CertResyncCertificateChainEngine@4 +CertRetrieveLogoOrBiometricInfo@36 +CertSaveStore@24 +CertSelectCertificateChains@32 +CertSerializeCRLStoreElement@16 +CertSerializeCTLStoreElement@16 +CertSerializeCertificateStoreElement@16 +CertSetCRLContextProperty@16 +CertSetCTLContextProperty@16 +CertSetCertificateContextPropertiesFromCTLEntry@12 +CertSetCertificateContextProperty@16 +CertSetEnhancedKeyUsage@8 +CertSetStoreProperty@16 +CertStrToNameA@28 +CertStrToNameW@28 +CertUnregisterPhysicalStore@12 +CertUnregisterSystemStore@8 +CertVerifyCRLRevocation@16 +CertVerifyCRLTimeValidity@8 +CertVerifyCTLUsage@28 +CertVerifyCertificateChainPolicy@16 +CertVerifyRevocation@28 +CertVerifySubjectCertificateContext@12 +CertVerifyTimeValidity@8 +CertVerifyValidityNesting@8 +CreateFileU@28 +CryptAcquireCertificatePrivateKey@24 +CryptBinaryToStringA@20 +CryptBinaryToStringW@20 +CryptCloseAsyncHandle@4 +CryptCreateAsyncHandle@8 +CryptCreateKeyIdentifierFromCSP@32 +CryptDecodeMessage@52 +CryptDecodeObject@28 +CryptDecodeObjectEx@32 +CryptDecryptAndVerifyMessageSignature@36 +CryptDecryptMessage@24 +CryptEncodeObject@20 +CryptEncodeObjectEx@28 +CryptEncryptMessage@28 +CryptEnumKeyIdentifierProperties@28 +CryptEnumOIDFunction@24 +CryptEnumOIDInfo@16 +CryptExportPKCS8@28 +CryptExportPublicKeyInfo@20 +CryptExportPublicKeyInfoEx@32 +CryptExportPublicKeyInfoFromBCryptKeyHandle@28 +CryptFindCertificateKeyProvInfo@12 +CryptFindLocalizedName@4 +CryptFindOIDInfo@12 +CryptFormatObject@36 +CryptFreeOIDFunctionAddress@8 +CryptGetAsyncParam@16 +CryptGetDefaultOIDDllList@16 +CryptGetDefaultOIDFunctionAddress@24 +CryptGetKeyIdentifierProperty@28 +CryptGetMessageCertificates@20 +CryptGetMessageSignerCount@12 +CryptGetOIDFunctionAddress@24 +CryptGetOIDFunctionValue@28 +CryptHashCertificate2@28 +CryptHashCertificate@28 +CryptHashMessage@36 +CryptHashPublicKeyInfo@28 +CryptHashToBeSigned@24 +CryptImportPKCS8@36 +CryptImportPublicKeyInfo@16 +CryptImportPublicKeyInfoEx2@20 +CryptImportPublicKeyInfoEx@28 +CryptInitOIDFunctionSet@8 +CryptInstallDefaultContext@24 +CryptInstallOIDFunctionAddress@24 +CryptLoadSip@12 +CryptMemAlloc@4 +CryptMemFree@4 +CryptMemRealloc@8 +CryptMsgCalculateEncodedLength@24 +CryptMsgClose@4 +CryptMsgControl@16 +CryptMsgCountersign@16 +CryptMsgCountersignEncoded@28 +CryptMsgDuplicate@4 +CryptMsgEncodeAndSignCTL@24 +CryptMsgGetAndVerifySigner@24 +CryptMsgGetParam@20 +CryptMsgOpenToDecode@24 +CryptMsgOpenToEncode@24 +CryptMsgSignCTL@28 +CryptMsgUpdate@16 +CryptMsgVerifyCountersignatureEncoded@28 +CryptMsgVerifyCountersignatureEncodedEx@40 +CryptProtectData@28 +CryptProtectMemory@12 +CryptQueryObject@44 +CryptRegisterDefaultOIDFunction@16 +CryptRegisterOIDFunction@20 +CryptRegisterOIDInfo@8 +CryptRetrieveTimeStamp@40 +CryptSIPAddProvider@4 +CryptSIPCreateIndirectData@12 +CryptSIPGetCaps@8 +CryptSIPGetSealedDigest@20 +CryptSIPGetSignedDataMsg@20 +CryptSIPLoad@12 +CryptSIPPutSignedDataMsg@20 +CryptSIPRemoveProvider@4 +CryptSIPRemoveSignedDataMsg@8 +CryptSIPRetrieveSubjectGuid@12 +CryptSIPRetrieveSubjectGuidForCatalogFile@12 +CryptSIPVerifyIndirectData@8 +CryptSetAsyncParam@16 +CryptSetKeyIdentifierProperty@24 +CryptSetOIDFunctionValue@28 +CryptSignAndEncodeCertificate@36 +CryptSignAndEncryptMessage@32 +CryptSignCertificate@36 +CryptSignMessage@28 +CryptSignMessageWithKey@20 +CryptStringToBinaryA@28 +CryptStringToBinaryW@28 +CryptUninstallDefaultContext@12 +CryptUnprotectData@28 +CryptUnprotectMemory@12 +CryptUnregisterDefaultOIDFunction@12 +CryptUnregisterOIDFunction@12 +CryptUnregisterOIDInfo@4 +CryptUpdateProtectedState@20 +CryptVerifyCertificateSignature@20 +CryptVerifyCertificateSignatureEx@32 +CryptVerifyDetachedMessageHash@32 +CryptVerifyDetachedMessageSignature@32 +CryptVerifyMessageHash@28 +CryptVerifyMessageSignature@28 +CryptVerifyMessageSignatureWithKey@24 +CryptVerifyTimeStampSignature@32 +I_CertChainEngineIsDisallowedCertificate@8 +I_CertDiagControl@12 +I_CertFinishSslHandshake@16 +I_CertProcessSslHandshake@24 +I_CertProtectFunction@28 +I_CertSrvProtectFunction@40 +I_CertSyncStore@8 +I_CertUpdateStore@16 +I_CryptAddRefLruEntry@4 +I_CryptAddSmartCardCertToStore@20 +I_CryptAllocTls@0 +I_CryptAllocTlsEx@4 +I_CryptCreateLruCache@8 +I_CryptCreateLruEntry@16 +I_CryptDetachTls@4 +I_CryptDisableLruOfEntries@4 +I_CryptEnableLruOfEntries@8 +I_CryptEnumMatchingLruEntries@4 +I_CryptFindLruEntry@8 +I_CryptFindLruEntryData@12 +I_CryptFindSmartCardCertInStore@16 +I_CryptFlushLruCache@12 +I_CryptFreeLruCache@12 +I_CryptFreeTls@8 +I_CryptGetAsn1Decoder@4 +I_CryptGetAsn1Encoder@4 +I_CryptGetDefaultCryptProv@4 +I_CryptGetDefaultCryptProvForEncrypt@12 +I_CryptGetFileVersion@12 +I_CryptGetLruEntryData@4 +I_CryptGetLruEntryIdentifier@4 +I_CryptGetOssGlobal@4 +I_CryptGetTls@4 +I_CryptInsertLruEntry@8 +I_CryptInstallAsn1Module@12 +I_CryptInstallOssGlobal@12 +I_CryptReadTrustedPublisherDWORDValueFromRegistry@8 +I_CryptRegisterSmartCardStore@20 +I_CryptReleaseLruEntry@4 +I_CryptRemoveLruEntry@12 +I_CryptSetTls@8 +I_CryptTouchLruEntry@8 +I_CryptUninstallAsn1Module@4 +I_CryptUninstallOssGlobal@4 +I_CryptUnregisterSmartCardStore@4 +I_CryptWalkAllLruCacheEntries@12 +PFXExportCertStore2@16 +PFXExportCertStore@16 +PFXExportCertStoreEx@20 +PFXImportCertStore@12 +PFXIsPFXBlob@4 +PFXVerifyPassword@12 +RegCreateHKCUKeyExU@36 +RegOpenHKCUKeyExU@20 diff --git a/lib/libc/mingw/lib32/cryptnet.def b/lib/libc/mingw/lib32/cryptnet.def new file mode 100644 index 000000000..d80b19dd8 --- /dev/null +++ b/lib/libc/mingw/lib32/cryptnet.def @@ -0,0 +1,26 @@ +; +; Definition file of CRYPTNET.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "CRYPTNET.dll" +EXPORTS +CertDllVerifyCTLUsage@28 +CertDllVerifyRevocation@28 +LdapProvOpenStore@28 +CryptCancelAsyncRetrieval@4 +CryptFlushTimeValidObject@20 +CryptGetObjectUrl@32 +CryptGetTimeValidObject@36 +CryptInstallCancelRetrieval@16 +CryptRetrieveObjectByUrlA@36 +CryptRetrieveObjectByUrlW@36 +CryptUninstallCancelRetrieval@8 +DllRegisterServer +DllUnregisterServer +I_CryptNetEnumUrlCacheEntry@16 +I_CryptNetGetHostNameFromUrl@12 +I_CryptNetGetUserDsStoreUrl@8 +I_CryptNetIsConnected +I_CryptNetSetUrlCacheFlushInfo@20 +I_CryptNetSetUrlCachePreFetchInfo@20 diff --git a/lib/libc/mingw/lib32/gdi32.def b/lib/libc/mingw/lib32/gdi32.def new file mode 100644 index 000000000..a2677fb51 --- /dev/null +++ b/lib/libc/mingw/lib32/gdi32.def @@ -0,0 +1,376 @@ +LIBRARY GDI32.dll +EXPORTS +AbortDoc@4 +AbortPath@4 +AddFontMemResourceEx@16 +AddFontResourceA@4 +AddFontResourceW@4 +AddFontResourceExA@12 +AddFontResourceExW@12 +AngleArc@24 +AnimatePalette@16 +Arc@36 +ArcTo@36 +BeginPath@4 +BitBlt@36 +CancelDC@4 +CheckColorsInGamut@16 +ChoosePixelFormat@8 +Chord@36 +CloseEnhMetaFile@4 +CloseFigure@4 +CloseMetaFile@4 +ColorCorrectPalette@16 +ColorMatchToTarget@12 +CombineRgn@16 +CombineTransform@12 +CopyEnhMetaFileA@8 +CopyEnhMetaFileW@8 +CopyMetaFileA@8 +CopyMetaFileW@8 +CreateBitmap@20 +CreateBitmapIndirect@4 +CreateBrushIndirect@4 +CreateColorSpaceA@4 +CreateColorSpaceW@4 +CreateCompatibleBitmap@12 +CreateCompatibleDC@4 +CreateDCA@16 +CreateDCW@16 +CreateDIBPatternBrush@8 +CreateDIBPatternBrushPt@8 +CreateDIBSection@24 +CreateDIBitmap@24 +CreateDiscardableBitmap@12 +CreateEllipticRgn@16 +CreateEllipticRgnIndirect@4 +CreateEnhMetaFileA@16 +CreateEnhMetaFileW@16 +CreateFontA@56 +CreateFontIndirectA@4 +CreateFontIndirectExA@4 +CreateFontIndirectExW@4 +CreateFontIndirectW@4 +CreateFontW@56 +CreateHalftonePalette@4 +CreateHatchBrush@8 +CreateICA@16 +CreateICW@16 +CreateMetaFileA@4 +CreateMetaFileW@4 +CreatePalette@4 +CreatePatternBrush@4 +CreatePen@12 +CreatePenIndirect@4 +CreatePolyPolygonRgn@16 +CreatePolygonRgn@12 +CreateRectRgn@16 +CreateRectRgnIndirect@4 +CreateRoundRectRgn@24 +CreateScalableFontResourceA@16 +CreateScalableFontResourceW@16 +CreateSolidBrush@4 +DPtoLP@12 +DeleteColorSpace@4 +DeleteDC@4 +DeleteEnhMetaFile@4 +DeleteMetaFile@4 +DeleteObject@4 +DescribePixelFormat@16 +DeviceCapabilitiesEx@24 +DeviceCapabilitiesExA@24 +DeviceCapabilitiesExW@24 +DrawEscape@16 +Ellipse@20 +EnableEUDC@4 +EndDoc@4 +EndPage@4 +EndPath@4 +EnumEnhMetaFile@20 +EnumFontFamiliesA@16 +EnumFontFamiliesExA@20 +EnumFontFamiliesExW@20 +EnumFontFamiliesW@16 +EnumFontsA@16 +EnumFontsW@16 +EnumICMProfilesA@12 +EnumICMProfilesW@12 +EnumMetaFile@16 +EnumObjects@16 +EqualRgn@8 +Escape@20 +ExcludeClipRect@20 +ExtCreatePen@20 +ExtCreateRegion@12 +ExtEscape@24 +ExtFloodFill@20 +ExtSelectClipRgn@12 +ExtTextOutA@32 +ExtTextOutW@32 +FillPath@4 +FillRgn@12 +FixBrushOrgEx@16 +FlattenPath@4 +FloodFill@16 +FontIsLinked@4 +FrameRgn@20 +GdiAlphaBlend@44 +GdiComment@12 +GdiConvertToDevmodeW@4 +GdiEntry13@0 ; alias for DdQueryDisplaySettingsUniqueness +GdiFlush@0 +GdiGetBatchLimit@0 +GdiGetCharDimensions@12 +GdiGetCodePage@4 +GdiGetSpoolMessage@16 +GdiGradientFill@24 +GdiInitSpool@0 +GdiInitializeLanguagePack@4 +GdiIsMetaFileDC@4 +GdiIsMetaPrintDC@4 +GdiIsPlayMetafileDC@4 +GdiPlayDCScript@24 +GdiPlayJournal@20 +GdiPlayScript@28 +GdiRealizationInfo@8 +GdiSetBatchLimit@4 +GdiTransparentBlt@44 +GetArcDirection@4 +GetAspectRatioFilterEx@8 +GetBitmapBits@12 +GetBitmapDimensionEx@8 +GetBkColor@4 +GetBkMode@4 +GetBoundsRect@12 +GetBrushOrgEx@8 +GetCharABCWidthsA@16 +GetCharABCWidthsFloatA@16 +GetCharABCWidthsFloatW@16 +GetCharABCWidthsI@20 +GetCharABCWidthsW@16 +GetCharWidth32A@16 +GetCharWidth32W@16 +GetCharWidthA@16 +GetCharWidthFloatA@16 +GetCharWidthFloatW@16 +GetCharWidthI@20 +GetCharWidthW@16 +GetCharacterPlacementA@24 +GetCharacterPlacementW@24 +GetClipBox@8 +GetClipRgn@8 +GetColorAdjustment@8 +GetColorSpace@4 +GetCurrentObject@8 +GetCurrentPositionEx@8 +GetDCBrushColor@4 +GetDCOrgEx@8 +GetDCPenColor@4 +GetDIBColorTable@16 +GetDIBits@28 +GetDeviceCaps@8 +GetDeviceGammaRamp@8 +GetEnhMetaFileA@4 +GetEnhMetaFileBits@12 +GetEnhMetaFileDescriptionA@12 +GetEnhMetaFileDescriptionW@12 +GetEnhMetaFileHeader@12 +GetEnhMetaFilePaletteEntries@12 +GetEnhMetaFilePixelFormat@12 +GetEnhMetaFileW@4 +GetFontData@20 +GetFontLanguageInfo@4 +GetFontResourceInfo@16 +GetFontResourceInfoW@16 +GetFontUnicodeRanges@8 +GetGlyphIndicesA@20 +GetGlyphIndicesW@20 +GetGlyphOutline@28 +GetGlyphOutlineA@28 +GetGlyphOutlineW@28 +GetGlyphOutlineWow@28 +GetGraphicsMode@4 +GetICMProfileA@12 +GetICMProfileW@12 +GetKerningPairs@12 +GetKerningPairsA@12 +GetKerningPairsW@12 +GetLayout@4 +GetLogColorSpaceA@12 +GetLogColorSpaceW@12 +GetMapMode@4 +GetMetaFileA@4 +GetMetaFileBitsEx@12 +GetMetaFileW@4 +GetMetaRgn@8 +GetMiterLimit@8 +GetNearestColor@8 +GetNearestPaletteIndex@8 +GetObjectA@12 +GetObjectType@4 +GetObjectW@12 +GetOutlineTextMetricsA@12 +GetOutlineTextMetricsW@12 +GetPaletteEntries@16 +GetPath@16 +GetPixel@12 +GetPixelFormat@4 +GetPolyFillMode@4 +GetROP2@4 +GetRandomRgn@12 +GetRasterizerCaps@8 +GetRegionData@12 +GetRelAbs@8 +GetRgnBox@8 +GetStockObject@4 +GetStretchBltMode@4 +GetSystemPaletteEntries@16 +GetSystemPaletteUse@4 +GetTextAlign@4 +GetTextCharacterExtra@4 +GetTextCharset@4 +GetTextCharsetInfo@12 +GetTextColor@4 +GetTextExtentExPointA@28 +GetTextExtentExPointW@28 +GetTextExtentExPointI@28 +GetTextExtentPoint32A@16 +GetTextExtentPoint32W@16 +GetTextExtentPointA@16 +GetTextExtentPointI@16 +GetTextExtentPointW@16 +GetTextFaceA@12 +GetTextFaceW@12 +GetTextMetricsA@8 +GetTextMetricsW@8 +GetTransform@12 +GetViewportExtEx@8 +GetViewportOrgEx@8 +GetWinMetaFileBits@20 +GetWindowExtEx@8 +GetWindowOrgEx@8 +GetWorldTransform@8 +IntersectClipRect@20 +InvertRgn@8 +LPtoDP@12 +LineDDA@24 +LineTo@12 +MaskBlt@48 +ModifyWorldTransform@12 +MoveToEx@16 +NamedEscape@28 +OffsetClipRgn@12 +OffsetRgn@12 +OffsetViewportOrgEx@16 +OffsetWindowOrgEx@16 +PaintRgn@8 +PatBlt@24 +PathToRegion@4 +Pie@36 +PlayEnhMetaFile@12 +PlayEnhMetaFileRecord@16 +PlayMetaFile@8 +PlayMetaFileRecord@16 +PlgBlt@40 +PolyBezier@12 +PolyBezierTo@12 +PolyDraw@16 +PolyPolygon@16 +PolyPolyline@16 +PolyTextOutA@12 +PolyTextOutW@12 +Polygon@12 +Polyline@12 +PolylineTo@12 +PtInRegion@12 +PtVisible@12 +RealizePalette@4 +RectInRegion@8 +RectVisible@8 +Rectangle@20 +RemoveFontMemResourceEx@4 +RemoveFontResourceA@4 +RemoveFontResourceW@4 +RemoveFontResourceExA@12 +RemoveFontResourceExW@12 +ResetDCA@8 +ResetDCW@8 +ResizePalette@8 +RestoreDC@8 +RoundRect@28 +SaveDC@4 +ScaleViewportExtEx@24 +ScaleWindowExtEx@24 +SelectBrushLocal@8 +SelectClipPath@8 +SelectClipRgn@8 +SelectFontLocal@8 +SelectObject@8 +SelectPalette@12 +SetAbortProc@8 +SetArcDirection@8 +SetBitmapBits@12 +SetBitmapDimensionEx@16 +SetBkColor@8 +SetBkMode@8 +SetBoundsRect@12 +SetBrushOrgEx@16 +SetColorAdjustment@8 +SetColorSpace@8 +SetDCBrushColor@8 +SetDCPenColor@8 +SetDIBColorTable@16 +SetDIBits@28 +SetDIBitsToDevice@48 +SetDeviceGammaRamp@8 +SetEnhMetaFileBits@8 +SetFontEnumeration@4 +SetGraphicsMode@8 +SetICMMode@8 +SetICMProfileA@8 +SetICMProfileW@8 +SetLayout@8 +SetMagicColors@12 +SetMapMode@8 +SetMapperFlags@8 +SetMetaFileBitsEx@8 +SetMetaRgn@4 +SetMiterLimit@12 +SetPaletteEntries@16 +SetPixel@16 +SetPixelFormat@12 +SetPixelV@16 +SetPolyFillMode@8 +SetROP2@8 +SetRectRgn@20 +SetRelAbs@8 +SetStretchBltMode@8 +SetSystemPaletteUse@8 +SetTextAlign@8 +SetTextCharacterExtra@8 +SetTextColor@8 +SetTextJustification@12 +SetViewportExtEx@16 +SetViewportOrgEx@16 +SetVirtualResolution@20 +SetWinMetaFileBits@16 +SetWindowExtEx@16 +SetWindowOrgEx@16 +SetWorldTransform@8 +StartDocA@8 +StartDocW@8 +StartPage@4 +StretchBlt@44 +StretchDIBits@52 +StrokeAndFillPath@4 +StrokePath@4 +SwapBuffers@4 +TextOutA@20 +TextOutW@20 +TranslateCharsetInfo@12 +UnrealizeObject@4 +UpdateColors@4 +UpdateICMRegKeyA@16 +UpdateICMRegKeyW@16 +WidenPath@4 +gdiPlaySpoolStream@24 diff --git a/lib/libc/mingw/lib32/imm32.def b/lib/libc/mingw/lib32/imm32.def new file mode 100644 index 000000000..ad2ceb442 --- /dev/null +++ b/lib/libc/mingw/lib32/imm32.def @@ -0,0 +1,136 @@ +LIBRARY IMM32.DLL +EXPORTS +CtfImmAppCompatEnableIMEonProtectedCode +CtfImmCoUninitialize +CtfImmDispatchDefImeMessage@16 +CtfImmEnterCoInitCountSkipMode +CtfImmGenerateMessage@8 +CtfImmGetCompatibleKeyboardLayout@4 +CtfImmGetGuidAtom@12 +CtfImmGetIMEFileName@8 +CtfImmGetTMAEFlags +CtfImmHideToolbarWnd +CtfImmIsCiceroEnabled +CtfImmIsCiceroStartedInThread +CtfImmIsGuidMapEnable@4 +CtfImmIsTextFrameServiceDisabled +CtfImmLastEnabledWndDestroy@4 +CtfImmLeaveCoInitCountSkipMode +CtfImmNotify@12 +CtfImmRestoreToolbarWnd@4 +CtfImmSetAppCompatFlags@4 +CtfImmSetCiceroStartInThread@4 +CtfImmSetDefaultRemoteKeyboardLayout@8 +CtfImmTIMActivate@8 +GetKeyboardLayoutCP@4 +ImmActivateLayout@4 +ImmAssociateContext@8 +ImmAssociateContextEx@12 +ImmCallImeConsoleIME@20 +ImmConfigureIMEA@16 +ImmConfigureIMEW@16 +ImmCreateContext@0 +ImmCreateIMCC@4 +ImmCreateSoftKeyboard@16 +ImmDestroyContext@4 +ImmDestroyIMCC@4 +ImmDestroySoftKeyboard@4 +ImmDisableIME@4 +ImmDisableIme@4 +ImmDisableTextFrameService@4 +ImmEnumInputContext@12 +ImmEnumRegisterWordA@24 +ImmEnumRegisterWordW@24 +ImmEscapeA@16 +ImmEscapeW@16 +ImmFreeLayout@4 +ImmGenerateMessage@4 +ImmGetAppCompatFlags@4 +ImmGetCandidateListA@16 +ImmGetCandidateListCountA@8 +ImmGetCandidateListCountW@8 +ImmGetCandidateListW@16 +ImmGetCandidateWindow@12 +ImmGetCompositionFontA@8 +ImmGetCompositionFontW@8 +ImmGetCompositionStringA@16 +ImmGetCompositionStringW@16 +ImmGetCompositionWindow@8 +ImmGetContext@4 +ImmGetConversionListA@24 +ImmGetConversionListW@24 +ImmGetConversionStatus@12 +ImmGetDefaultIMEWnd@4 +ImmGetDescriptionA@12 +ImmGetDescriptionW@12 +ImmGetGuideLineA@16 +ImmGetGuideLineW@16 +ImmGetHotKey@16 +ImmGetIMCCLockCount@4 +ImmGetIMCCSize@4 +ImmGetIMCLockCount@4 +ImmGetIMEFileNameA@12 +ImmGetIMEFileNameW@12 +ImmGetImeInfoEx@12 +ImmGetImeMenuItemsA@24 +ImmGetImeMenuItemsW@24 +ImmGetOpenStatus@4 +ImmGetProperty@8 +ImmGetRegisterWordStyleA@12 +ImmGetRegisterWordStyleW@12 +ImmGetStatusWindowPos@8 +ImmGetVirtualKey@4 +ImmIMPGetIMEA@8 +ImmIMPGetIMEW@8 +ImmIMPQueryIMEA@4 +ImmIMPQueryIMEW@4 +ImmIMPSetIMEA@8 +ImmIMPSetIMEW@8 +ImmInstallIMEA@8 +ImmInstallIMEW@8 +ImmIsIME@4 +ImmIsUIMessageA@16 +ImmIsUIMessageW@16 +ImmLoadIME@4 +ImmLoadLayout@8 +ImmLockClientImc@4 +ImmLockIMC@4 +ImmLockIMCC@4 +ImmLockImeDpi@4 +ImmNotifyIME@16 +ImmProcessKey@20 +ImmPutImeMenuItemsIntoMappedFile@4 +ImmReSizeIMCC@8 +ImmRegisterClient@8 +ImmRegisterWordA@16 +ImmRegisterWordW@16 +ImmReleaseContext@8 +ImmRequestMessageA@12 +ImmRequestMessageW@12 +ImmSendIMEMessageExA@8 +ImmSendIMEMessageExW@8 +ImmSetActiveContext@12 +ImmSetActiveContextConsoleIME@8 +ImmSetCandidateWindow@8 +ImmSetCompositionFontA@8 +ImmSetCompositionFontW@8 +ImmSetCompositionStringA@24 +ImmSetCompositionStringW@24 +ImmSetCompositionWindow@8 +ImmSetConversionStatus@12 +ImmSetHotKey@16 +ImmSetOpenStatus@8 +ImmSetStatusWindowPos@8 +ImmShowSoftKeyboard@8 +ImmSimulateHotKey@8 +ImmSystemHandler@12 +ImmTranslateMessage@16 +ImmUnlockClientImc@4 +ImmUnlockIMC@4 +ImmUnlockIMCC@4 +ImmUnlockImeDpi@4 +ImmUnregisterWordA@16 +ImmUnregisterWordW@16 +ImmWINNLSEnableIME@8 +ImmWINNLSGetEnableStatus@4 +ImmWINNLSGetIMEHotkey@4 diff --git a/lib/libc/mingw/lib32/kernel32.def b/lib/libc/mingw/lib32/kernel32.def new file mode 100644 index 000000000..464a19996 --- /dev/null +++ b/lib/libc/mingw/lib32/kernel32.def @@ -0,0 +1,1660 @@ +; +; Definition file of KERNEL32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "KERNEL32.dll" +EXPORTS +BaseThreadInitThunk@4 +InterlockedPushListSList@8 +AcquireSRWLockExclusive@4 +AcquireSRWLockShared@4 +ActivateActCtx@8 +ActivateActCtxWorker@8 +AddAtomA@4 +AddAtomW@4 +AddConsoleAliasA@12 +AddConsoleAliasW@12 +AddDllDirectory@4 +AddIntegrityLabelToBoundaryDescriptor@8 +AddLocalAlternateComputerNameA@8 +AddLocalAlternateComputerNameW@8 +AddRefActCtx@4 +AddRefActCtxWorker@4 +AddResourceAttributeAce@28 +AddSIDToBoundaryDescriptor@8 +AddScopedPolicyIDAce@20 +AddSecureMemoryCacheCallback@4 +AddVectoredContinueHandler@8 +AddVectoredExceptionHandler@8 +AdjustCalendarDate@12 +AllocConsole@0 +AllocateUserPhysicalPages@12 +AllocateUserPhysicalPagesNuma@16 +AppPolicyGetClrCompat@8 +AppPolicyGetCreateFileAccess@8 +AAppPolicyGetLifecycleManagement@8 +AppPolicyGetMediaFoundationCodecLoading@8 +AppPolicyGetProcessTerminationMethod@8 +AppPolicyGetShowDeveloperDiagnostic@8 +AppPolicyGetThreadInitializationType@8 +AppPolicyGetWindowingModel@8 +AppXGetOSMaxVersionTested@8 +ApplicationRecoveryFinished@4 +ApplicationRecoveryInProgress@4 +AreFileApisANSI@0 +AssignProcessToJobObject@8 +AttachConsole@4 +BackupRead@28 +BackupSeek@24 +BackupWrite@28 +BaseAttachCompleteThunk@0 +BaseCheckAppcompatCache@16 +BaseCheckAppcompatCacheEx@24 +BaseCheckAppcompatCacheExWorker@36 +BaseCheckAppcompatCacheWorker@16 +BaseCheckElevation@48 +BaseCheckRunApp@52 +BaseCleanupAppcompatCacheSupport@4 +BaseDllReadWriteIniFile@32 +BaseDumpAppcompatCache@0 +BaseDumpAppcompatCacheWorker@0 +BaseElevationPostProcessing@12 +BaseFlushAppcompatCache@0 +BaseFlushAppcompatCacheWorker@0 +BaseFormatObjectAttributes@16 +BaseFormatTimeOut@8 +BaseFreeAppCompatDataForProcessWorker@4 +BaseGenerateAppCompatData@24 +BaseGetNamedObjectDirectory@4 +BaseInitAppcompatCacheSupport@0 +BaseInitAppcompatCacheSupportWorker@0 +BaseIsAppcompatInfrastructureDisabled@0 +BaseIsAppcompatInfrastructureDisabledWorker@0 +BaseIsDosApplication@8 +BaseQueryModuleData@28 +BaseReadAppCompatDataForProcessWorker@12 +BaseSetLastNTError@4 +BaseUpdateAppcompatCache@12 +BaseUpdateAppcompatCacheWorker@12 +BaseUpdateVDMEntry@16 +BaseVerifyUnicodeString@4 +BaseWriteErrorElevationRequiredEvent@0 +Basep8BitStringToDynamicUnicodeString@8 +BasepAllocateActivationContextActivationBlock@16 +BasepAnsiStringToDynamicUnicodeString@8 +BasepAppContainerEnvironmentExtension@12 +BasepAppXExtension@24 +BasepCheckAppCompat@16 +BasepCheckBadapp@56 +BasepCheckWebBladeHashes@4 +BasepCheckWinSaferRestrictions@28 +BasepConstructSxsCreateProcessMessage@80 +BasepCopyEncryption@12 +BasepFreeActivationContextActivationBlock@4 +BasepFreeAppCompatData@12 +BasepGetAppCompatData@60 +BasepGetComputerNameFromNtPath@16 +BasepGetExeArchType@12 +BasepInitAppCompatData@12 +BasepIsProcessAllowed@4 +BasepMapModuleHandle@8 +BasepNotifyLoadStringResource@16 +BasepPostSuccessAppXExtension@8 +BasepProcessInvalidImage@84 +BasepQueryAppCompat@72 +BasepQueryModuleChpeSettings@40 +BasepReleaseAppXContext@4 +BasepReleaseSxsCreateProcessUtilityStruct@4 +BasepReportFault@8 +BasepSetFileEncryptionCompression@32 +Beep@8 +BeginUpdateResourceA@8 +BeginUpdateResourceW@8 +BindIoCompletionCallback@12 +BuildCommDCBA@8 +BuildCommDCBAndTimeoutsA@12 +BuildCommDCBAndTimeoutsW@12 +BuildCommDCBW@8 +CallNamedPipeA@28 +CallNamedPipeW@28 +CallbackMayRunLong@4 +CancelDeviceWakeupRequest@4 +CancelIo@4 +CancelIoEx@8 +CancelSynchronousIo@4 +CancelThreadpoolIo@4 +CancelTimerQueueTimer@8 +CancelWaitableTimer@4 +CeipIsOptedIn@0 +ChangeTimerQueueTimer@16 +CheckAllowDecryptedRemoteDestinationPolicy@0 +CheckElevation@20 +CheckElevationEnabled@4 +CheckForReadOnlyResource@8 +CheckForReadOnlyResourceFilter@4 +CheckNameLegalDOS8Dot3A@20 +CheckNameLegalDOS8Dot3W@20 +CheckRemoteDebuggerPresent@8 +CheckTokenCapability@12 +CheckTokenMembershipEx@16 +ClearCommBreak@4 +ClearCommError@12 +CloseConsoleHandle@4 +CloseHandle@4 +ClosePackageInfo@4 +ClosePrivateNamespace@8 +CloseProfileUserMapping@0 +ClosePseudoConsole@4 +CloseState@4 +CloseThreadpool@4 +CloseThreadpoolCleanupGroup@4 +CloseThreadpoolCleanupGroupMembers@12 +CloseThreadpoolIo@4 +CloseThreadpoolTimer@4 +CloseThreadpoolWait@4 +CloseThreadpoolWork@4 +CmdBatNotification@4 +CommConfigDialogA@12 +CommConfigDialogW@12 +CompareCalendarDates@12 +CompareFileTime@8 +CompareStringA@24 +CompareStringEx@36 +CompareStringOrdinal@20 +CompareStringW@24 +ConnectNamedPipe@8 +ConsoleIMERoutine@4 +ConsoleMenuControl@12 +ContinueDebugEvent@12 +ConvertCalDateTimeToSystemTime@8 +ConvertDefaultLocale@4 +ConvertFiberToThread@0 +ConvertNLSDayOfWeekToWin32DayOfWeek@4 +ConvertSystemTimeToCalDateTime@12 +ConvertThreadToFiber@4 +ConvertThreadToFiberEx@8 +ConvertToGlobalHandle@4 +CopyContext@12 +CopyFile2@12 +CopyFileA@12 +CopyFileExA@24 +CopyFileExW@24 +CopyFileTransactedA@28 +CopyFileTransactedW@28 +CopyFileW@12 +CopyLZFile@8 +CreateActCtxA@4 +CreateActCtxW@4 +CreateActCtxWWorker@4 +CreateBoundaryDescriptorA@8 +CreateBoundaryDescriptorW@8 +CreateConsoleScreenBuffer@20 +CreateDirectoryA@8 +CreateDirectoryExA@12 +CreateDirectoryExW@12 +CreateDirectoryTransactedA@16 +CreateDirectoryTransactedW@16 +CreateDirectoryW@8 +CreateEnclave@32 +CreateEventA@16 +CreateEventExA@16 +CreateEventExW@16 +CreateEventW@16 +CreateFiber@12 +CreateFiberEx@20 +CreateFile2@20 +CreateFileA@28 +CreateFileMappingA@24 +CreateFileMappingFromApp@24 +CreateFileMappingNumaA@28 +CreateFileMappingNumaW@28 +CreateFileMappingW@24 +CreateFileTransactedA@40 +CreateFileTransactedW@40 +CreateFileW@28 +CreateHardLinkA@12 +CreateHardLinkTransactedA@16 +CreateHardLinkTransactedW@16 +CreateHardLinkW@12 +CreateIoCompletionPort@16 +CreateJobObjectA@8 +CreateJobObjectW@8 +CreateJobSet@12 +CreateMailslotA@16 +CreateMailslotW@16 +CreateMemoryResourceNotification@4 +CreateMutexA@12 +CreateMutexExA@16 +CreateMutexExW@16 +CreateMutexW@12 +CreateNamedPipeA@32 +CreateNamedPipeW@32 +CreatePipe@16 +CreatePrivateNamespaceA@12 +CreatePrivateNamespaceW@12 +CreateProcessA@40 +CreateProcessAsUserA@44 +CreateProcessAsUserW@44 +CreateProcessInternalA@48 +CreateProcessInternalW@48 +CreateProcessW@40 +CreatePseudoConsole@20 +CreateRemoteThread@28 +CreateRemoteThreadEx@32 +CreateSemaphoreA@16 +CreateSemaphoreExA@24 +CreateSemaphoreExW@24 +CreateSemaphoreW@16 +CreateSocketHandle@0 +CreateSymbolicLinkA@12 +CreateSymbolicLinkTransactedA@16 +CreateSymbolicLinkTransactedW@16 +CreateSymbolicLinkW@12 +CreateTapePartition@16 +CreateThread@24 +CreateThreadpool@4 +CreateThreadpoolCleanupGroup@0 +CreateThreadpoolIo@16 +CreateThreadpoolTimer@12 +CreateThreadpoolWait@12 +CreateThreadpoolWork@12 +CreateTimerQueue@0 +CreateTimerQueueTimer@28 +CreateToolhelp32Snapshot@8 +CreateVirtualBuffer@12 +CreateWaitableTimerA@12 +CreateWaitableTimerExA@16 +CreateWaitableTimerExW@16 +CreateWaitableTimerW@12 +CtrlRoutine@4 +DeactivateActCtx@8 +DeactivateActCtxWorker@8 +DebugActiveProcess@4 +DebugActiveProcessStop@4 +DebugBreak@0 +DebugBreakProcess@4 +DebugSetProcessKillOnExit@4 +DecodePointer@4 +DecodeSystemPointer@4 +DefineDosDeviceA@12 +DefineDosDeviceW@12 +DelayLoadFailureHook@8 +DeleteAtom@4 +DeleteBoundaryDescriptor@4 +DeleteCriticalSection@4 +DeleteFiber@4 +DeleteFileA@4 +DeleteFileTransactedA@8 +DeleteFileTransactedW@8 +DeleteFileW@4 +DeleteProcThreadAttributeList@4 +DeleteSynchronizationBarrier@4 +DeleteTimerQueue@4 +DeleteTimerQueueEx@8 +DeleteTimerQueueTimer@12 +DeleteVolumeMountPointA@4 +DeleteVolumeMountPointW@4 +DeviceIoControl@32 +DisableThreadLibraryCalls@4 +DisableThreadProfiling@4 +DisassociateCurrentThreadFromCallback@4 +DiscardVirtualMemory@8 +DisconnectNamedPipe@4 +DnsHostnameToComputerNameA@12 +DnsHostnameToComputerNameExW@12 +DnsHostnameToComputerNameW@12 +DosDateTimeToFileTime@12 +DosPathToSessionPathA@12 +DosPathToSessionPathW@12 +DuplicateConsoleHandle@16 +DuplicateEncryptionInfoFileExt@20 +DuplicateHandle@28 +EnableThreadProfiling@20 +EncodePointer@4 +EncodeSystemPointer@4 +EndUpdateResourceA@8 +EndUpdateResourceW@8 +EnterCriticalSection@4 +EnterSynchronizationBarrier@8 +EnumCalendarInfoA@16 +EnumCalendarInfoExA@16 +EnumCalendarInfoExEx@24 +EnumCalendarInfoExW@16 +EnumCalendarInfoW@16 +EnumDateFormatsA@12 +EnumDateFormatsExA@12 +EnumDateFormatsExEx@16 +EnumDateFormatsExW@12 +EnumDateFormatsW@12 +EnumLanguageGroupLocalesA@16 +EnumLanguageGroupLocalesW@16 +EnumResourceLanguagesA@20 +EnumResourceLanguagesExA@28 +EnumResourceLanguagesExW@28 +EnumResourceLanguagesW@20 +EnumResourceNamesA@16 +EnumResourceNamesExA@24 +EnumResourceNamesExW@24 +EnumResourceNamesW@16 +EnumResourceTypesA@12 +EnumResourceTypesExA@20 +EnumResourceTypesExW@20 +EnumResourceTypesW@12 +EnumSystemCodePagesA@8 +EnumSystemCodePagesW@8 +EnumSystemFirmwareTables@12 +EnumSystemGeoID@12 +EnumSystemGeoNames@12 +EnumSystemLanguageGroupsA@12 +EnumSystemLanguageGroupsW@12 +EnumSystemLocalesA@8 +EnumSystemLocalesEx@16 +EnumSystemLocalesW@8 +EnumTimeFormatsA@12 +EnumTimeFormatsEx@16 +EnumTimeFormatsW@12 +EnumUILanguagesA@12 +EnumUILanguagesW@12 +EnumerateLocalComputerNamesA@16 +EnumerateLocalComputerNamesW@16 +EraseTape@12 +EscapeCommFunction@8 +ExitProcess@4 +ExitThread@4 +ExitVDM@8 +ExpandEnvironmentStringsA@12 +ExpandEnvironmentStringsW@12 +ExpungeConsoleCommandHistoryA@4 +ExpungeConsoleCommandHistoryW@4 +ExtendVirtualBuffer@8 +FatalAppExitA@8 +FatalAppExitW@8 +FatalExit@4 +FileTimeToDosDateTime@12 +FileTimeToLocalFileTime@8 +FileTimeToSystemTime@8 +FillConsoleOutputAttribute@20 +FillConsoleOutputCharacterA@20 +FillConsoleOutputCharacterW@20 +FindActCtxSectionGuid@20 +FindActCtxSectionGuidWorker@20 +FindActCtxSectionStringA@20 +FindActCtxSectionStringW@20 +FindActCtxSectionStringWWorker@20 +FindAtomA@4 +FindAtomW@4 +FindClose@4 +FindCloseChangeNotification@4 +FindFirstChangeNotificationA@12 +FindFirstChangeNotificationW@12 +FindFirstFileA@8 +FindFirstFileExA@24 +FindFirstFileExW@24 +FindFirstFileNameTransactedW@20 +FindFirstFileNameW@16 +FindFirstFileTransactedA@28 +FindFirstFileTransactedW@28 +FindFirstFileW@8 +FindFirstStreamTransactedW@20 +FindFirstStreamW@16 +FindFirstVolumeA@8 +FindFirstVolumeMountPointA@12 +FindFirstVolumeMountPointW@12 +FindFirstVolumeW@8 +FindNLSString@28 +FindNLSStringEx@40 +FindNextChangeNotification@4 +FindNextFileA@8 +FindNextFileNameW@12 +FindNextFileW@8 +FindNextStreamW@8 +FindNextVolumeA@12 +FindNextVolumeMountPointA@12 +FindNextVolumeMountPointW@12 +FindNextVolumeW@12 +FindPackagesByPackageFamily@28 +FindResourceA@12 +FindResourceExA@16 +FindResourceExW@16 +FindResourceW@12 +FindStringOrdinal@24 +FindVolumeClose@4 +FindVolumeMountPointClose@4 +FlsAlloc@4 +FlsFree@4 +FlsGetValue@4 +FlsSetValue@8 +FlushConsoleInputBuffer@4 +FlushFileBuffers@4 +FlushInstructionCache@12 +FlushProcessWriteBuffers@0 +FlushViewOfFile@8 +FoldStringA@20 +FoldStringW@20 +FormatApplicationUserModelId@16 +FormatMessageA@28 +FormatMessageW@28 +FreeConsole@0 +FreeEnvironmentStringsA@4 +FreeEnvironmentStringsW@4 +FreeLibrary@4 +FreeLibraryAndExitThread@8 +FreeLibraryWhenCallbackReturns@8 +FreeMemoryJobObject@4 +FreeResource@4 +FreeUserPhysicalPages@12 +FreeVirtualBuffer@4 +GenerateConsoleCtrlEvent@8 +GetACP@0 +GetActiveProcessorCount@4 +GetActiveProcessorGroupCount@0 +GetAppContainerAce@16 +GetAppContainerNamedObjectPath@20 +GetApplicationRecoveryCallback@20 +GetApplicationRecoveryCallbackWorker@20 +GetApplicationRestartSettings@16 +GetApplicationRestartSettingsWorker@16 +GetApplicationUserModelId@12 +GetAtomNameA@12 +GetAtomNameW@12 +GetBinaryType@8 +GetBinaryTypeA@8 +GetBinaryTypeW@8 +GetCPFileNameFromRegistry@12 +GetCPInfo@8 +GetCPInfoExA@12 +GetCPInfoExW@12 +GetCachedSigningLevel@24 +GetCalendarDateFormat@24 +GetCalendarDateFormatEx@24 +GetCalendarDaysInMonth@16 +GetCalendarDifferenceInDays@12 +GetCalendarInfoA@24 +GetCalendarInfoEx@28 +GetCalendarInfoW@24 +GetCalendarMonthsInYear@12 +GetCalendarSupportedDateRange@12 +GetCalendarWeekNumber@16 +GetComPlusPackageInstallStatus@0 +GetCommConfig@12 +GetCommMask@8 +GetCommModemStatus@8 +GetCommProperties@8 +GetCommState@8 +GetCommTimeouts@8 +GetCommandLineA@0 +GetCommandLineW@0 +GetCompressedFileSizeA@8 +GetCompressedFileSizeTransactedA@12 +GetCompressedFileSizeTransactedW@12 +GetCompressedFileSizeW@8 +GetComputerNameA@8 +GetComputerNameExA@12 +GetComputerNameExW@12 +GetComputerNameW@8 +GetConsoleAliasA@16 +GetConsoleAliasExesA@8 +GetConsoleAliasExesLengthA@0 +GetConsoleAliasExesLengthW@0 +GetConsoleAliasExesW@8 +GetConsoleAliasW@16 +GetConsoleAliasesA@12 +GetConsoleAliasesLengthA@4 +GetConsoleAliasesLengthW@4 +GetConsoleAliasesW@12 +GetConsoleCP@0 +GetConsoleCharType@12 +GetConsoleCommandHistoryA@12 +GetConsoleCommandHistoryLengthA@4 +GetConsoleCommandHistoryLengthW@4 +GetConsoleCommandHistoryW@12 +GetConsoleCursorInfo@8 +GetConsoleCursorMode@12 +GetConsoleDisplayMode@4 +GetConsoleFontInfo@16 +GetConsoleFontSize@8 +GetConsoleHardwareState@12 +GetConsoleHistoryInfo@4 +GetConsoleInputExeNameA@8 +GetConsoleInputExeNameW@8 +GetConsoleInputWaitHandle@0 +GetConsoleKeyboardLayoutNameA@4 +GetConsoleKeyboardLayoutNameW@4 +GetConsoleMode@8 +GetConsoleNlsMode@8 +GetConsoleOriginalTitleA@8 +GetConsoleOriginalTitleW@8 +GetConsoleOutputCP@0 +GetConsoleProcessList@8 +GetConsoleScreenBufferInfo@8 +GetConsoleScreenBufferInfoEx@8 +GetConsoleSelectionInfo@4 +GetConsoleTitleA@8 +GetConsoleTitleW@8 +GetConsoleWindow@0 +GetCurrencyFormatA@24 +GetCurrencyFormatEx@24 +GetCurrencyFormatW@24 +GetCurrentActCtx@4 +GetCurrentActCtxWorker@4 +GetCurrentApplicationUserModelId@8 +GetCurrentConsoleFont@12 +GetCurrentConsoleFontEx@12 +GetCurrentDirectoryA@8 +GetCurrentDirectoryW@8 +GetCurrentPackageFamilyName@8 +GetCurrentPackageFullName@8 +GetCurrentPackageId@8 +GetCurrentPackageInfo@16 +GetCurrentPackagePath@8 +GetCurrentProcess@0 +GetCurrentProcessId@0 +GetCurrentProcessorNumber@0 +GetCurrentProcessorNumberEx@4 +GetCurrentThread@0 +GetCurrentThreadId@0 +GetCurrentThreadStackLimits@8 +GetDateFormatA@24 +GetDateFormatAWorker@28 +GetDateFormatEx@28 +GetDateFormatW@24 +GetDateFormatWWorker@28 +GetDefaultCommConfigA@12 +GetDefaultCommConfigW@12 +GetDevicePowerState@8 +GetDiskFreeSpaceA@20 +GetDiskFreeSpaceExA@16 +GetDiskFreeSpaceExW@16 +GetDiskFreeSpaceW@20 +GetDiskSpaceInformationA@8 +GetDiskSpaceInformationW@8 +GetDllDirectoryA@8 +GetDllDirectoryW@8 +GetDriveTypeA@4 +GetDriveTypeW@4 +GetDurationFormat@32 +GetDurationFormatEx@32 +GetDynamicTimeZoneInformation@4 +GetEnabledXStateFeatures@0 +GetEncryptedFileVersionExt@8 +GetEnvironmentStrings@0 +GetEnvironmentStringsA@0 +GetEnvironmentStringsW@0 +GetEnvironmentVariableA@12 +GetEnvironmentVariableW@12 +GetEraNameCountedString@16 +GetErrorMode@0 +GetExitCodeProcess@8 +GetExitCodeThread@8 +GetExpandedNameA@8 +GetExpandedNameW@8 +GetFileAttributesA@4 +GetFileAttributesExA@12 +GetFileAttributesExW@12 +GetFileAttributesTransactedA@16 +GetFileAttributesTransactedW@16 +GetFileAttributesW@4 +GetFileBandwidthReservation@24 +GetFileInformationByHandle@8 +GetFileInformationByHandleEx@16 +GetFileMUIInfo@16 +GetFileMUIPath@28 +GetFileSize@8 +GetFileSizeEx@8 +GetFileTime@16 +GetFileType@4 +GetFinalPathNameByHandleA@16 +GetFinalPathNameByHandleW@16 +GetFirmwareEnvironmentVariableA@16 +GetFirmwareEnvironmentVariableExA@20 +GetFirmwareEnvironmentVariableExW@20 +GetFirmwareEnvironmentVariableW@16 +GetFirmwareType@4 +GetFullPathNameA@16 +GetFullPathNameTransactedA@20 +GetFullPathNameTransactedW@20 +GetFullPathNameW@16 +GetGeoInfoA@20 +GetGeoInfoEx@16 +GetGeoInfoW@20 +GetHandleContext@4 +GetHandleInformation@8 +GetLargePageMinimum@0 +GetLargestConsoleWindowSize@4 +GetLastError@0 +GetLocalTime@4 +GetLocaleInfoA@16 +GetLocaleInfoEx@16 +GetLocaleInfoW@16 +GetLogicalDriveStringsA@8 +GetLogicalDriveStringsW@8 +GetLogicalDrives@0 +GetLogicalProcessorInformation@8 +GetLogicalProcessorInformationEx@12 +GetLongPathNameA@12 +GetLongPathNameTransactedA@16 +GetLongPathNameTransactedW@16 +GetLongPathNameW@12 +GetMailslotInfo@20 +GetMaximumProcessorCount@4 +GetMaximumProcessorGroupCount@0 +GetMemoryErrorHandlingCapabilities@4 +GetModuleFileNameA@12 +GetModuleFileNameW@12 +GetModuleHandleA@4 +GetModuleHandleExA@12 +GetModuleHandleExW@12 +GetModuleHandleW@4 +GetNLSVersion@12 +GetNLSVersionEx@12 +GetNamedPipeAttribute@20 +GetNamedPipeClientComputerNameA@12 +GetNamedPipeClientComputerNameW@12 +GetNamedPipeClientProcessId@8 +GetNamedPipeClientSessionId@8 +GetNamedPipeHandleStateA@28 +GetNamedPipeHandleStateW@28 +GetNamedPipeInfo@20 +GetNamedPipeServerProcessId@8 +GetNamedPipeServerSessionId@8 +GetNativeSystemInfo@4 +GetNextVDMCommand@4 +GetNumaAvailableMemoryNode@8 +GetNumaAvailableMemoryNodeEx@8 +GetNumaHighestNodeNumber@4 +GetNumaNodeNumberFromHandle@8 +GetNumaNodeProcessorMask@8 +GetNumaNodeProcessorMaskEx@8 +GetNumaProcessorNode@8 +GetNumaProcessorNodeEx@8 +GetNumaProximityNode@8 +GetNumaProximityNodeEx@8 +GetNumberFormatA@24 +GetNumberFormatEx@24 +GetNumberFormatW@24 +GetNumberOfConsoleFonts@0 +GetNumberOfConsoleInputEvents@8 +GetNumberOfConsoleMouseButtons@4 +GetOEMCP@0 +GetOverlappedResult@16 +GetOverlappedResultEx@20 +GetPackageApplicationIds@16 +GetPackageFamilyName@12 +GetPackageFullName@12 +GetPackageId@12 +GetPackageInfo@20 +GetPackagePath@24 +GetPackagePathByFullName@12 +GetPackagesByPackageFamily@20 +GetPhysicallyInstalledSystemMemory@4 +GetPriorityClass@4 +GetPrivateProfileIntA@16 +GetPrivateProfileIntW@16 +GetPrivateProfileSectionA@16 +GetPrivateProfileSectionNamesA@12 +GetPrivateProfileSectionNamesW@12 +GetPrivateProfileSectionW@16 +GetPrivateProfileStringA@24 +GetPrivateProfileStringW@24 +GetPrivateProfileStructA@20 +GetPrivateProfileStructW@20 +GetProcAddress@8 +GetProcessAffinityMask@12 +GetProcessDEPPolicy@12 +GetProcessDefaultCpuSets@16 +GetProcessGroupAffinity@12 +GetProcessHandleCount@8 +GetProcessHeap@0 +GetProcessHeaps@8 +GetProcessId@4 +GetProcessIdOfThread@4 +GetProcessInformation@16 +GetProcessIoCounters@8 +GetProcessMitigationPolicy@16 +GetProcessPreferredUILanguages@16 +GetProcessPriorityBoost@8 +GetProcessShutdownParameters@8 +GetProcessTimes@20 +GetProcessUserModeExceptionPolicy@4 +GetProcessVersion@4 +GetProcessWorkingSetSize@12 +GetProcessWorkingSetSizeEx@16 +GetProcessorSystemCycleTime@12 +GetProductInfo@20 +GetProductName@8 +GetProfileIntA@12 +GetProfileIntW@12 +GetProfileSectionA@12 +GetProfileSectionW@12 +GetProfileStringA@20 +GetProfileStringW@20 +GetQueuedCompletionStatus@20 +GetQueuedCompletionStatusEx@24 +GetShortPathNameA@12 +GetShortPathNameW@12 +GetStagedPackagePathByFullName@12 +GetStartupInfoA@4 +GetStartupInfoW@4 +GetStateFolder@16 +GetStdHandle@4 +GetStringScripts@20 +GetStringTypeA@20 +GetStringTypeExA@20 +GetStringTypeExW@20 +GetStringTypeW@16 +GetSystemAppDataKey@16 +GetSystemCpuSetInformation@20 +GetSystemDEPPolicy@0 +GetSystemDefaultLCID@0 +GetSystemDefaultLangID@0 +GetSystemDefaultLocaleName@8 +GetSystemDefaultUILanguage@0 +GetSystemDirectoryA@8 +GetSystemDirectoryW@8 +GetSystemFileCacheSize@12 +GetSystemFirmwareTable@16 +GetSystemInfo@4 +GetSystemPowerStatus@4 +GetSystemPreferredUILanguages@16 +GetSystemRegistryQuota@8 +GetSystemTime@4 +GetSystemTimeAdjustment@12 +GetSystemTimeAsFileTime@4 +GetSystemTimePreciseAsFileTime@4 +GetSystemTimes@12 +GetSystemWindowsDirectoryA@8 +GetSystemWindowsDirectoryW@8 +GetSystemWow64DirectoryA@8 +GetSystemWow64DirectoryW@8 +GetTapeParameters@16 +GetTapePosition@20 +GetTapeStatus@4 +GetTempFileNameA@16 +GetTempFileNameW@16 +GetTempPathA@8 +GetTempPathW@8 +GetThreadContext@8 +GetThreadDescription@8 +GetThreadErrorMode@0 +GetThreadGroupAffinity@8 +GetThreadIOPendingFlag@8 +GetThreadId@4 +GetThreadIdealProcessorEx@8 +GetThreadInformation@16 +GetThreadLocale@0 +GetThreadPreferredUILanguages@16 +GetThreadPriority@4 +GetThreadPriorityBoost@8 +GetThreadSelectedCpuSets@16 +GetThreadSelectorEntry@12 +GetThreadTimes@20 +GetThreadUILanguage@0 +GetTickCount64@0 +GetTickCount@0 +GetTimeFormatA@24 +GetTimeFormatAWorker@28 +GetTimeFormatEx@24 +GetTimeFormatW@24 +GetTimeFormatWWorker@24 +GetTimeZoneInformation@4 +GetTimeZoneInformationForYear@12 +GetUILanguageInfo@20 +GetUserDefaultGeoName@8 +GetUserDefaultLCID@0 +GetUserDefaultLangID@0 +GetUserDefaultLocaleName@8 +GetUserDefaultUILanguage@0 +GetUserGeoID@4 +GetUserPreferredUILanguages@16 +GetVDMCurrentDirectories@8 +GetVersion@0 +GetVersionExA@4 +GetVersionExW@4 +GetVolumeInformationA@32 +GetVolumeInformationByHandleW@32 +GetVolumeInformationW@32 +GetVolumeNameForVolumeMountPointA@12 +GetVolumeNameForVolumeMountPointW@12 +GetVolumePathNameA@12 +GetVolumePathNameW@12 +GetVolumePathNamesForVolumeNameA@16 +GetVolumePathNamesForVolumeNameW@16 +GetWindowsDirectoryA@8 +GetWindowsDirectoryW@8 +GetWriteWatch@24 +GetXStateFeaturesMask@8 +GlobalAddAtomA@4 +GlobalAddAtomExA@8 +GlobalAddAtomExW@8 +GlobalAddAtomW@4 +GlobalAlloc@8 +GlobalCompact@4 +GlobalDeleteAtom@4 +GlobalFindAtomA@4 +GlobalFindAtomW@4 +GlobalFix@4 +GlobalFlags@4 +GlobalFree@4 +GlobalGetAtomNameA@12 +GlobalGetAtomNameW@12 +GlobalHandle@4 +GlobalLock@4 +GlobalMemoryStatus@4 +GlobalMemoryStatusEx@4 +GlobalMemoryStatusVlm@4 +GlobalReAlloc@12 +GlobalSize@4 +GlobalUnWire@4 +GlobalUnfix@4 +GlobalUnlock@4 +GlobalWire@4 +Heap32First@12 +Heap32ListFirst@8 +Heap32ListNext@8 +Heap32Next@4 +HeapAlloc@12 +HeapCompact@8 +HeapCreate@12 +HeapCreateTagsW@16 +HeapDestroy@4 +HeapExtend@16 +HeapFree@12 +HeapLock@4 +HeapQueryInformation@20 +HeapQueryTagW@20 +HeapReAlloc@16 +HeapSetInformation@16 +HeapSize@12 +HeapSummary@12 +HeapUnlock@4 +HeapUsage@20 +HeapValidate@12 +HeapWalk@8 +IdnToAscii@20 +IdnToNameprepUnicode@20 +IdnToUnicode@20 +InitAtomTable@4 +InitializeCriticalSection@4 +InitOnceBeginInitialize@16 +InitOnceComplete@12 +InitOnceExecuteOnce@16 +InitOnceInitialize@4 +InitializeConditionVariable@4 +InitializeContext2@24 +InitializeContext@16 +InitializeCriticalSection@4 +InitializeCriticalSectionAndSpinCount@8 +InitializeCriticalSectionEx@12 +InitializeEnclave@20 +InitializeProcThreadAttributeList@16 +InitializeSListHead@4 +InitializeSRWLock@4 +InitializeSynchronizationBarrier@12 +InstallELAMCertificateInfo@4 +InterlockedCompareExchange64@20 DATA ; FIXME: this is for Vista+. forwards to NTDLL.RtlInterlockedCompareExchange64@20 +InterlockedCompareExchange@12 DATA +InterlockedDecrement@4 DATA +InterlockedExchange@8 DATA +InterlockedExchangeAdd@8 DATA +InterlockedFlushSList@4 +InterlockedIncrement@4 DATA +InterlockedPopEntrySList@4 +InterlockedPushEntrySList@8 +InterlockedPushListSListEx@16 +InvalidateConsoleDIBits@8 +IsBadCodePtr@4 +IsBadHugeReadPtr@8 +IsBadHugeWritePtr@8 +IsBadReadPtr@8 +IsBadStringPtrA@8 +IsBadStringPtrW@8 +IsBadWritePtr@8 +IsCalendarLeapDay@20 +IsCalendarLeapMonth@16 +IsCalendarLeapYear@12 +IsDBCSLeadByte@4 +IsDBCSLeadByteEx@8 +IsDebuggerPresent@0 +IsEnclaveTypeSupported@4 +IsNLSDefinedString@20 +IsNativeVhdBoot@4 +IsNormalizedString@12 +IsProcessCritical@8 +IsProcessInJob@12 +IsProcessorFeaturePresent@4 +IsSystemResumeAutomatic@0 +IsThreadAFiber@0 +IsThreadpoolTimerSet@4 +IsTimeZoneRedirectionEnabled@0 +IsValidCalDateTime@8 +IsValidCodePage@4 +IsValidLanguageGroup@8 +IsValidLocale@8 +IsValidLocaleName@4 +IsValidNLSVersion@12 +IsWow64GuestMachineSupported@8 +IsWow64Process2@12 +IsWow64Process@8 +K32EmptyWorkingSet@4 +K32EnumDeviceDrivers@12 +K32EnumPageFilesA@8 +K32EnumPageFilesW@8 +K32EnumProcessModules@16 +K32EnumProcessModulesEx@20 +K32EnumProcesses@12 +K32GetDeviceDriverBaseNameA@12 +K32GetDeviceDriverBaseNameW@12 +K32GetDeviceDriverFileNameA@12 +K32GetDeviceDriverFileNameW@12 +K32GetMappedFileNameA@16 +K32GetMappedFileNameW@16 +K32GetModuleBaseNameA@16 +K32GetModuleBaseNameW@16 +K32GetModuleFileNameExA@16 +K32GetModuleFileNameExW@16 +K32GetModuleInformation@16 +K32GetPerformanceInfo@8 +K32GetProcessImageFileNameA@12 +K32GetProcessImageFileNameW@12 +K32GetProcessMemoryInfo@12 +K32GetWsChanges@12 +K32GetWsChangesEx@12 +K32InitializeProcessForWsWatch@4 +K32QueryWorkingSet@12 +K32QueryWorkingSetEx@12 +LCIDToLocaleName@16 +LCMapStringA@24 +LCMapStringEx@36 +LCMapStringW@24 +LZClose@4 +LZCloseFile@4 +LZCopy@8 +LZCreateFileW@20 +LZDone@0 +LZInit@4 +LZOpenFileA@12 +LZOpenFileW@12 +LZRead@12 +LZSeek@12 +LZStart@0 +LeaveCriticalSection@4 +LeaveCriticalSectionWhenCallbackReturns@8 +LoadAppInitDlls@0 +LoadEnclaveData@36 +LoadLibraryA@4 +LoadLibraryExA@12 +LoadLibraryExW@12 +LoadLibraryW@4 +LoadModule@8 +LoadPackagedLibrary@8 +LoadResource@8 +LoadStringBaseExW@20 +LoadStringBaseW@16 +LocalAlloc@8 +LocalCompact@4 +LocalFileTimeToFileTime@8 +LocalFileTimeToLocalSystemTime@12 +LocalFlags@4 +LocalFree@4 +LocalHandle@4 +LocalLock@4 +LocalReAlloc@12 +LocalShrink@8 +LocalSize@4 +LocalSystemTimeToLocalFileTime@12 +LocalUnlock@4 +LocaleNameToLCID@8 +LocateXStateFeature@12 +LockFile@20 +LockFileEx@24 +LockResource@4 +MapUserPhysicalPages@12 +MapUserPhysicalPagesScatter@12 +MapViewOfFile@20 +MapViewOfFileEx@24 +MapViewOfFileExNuma@28 +MapViewOfFileVlm@28 +MapViewOfFileFromApp@20 +Module32First@8 +Module32FirstW@8 +Module32Next@8 +Module32NextW@8 +MoveFileA@8 +MoveFileExA@12 +MoveFileExW@12 +MoveFileTransactedA@24 +MoveFileTransactedW@24 +MoveFileW@8 +MoveFileWithProgressA@20 +MoveFileWithProgressW@20 +MulDiv@12 +MultiByteToWideChar@24 +NeedCurrentDirectoryForExePathA@4 +NeedCurrentDirectoryForExePathW@4 +NlsCheckPolicy@8 +NlsConvertIntegerToString@20 +NlsEventDataDescCreate@16 +NlsGetCacheUpdateCount@0 +NlsUpdateLocale@8 +NlsUpdateSystemLocale@8 +NlsWriteEtwEvent@20 +NormalizeString@20 +NotifyMountMgr@12 +NotifyUILanguageChange@20 +NtVdm64CreateProcessInternalW@48 +OOBEComplete@4 +OfferVirtualMemory@12 +OpenConsoleW@16 +OpenConsoleWStub@16 +OpenEventA@12 +OpenEventW@12 +OpenFile@12 +OpenFileById@24 +OpenFileMappingA@12 +OpenFileMappingW@12 +OpenJobObjectA@12 +OpenJobObjectW@12 +OpenMutexA@12 +OpenMutexW@12 +OpenPackageInfoByFullName@12 +OpenPrivateNamespaceA@8 +OpenPrivateNamespaceW@8 +OpenProcess@12 +; MSDN says OpenProcessToken is from Advapi32.dll, not Kernel32.dll +; OpenProcessToken@12 +OpenProfileUserMapping@0 +OpenSemaphoreA@12 +OpenSemaphoreW@12 +OpenState@0 +OpenStateExplicit@8 +OpenThread@12 +OpenThreadToken@16 +OpenWaitableTimerA@12 +OpenWaitableTimerW@12 +OutputDebugStringA@4 +OutputDebugStringW@4 +PackageFamilyNameFromFullName@12 +PackageFamilyNameFromId@12 +PackageFullNameFromId@12 +PackageIdFromFullName@16 +PackageNameAndPublisherIdFromFamilyName@20 +ParseApplicationUserModelId@20 +PeekConsoleInputA@16 +PeekConsoleInputW@16 +PeekNamedPipe@24 +PostQueuedCompletionStatus@16 +PowerClearRequest@8 +PowerCreateRequest@4 +PowerSetRequest@8 +PrefetchVirtualMemory@16 +PrepareTape@12 +PrivCopyFileExW@24 +PrivMoveFileIdentityW@12 +Process32First@8 +Process32FirstW@8 +Process32Next@8 +Process32NextW@8 +ProcessIdToSessionId@8 +PssCaptureSnapshot@16 +PssDuplicateSnapshot@20 +PssFreeSnapshot@8 +PssQuerySnapshot@16 +PssWalkMarkerCreate@8 +PssWalkMarkerFree@4 +PssWalkMarkerGetPosition@8 +PssWalkMarkerRewind@4 +PssWalkMarkerSeek@8 +PssWalkMarkerSeekToBeginning@4 +PssWalkMarkerSetPosition@8 +PssWalkMarkerTell@8 +PssWalkSnapshot@20 +PulseEvent@4 +PurgeComm@8 +QueryActCtxSettingsW@28 +QueryActCtxSettingsWWorker@28 +QueryActCtxW@28 +QueryActCtxWWorker@28 +QueryDepthSList@4 +QueryDosDeviceA@12 +QueryDosDeviceW@12 +QueryFullProcessImageNameA@16 +QueryFullProcessImageNameW@16 +QueryIdleProcessorCycleTime@8 +QueryIdleProcessorCycleTimeEx@12 +QueryInformationJobObject@20 +QueryIoRateControlInformationJobObject@16 +QueryMemoryResourceNotification@8 +QueryPerformanceCounter@4 +QueryPerformanceFrequency@4 +QueryProcessAffinityUpdateMode@8 +QueryProcessCycleTime@8 +QueryProtectedPolicy@8 +QueryThreadCycleTime@8 +QueryThreadProfiling@8 +QueryThreadpoolStackInformation@8 +QueryUnbiasedInterruptTime@4 +QueueUserAPC@12 +QueueUserWorkItem@12 +QueryWin31IniFilesMappedToRegistry@16 +QuirkGetData2Worker@8 +QuirkGetDataWorker@8 +QuirkIsEnabled2Worker@12 +QuirkIsEnabled3Worker@8 +QuirkIsEnabledForPackage2Worker@24 +QuirkIsEnabledForPackage3Worker@20 +QuirkIsEnabledForPackage4Worker@20 +QuirkIsEnabledForPackageWorker@16 +QuirkIsEnabledForProcessWorker@12 +QuirkIsEnabledWorker@4 +RaiseException@16 +RaiseFailFastException@12 +RaiseInvalid16BitExeError@4 +ReOpenFile@16 +ReclaimVirtualMemory@8 +ReadConsoleA@20 +ReadConsoleInputA@16 +ReadConsoleInputExA@20 +ReadConsoleInputExW@20 +ReadConsoleInputW@16 +ReadConsoleOutputA@20 +ReadConsoleOutputAttribute@20 +ReadConsoleOutputCharacterA@20 +ReadConsoleOutputCharacterW@20 +ReadConsoleOutputW@20 +ReadConsoleW@20 +ReadDirectoryChangesExW@36 +ReadDirectoryChangesW@32 +ReadFile@20 +ReadFileEx@20 +ReadFileScatter@20 +ReadFileVlm@20 +ReadProcessMemory@20 +ReadThreadProfilingData@12 +ReclaimVirtualMemory@8 +; +; MSDN says these functions are exported +; from advapi32.dll. Commented out for +; compatibility with older versions of +; Windows. +; +; RegKrnGetGlobalState and RegKrnInitialize +; are known exceptions. +; +;RegCloseKey@4 +;RegCopyTreeW@12 +;RegCreateKeyExA@36 +;RegCreateKeyExW@36 +;RegDeleteKeyExA@16 +;RegDeleteKeyExW@16 +;RegDeleteTreeA@8 +;RegDeleteTreeW@8 +;RegDeleteValueA@8 +;RegDeleteValueW@8 +;RegDisablePredefinedCacheEx@0 +;RegEnumKeyExA@32 +;RegEnumKeyExW@32 +;RegEnumValueA@32 +;RegEnumValueW@32 +;RegFlushKey@4 +;RegGetKeySecurity@16 +;RegGetValueA@28 +;RegGetValueW@28 +;RegLoadKeyA@12 +;RegLoadKeyW@12 +;RegLoadMUIStringA@28 +;RegLoadMUIStringW@28 +;RegNotifyChangeKeyValue@20 +;RegOpenCurrentUser@8 +;RegOpenKeyExA@20 +;RegOpenKeyExW@20 +;RegOpenUserClassesRoot@16 +;RegQueryInfoKeyA@48 +;RegQueryInfoKeyW@48 +;RegQueryValueExA@24 +;RegQueryValueExW@24 +;RegRestoreKeyA@12 +;RegRestoreKeyW@12 +;RegSaveKeyExA@16 +;RegSaveKeyExW@16 +;RegSetKeySecurity@12 +;RegSetValueExA@24 +;RegSetValueExW@24 +;RegUnLoadKeyA@8 +;RegUnLoadKeyW@8 +RegisterApplicationRecoveryCallback@16 +RegisterApplicationRestart@8 +RegisterBadMemoryNotification@4 +RegisterConsoleIME@8 +RegisterConsoleOS2@4 +RegisterConsoleVDM@44 +RegisterWaitForInputIdle@4 +RegisterWaitForSingleObject@24 +RegisterWaitForSingleObjectEx@20 +RegisterWaitUntilOOBECompleted@12 +RegisterWowBaseHandlers@4 +RegisterWowExec@4 +ReleaseActCtx@4 +ReleaseActCtxWorker@4 +ReleaseMutex@4 +ReleaseMutexWhenCallbackReturns@8 +ReleaseSRWLockExclusive@4 +ReleaseSRWLockShared@4 +ReleaseSemaphore@12 +ReleaseSemaphoreWhenCallbackReturns@12 +ResolveLocaleName@12 +RemoveDirectoryA@4 +RemoveDirectoryTransactedA@8 +RemoveDirectoryTransactedW@8 +RemoveDirectoryW@4 +RemoveDllDirectory@4 +RemoveLocalAlternateComputerNameA@8 +RemoveLocalAlternateComputerNameW@8 +RemoveSecureMemoryCacheCallback@4 +RemoveVectoredContinueHandler@4 +RemoveVectoredExceptionHandler@4 +ReplaceFile@24 +ReplaceFileA@24 +ReplaceFileW@24 +ReplacePartitionUnit@12 +RequestDeviceWakeup@4 +RequestWakeupLatency@4 +ResetEvent@4 +ResetWriteWatch@8 +ResizePseudoConsole@8 +ResolveDelayLoadedAPI@24 +ResolveDelayLoadsFromDll@12 +ResolveLocaleName@12 +RestoreLastError@4 +ResumeThread@4 +RtlCaptureContext@4 +RtlCaptureStackBackTrace@16 +RtlFillMemory@12 +RtlMoveMemory@12 +RtlPcToFileHeader@8 +RtlUnwind@16 +RtlZeroMemory@8 +ScrollConsoleScreenBufferA@20 +ScrollConsoleScreenBufferW@20 +SearchPathA@24 +SearchPathW@24 +SetCachedSigningLevel@16 +SetCalendarInfoA@16 +SetCalendarInfoW@16 +SetClientTimeZoneInformation@4 +SetComPlusPackageInstallStatus@4 +SetCommBreak@4 +SetCommConfig@12 +SetCommMask@8 +SetCommState@8 +SetCommTimeouts@8 +SetComputerNameA@4 +SetComputerNameEx2W@12 +SetComputerNameExA@8 +SetComputerNameExW@8 +SetComputerNameW@4 +SetConsoleActiveScreenBuffer@4 +SetConsoleCP@4 +SetConsoleCommandHistoryMode@4 +SetConsoleCtrlHandler@8 +SetConsoleCursor@8 +SetConsoleCursorInfo@8 +SetConsoleCursorMode@12 +SetConsoleCursorPosition@8 +SetConsoleDisplayMode@12 +SetConsoleFont@8 +SetConsoleHardwareState@12 +SetConsoleHistoryInfo@4 +SetConsoleIcon@4 +SetConsoleInputExeNameA@4 +SetConsoleInputExeNameW@4 +SetConsoleKeyShortcuts@16 +SetConsoleLocalEUDC@16 +SetConsoleMaximumWindowSize@8 +SetConsoleMenuClose@4 +SetConsoleMode@8 +SetConsoleNlsMode@8 +SetConsoleNumberOfCommandsA@8 +SetConsoleNumberOfCommandsW@8 +SetConsoleOS2OemFormat@4 +SetConsoleOutputCP@4 +SetConsolePalette@12 +SetConsoleScreenBufferInfoEx@8 +SetConsoleScreenBufferSize@8 +SetConsoleTextAttribute@8 +SetConsoleTitleA@4 +SetConsoleTitleW@4 +SetConsoleWindowInfo@12 +SetCriticalSectionSpinCount@8 +SetCurrentConsoleFontEx@12 +SetCurrentDirectoryA@4 +SetCurrentDirectoryW@4 +SetDefaultCommConfigA@12 +SetDefaultCommConfigW@12 +SetDefaultDllDirectories@4 +SetDllDirectoryA@4 +SetDllDirectoryW@4 +SetDynamicTimeZoneInformation@4 +SetEndOfFile@4 +SetEnvironmentStringsA@4 +SetEnvironmentStringsW@4 +SetEnvironmentVariableA@8 +SetEnvironmentVariableW@8 +SetErrorMode@4 +SetEvent@4 +SetEventWhenCallbackReturns@8 +SetFileApisToANSI@0 +SetFileApisToOEM@0 +SetFileAttributesA@8 +SetFileAttributesTransactedA@12 +SetFileAttributesTransactedW@12 +SetFileAttributesW@8 +SetFileBandwidthReservation@24 +SetFileCompletionNotificationModes@8 +SetFileInformationByHandle@16 +SetFileIoOverlappedRange@12 +SetFilePointer@16 +SetFilePointerEx@20 +SetFileShortNameA@8 +SetFileShortNameW@8 +SetFileTime@16 +SetFileValidData@12 +SetFirmwareEnvironmentVariableA@16 +SetFirmwareEnvironmentVariableExA@20 +SetFirmwareEnvironmentVariableExW@20 +SetFirmwareEnvironmentVariableW@16 +SetHandleContext@8 +SetHandleCount@4 +SetHandleInformation@12 +SetInformationJobObject@16 +SetIoRateControlInformationJobObject@8 +SetLastConsoleEventActive@0 +SetLastError@4 +SetLocalPrimaryComputerNameA@8 +SetLocalPrimaryComputerNameW@8 +SetLocalTime@4 +SetLocaleInfoA@12 +SetLocaleInfoW@12 +SetMailslotInfo@8 +SetMessageWaitingIndicator@8 +SetNamedPipeAttribute@20 +SetNamedPipeHandleState@16 +SetPriorityClass@8 +SetProcessAffinityMask@8 +SetProcessAffinityUpdateMode@8 +SetProcessDEPPolicy@4 +SetProcessDefaultCpuSets@12 +SetProcessInformation@16 +SetProcessMitigationPolicy@12 +SetProcessPreferredUILanguages@12 +SetProcessPriorityBoost@8 +SetProcessShutdownParameters@8 +SetProcessUserModeExceptionPolicy@4 +SetProcessWorkingSetSize@12 +SetProcessWorkingSetSizeEx@16 +SetProtectedPolicy@12 +SetSearchPathMode@4 +SetStdHandle@8 +SetStdHandleEx@12 +SetSystemFileCacheSize@12 +SetSystemPowerState@8 +SetSystemTime@4 +SetSystemTimeAdjustment@8 +SetTapeParameters@12 +SetTapePosition@24 +SetTermsrvAppInstallMode@4 +SetThreadAffinityMask@8 +SetThreadContext@8 +SetThreadDescription@8 +SetThreadErrorMode@8 +SetThreadExecutionState@4 +SetThreadGroupAffinity@12 +SetThreadIdealProcessor@8 +SetThreadIdealProcessorEx@12 +SetThreadInformation@16 +SetThreadLocale@4 +SetThreadPreferredUILanguages@12 +SetThreadPriority@8 +SetThreadPriorityBoost@8 +SetThreadSelectedCpuSets@12 +SetThreadStackGuarantee@4 +SetThreadToken@8 +SetThreadUILanguage@4 +SetThreadpoolStackInformation@8 +SetThreadpoolThreadMaximum@8 +SetThreadpoolThreadMinimum@8 +SetThreadpoolTimer@16 +SetThreadpoolTimerEx@16 +SetThreadpoolWait@12 +SetThreadpoolWaitEx@16 +SetTimeZoneInformation@4 +SetTimerQueueTimer@24 +SetUnhandledExceptionFilter@4 +SetUserGeoID@4 +SetUserGeoName@4 +SetVDMCurrentDirectories@8 +SetVolumeLabelA@8 +SetVolumeLabelW@8 +SetVolumeMountPointA@8 +SetVolumeMountPointW@8 +SetVolumeMountPointWStub@8 +SetWaitableTimer@24 +SetWaitableTimerEx@28 +SetXStateFeaturesMask@12 +SetupComm@12 +ShowConsoleCursor@8 +SignalObjectAndWait@16 +SizeofResource@8 +Sleep@4 +SleepConditionVariableCS@12 +SleepConditionVariableSRW@16 +SleepEx@8 +SortCloseHandle@4 +SortGetHandle@12 +StartThreadpoolIo@4 +SubmitThreadpoolWork@4 +SuspendThread@4 +SwitchToFiber@4 +SwitchToThread@0 +SystemTimeToFileTime@8 +SystemTimeToTzSpecificLocalTime@12 +SystemTimeToTzSpecificLocalTimeEx@12 +TerminateJobObject@8 +TerminateProcess@8 +TerminateThread@8 +TermsrvAppInstallMode@0 +TermsrvConvertSysRootToUserDir@8 +TermsrvCreateRegEntry@20 +TermsrvDeleteKey@4 +TermsrvDeleteValue@8 +TermsrvGetPreSetValue@16 +TermsrvGetWindowsDirectoryA@8 +TermsrvGetWindowsDirectoryW@8 +TermsrvOpenRegEntry@12 +TermsrvOpenUserClasses@8 +TermsrvRestoreKey@12 +TermsrvSetKeySecurity@12 +TermsrvSetValueKey@24 +TermsrvSyncUserIniFileExt@4 +Thread32First@8 +Thread32Next@8 +TlsAlloc@0 +TlsFree@4 +TlsGetValue@4 +TlsSetValue@8 +Toolhelp32ReadProcessMemory@20 +TransactNamedPipe@28 +TransmitCommChar@8 +TrimVirtualBuffer@4 +TryAcquireSRWLockExclusive@4 +TryAcquireSRWLockShared@4 +TryEnterCriticalSection@4 +TrySubmitThreadpoolCallback@12 +TzSpecificLocalTimeToSystemTime@12 +TzSpecificLocalTimeToSystemTimeEx@12 +UTRegister@28 +UTUnRegister@4 +UnhandledExceptionFilter@4 +UnlockFile@20 +UnlockFileEx@20 +UnmapViewOfFile@4 +UnmapViewOfFileVlm@4 +UnregisterApplicationRecoveryCallback@0 +UnregisterApplicationRestart@0 +UnregisterBadMemoryNotification@4 +UnregisterConsoleIME@0 +UnregisterWait@4 +UnregisterWaitEx@8 +UnregisterWaitUntilOOBECompleted@4 +UpdateCalendarDayOfWeek@4 +UpdateProcThreadAttribute@28 +UpdateResourceA@24 +UpdateResourceW@24 +VDMConsoleOperation@8 +VDMOperationStarted@4 +VerLanguageNameA@12 +VerLanguageNameW@12 +VerSetConditionMask@16 +VerifyConsoleIoHandle@4 +VerifyScripts@20 +VerifyVersionInfoA@16 +VerifyVersionInfoW@16 +VirtualAlloc@16 +VirtualAllocEx@20 +VirtualAllocExNuma@24 +VirtualAllocVlm@24 +VirtualBufferExceptionHandler@12 +VirtualFree@12 +VirtualFreeEx@16 +VirtualFreeVlm@20 +VirtualLock@8 +VirtualProtect@16 +VirtualProtectEx@20 +VirtualProtectVlm@24 +VirtualQuery@12 +VirtualQueryEx@16 +VirtualQueryVlm@16 +VirtualUnlock@8 +WTSGetActiveConsoleSessionId@0 +WaitCommEvent@12 +WaitForDebugEvent@8 +WaitForMultipleObjects@16 +WaitForMultipleObjectsEx@20 +WaitForSingleObject@8 +WaitForSingleObjectEx@12 +WaitForThreadpoolIoCallbacks@8 +WaitForThreadpoolTimerCallbacks@8 +WaitForThreadpoolWaitCallbacks@8 +WaitForThreadpoolWorkCallbacks@8 +WaitNamedPipeA@8 +WaitNamedPipeW@8 +WakeAllConditionVariable@4 +WakeConditionVariable@4 +WerGetFlags@8 +WerGetFlagsWorker@8 +WerRegisterAdditionalProcess@8 +WerRegisterAppLocalDump@4 +WerRegisterCustomMetadata@8 +WerRegisterExcludedMemoryBlock@8 +WerRegisterFile@12 +WerRegisterFileWorker@12 +WerRegisterMemoryBlock@8 +WerRegisterMemoryBlockWorker@8 +WerRegisterRuntimeExceptionModule@8 +WerRegisterRuntimeExceptionModuleWorker@8 +WerSetFlags@4 +WerSetFlagsWorker@4 +WerUnregisterAdditionalProcess@4 +WerUnregisterAppLocalDump +WerUnregisterCustomMetadata@4 +WerUnregisterExcludedMemoryBlock@4 +WerUnregisterFile@4 +WerUnregisterFileWorker@4 +WerUnregisterMemoryBlock@4 +WerUnregisterMemoryBlockWorker@4 +WerUnregisterRuntimeExceptionModule@8 +WerUnregisterRuntimeExceptionModuleWorker@8 +WerpCleanupMessageMapping@0 +WerpGetDebugger@8 +WerpInitiateRemoteRecovery@4 +WerpNotifyLoadStringResource@16 +WerpNotifyLoadStringResourceEx@20 +WerpNotifyUseStringResource@4 +WerpStringLookup@8 +WideCharToMultiByte@32 +WinExec@8 +Wow64DisableWow64FsRedirection@4 +Wow64EnableWow64FsRedirection@4 +Wow64GetThreadContext@8 +Wow64GetThreadSelectorEntry@12 +Wow64RevertWow64FsRedirection@4 +Wow64SetThreadContext@8 +Wow64SuspendThread@4 +WriteConsoleA@20 +WriteConsoleInputA@16 +WriteConsoleInputVDMA@16 +WriteConsoleInputVDMW@16 +WriteConsoleInputW@16 +WriteConsoleOutputA@20 +WriteConsoleOutputAttribute@20 +WriteConsoleOutputCharacterA@20 +WriteConsoleOutputCharacterW@20 +WriteConsoleOutputW@20 +WriteConsoleW@20 +WriteFile@20 +WriteFileEx@20 +WriteFileGather@20 +WriteFileVlm@20 +WritePrivateProfileSectionA@12 +WritePrivateProfileSectionW@12 +WritePrivateProfileStringA@16 +WritePrivateProfileStringW@16 +WritePrivateProfileStructA@20 +WritePrivateProfileStructW@20 +WriteProcessMemory@20 +WriteProcessMemoryVlm@20 +WriteProfileSectionA@8 +WriteProfileSectionW@8 +WriteProfileStringA@12 +WriteProfileStringW@12 +WriteTapemark@16 +WTSGetActiveConsoleSessionId@0 +ZombifyActCtx@4 +ZombifyActCtxWorker@4 +_hread@12 +_hwrite@12 +_lclose@4 +_lcreat@8 +_llseek@12 +_lopen@8 +_lread@12 +_lwrite@12 +lstrcat@8 +lstrcatA@8 +lstrcatW@8 +lstrcmp@8 +lstrcmpA@8 +lstrcmpW@8 +lstrcmpi@8 +lstrcmpiA@8 +lstrcmpiW@8 +lstrcpy@8 +lstrcpyA@8 +lstrcpyW@8 +lstrcpyn@12 +lstrcpynA@12 +lstrcpynW@12 +lstrlen@4 +lstrlenA@4 +lstrlenW@4 +; +; MSDN says these functions are exported +; from winmm.dll. Commented out for +; compatibility with older versions of +; Windows. +; +;timeBeginPeriod@4 +;timeEndPeriod@4 +;timeGetDevCaps@8 +;timeGetSystemTime@8 +;timeGetTime@0 diff --git a/lib/libc/mingw/lib32/lz32.def b/lib/libc/mingw/lib32/lz32.def new file mode 100644 index 000000000..7ea621312 --- /dev/null +++ b/lib/libc/mingw/lib32/lz32.def @@ -0,0 +1,14 @@ +LIBRARY LZ32.DLL +EXPORTS +CopyLZFile@8 +GetExpandedNameA@8 +GetExpandedNameW@8 +LZClose@4 +LZCopy@8 +LZDone@0 +LZInit@4 +LZOpenFileA@12 +LZOpenFileW@12 +LZRead@12 +LZSeek@12 +LZStart@0 diff --git a/lib/libc/mingw/lib32/mpr.def b/lib/libc/mingw/lib32/mpr.def new file mode 100644 index 000000000..f8dde41f7 --- /dev/null +++ b/lib/libc/mingw/lib32/mpr.def @@ -0,0 +1,72 @@ +LIBRARY MPR.DLL +EXPORTS +MultinetGetConnectionPerformanceA@8 +MultinetGetConnectionPerformanceW@8 +RestoreConnectionA0@8 +WNetAddConnection2A@16 +WNetAddConnection2W@16 +WNetAddConnection3A@20 +WNetAddConnection3W@20 +WNetAddConnectionA@12 +WNetAddConnectionW@12 +WNetCancelConnection2A@12 +WNetCancelConnection2W@12 +WNetCancelConnectionA@8 +WNetCancelConnectionW@8 +WNetClearConnections@4 +WNetCloseEnum@4 +WNetConnectionDialog1A@4 +WNetConnectionDialog1W@4 +WNetConnectionDialog2@16 +WNetConnectionDialog@8 +WNetDirectoryNotifyA@12 +WNetDirectoryNotifyW@12 +WNetDisconnectDialog1A@4 +WNetDisconnectDialog1W@4 +WNetDisconnectDialog2@16 +WNetDisconnectDialog@8 +WNetEnumResourceA@16 +WNetEnumResourceW@16 +WNetFMXEditPerm@12 +WNetFMXGetPermCaps@4 +WNetFMXGetPermHelp@24 +WNetFormatNetworkNameA@24 +WNetFormatNetworkNameW@24 +WNetGetConnection2A@12 +WNetGetConnection2W@12 +WNetGetConnectionA@12 +WNetGetConnectionW@12 +WNetGetDirectoryTypeA@12 +WNetGetDirectoryTypeW@12 +WNetGetFormatNameProc@4 +WNetGetLastErrorA@20 +WNetGetLastErrorW@20 +WNetGetNetworkInformationA@8 +WNetGetNetworkInformationW@8 +WNetGetPropertyTextA@24 +WNetGetPropertyTextW@24 +WNetGetProviderNameA@12 +WNetGetProviderNameW@12 +WNetGetResourceInformationA@16 +WNetGetResourceInformationW@16 +WNetGetResourceParentA@12 +WNetGetResourceParentW@12 +WNetGetSearchDialog@4 +WNetGetUniversalNameA@16 +WNetGetUniversalNameW@16 +WNetGetUserA@12 +WNetGetUserW@12 +WNetLogonNotify@36 +WNetOpenEnumA@20 +WNetOpenEnumW@20 +WNetPasswordChangeNotify@32 +WNetPropertyDialogA@20 +WNetPropertyDialogW@20 +WNetRestoreConnection@8 +WNetSetConnectionA@12 +WNetSetConnectionW@12 +WNetSetLastErrorA@12 +WNetSetLastErrorW@12 +WNetSupportGlobalEnum@4 +WNetUseConnectionA@32 +WNetUseConnectionW@32 diff --git a/lib/libc/mingw/lib32/mswsock.def b/lib/libc/mingw/lib32/mswsock.def new file mode 100644 index 000000000..3e74f87a1 --- /dev/null +++ b/lib/libc/mingw/lib32/mswsock.def @@ -0,0 +1,28 @@ +LIBRARY MSWSOCK.DLL +EXPORTS +AcceptEx@32 +EnumProtocolsA@12 +EnumProtocolsW@12 +GetAcceptExSockaddrs@32 +GetAddressByNameA@40 +GetAddressByNameW@40 +GetNameByTypeA@12 +GetNameByTypeW@12 +GetServiceA@28 +GetServiceW@28 +GetTypeByNameA@8 +GetTypeByNameW@8 +MigrateWinsockConfiguration@12 +NPLoadNameSpaces@12 +SetServiceA@24 +SetServiceW@24 +TransmitFile@28 +WSARecvEx@16 +dn_expand@20 +getnetbyname@4 +inet_network@4 +rcmd@24 +rexec@24 +rresvport@4 +s_perror@8 +sethostname@8 diff --git a/lib/libc/mingw/lib32/ncrypt.def b/lib/libc/mingw/lib32/ncrypt.def new file mode 100644 index 000000000..255ed6ad8 --- /dev/null +++ b/lib/libc/mingw/lib32/ncrypt.def @@ -0,0 +1,61 @@ +; +; Definition file of ncrypt.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "ncrypt.dll" +EXPORTS +GetIsolationServerInterface@12 +GetKeyStorageInterface@12 +GetSChannelInterface@12 +NCryptCreatePersistedKey@24 +NCryptDecrypt@32 +NCryptDeleteKey@8 +NCryptDeriveKey@28 +NCryptEncrypt@32 +NCryptEnumAlgorithms@20 +NCryptEnumKeys@20 +NCryptEnumStorageProviders@12 +NCryptExportKey@32 +NCryptFinalizeKey@8 +NCryptFreeBuffer@4 +NCryptFreeObject@4 +NCryptGetProperty@24 +NCryptImportKey@32 +NCryptIsAlgSupported@12 +NCryptIsKeyHandle@4 +NCryptNotifyChangeKey@12 +NCryptOpenKey@20 +NCryptOpenStorageProvider@12 +NCryptSecretAgreement@16 +NCryptSetAuditingInterface@4 +NCryptSetProperty@20 +NCryptSignHash@32 +NCryptTranslateHandle@24 +NCryptVerifySignature@28 +SslChangeNotify@8 +SslComputeClientAuthHash@32 +SslComputeEapKeyBlock@32 +SslComputeFinishedHash@24 +SslCreateEphemeralKey@36 +SslCreateHandshakeHash@20 +SslDecrementProviderReferenceCount@4 +SslDecryptPacket@40 +SslEncryptPacket@44 +SslEnumCipherSuites@20 +SslEnumProtocolProviders@12 +SslExportKey@28 +SslFreeBuffer@4 +SslFreeObject@8 +SslGenerateMasterKey@44 +SslGenerateSessionKeys@24 +SslGetKeyProperty@20 +SslGetProviderProperty@24 +SslHashHandshake@20 +SslImportKey@24 +SslImportMasterKey@36 +SslIncrementProviderReferenceCount@4 +SslLookupCipherSuiteInfo@24 +SslOpenPrivateKey@16 +SslOpenProvider@12 +SslSignHash@32 diff --git a/lib/libc/mingw/lib32/netapi32.def b/lib/libc/mingw/lib32/netapi32.def new file mode 100644 index 000000000..3e0dfceaa --- /dev/null +++ b/lib/libc/mingw/lib32/netapi32.def @@ -0,0 +1,413 @@ +; +; Definition file of NETAPI32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "NETAPI32.dll" +EXPORTS +CredpValidateTargetName@52 +DavAddConnection@24 +DavDeleteConnection@4 +DavFlushFile@4 +DavGetExtendedError@16 +DavGetHTTPFromUNCPath@12 +DavGetUNCFromHTTPPath@12 +DsAddressToSiteNamesA@16 +DsAddressToSiteNamesExA@20 +DsAddressToSiteNamesExW@20 +DsAddressToSiteNamesW@16 +DsDeregisterDnsHostRecordsA@20 +DsDeregisterDnsHostRecordsW@20 +DsEnumerateDomainTrustsA@16 +DsEnumerateDomainTrustsW@16 +DsGetDcCloseW@4 +DsGetDcNameA@24 +DsGetDcNameW@24 +DsGetDcNameWithAccountA@32 +DsGetDcNameWithAccountW@32 +DsGetDcNextA@16 +DsGetDcNextW@16 +DsGetDcOpenA@28 +DsGetDcOpenW@28 +DsGetDcSiteCoverageA@12 +DsGetDcSiteCoverageW@12 +DsGetForestTrustInformationW@16 +DsGetSiteNameA@8 +DsGetSiteNameW@8 +DsMergeForestTrustInformationW@16 +DsRoleAbortDownlevelServerUpgrade@16 +DsRoleCancel@8 +DsRoleDcAsDc@68 +DsRoleDcAsReplica@12 +DsRoleDemoteDc@44 +DsRoleDnsNameToFlatName@16 +DsRoleFreeMemory@4 +DsRoleGetDatabaseFacts@24 +DsRoleGetDcOperationProgress@12 +DsRoleGetDcOperationResults@12 +DsRoleGetPrimaryDomainInformation@12 +DsRoleIfmHandleFree@8 +DsRoleServerSaveStateForUpgrade@4 +DsRoleUpgradeDownlevelServer@48 +DsValidateSubnetNameA@4 +DsValidateSubnetNameW@4 +I_BrowserDebugCall@12 +I_BrowserDebugTrace@8 +I_BrowserQueryEmulatedDomains@12 +I_BrowserQueryOtherDomains@16 +I_BrowserQueryStatistics@8 +I_BrowserResetNetlogonState@4 +I_BrowserResetStatistics@4 +I_BrowserServerEnum@44 +I_BrowserSetNetlogonState@16 +I_DsUpdateReadOnlyServerDnsRecords@28 +I_NetAccountDeltas@48 +I_NetAccountSync@48 +I_NetChainSetClientAttributes2@36 +I_NetChainSetClientAttributes@36 +I_NetDatabaseDeltas@32 +I_NetDatabaseRedo@28 +I_NetDatabaseSync2@36 +I_NetDatabaseSync@32 +I_NetDfsCreateExitPoint@24 +I_NetDfsCreateLocalPartition@28 +I_NetDfsDeleteExitPoint@16 +I_NetDfsDeleteLocalPartition@12 +I_NetDfsFixLocalVolume@36 +I_NetDfsGetFtServers@16 +I_NetDfsGetVersion@8 +I_NetDfsIsThisADomainName@4 +I_NetDfsManagerReportSiteInfo@8 +I_NetDfsModifyPrefix@12 +I_NetDfsSetLocalVolumeState@16 +I_NetDfsSetServerInfo@12 +I_NetGetDCList@16 +I_NetGetForestTrustInformation@24 +I_NetListCanonicalize@36 +I_NetListTraverse@12 +I_NetLogonControl2@20 +I_NetLogonControl@16 +I_NetLogonGetDomainInfo@28 +I_NetLogonSamLogoff@24 +I_NetLogonSamLogon@36 +I_NetLogonSamLogonEx@40 +I_NetLogonSamLogonWithFlags@40 +I_NetLogonSendToSam@24 +I_NetLogonUasLogoff@12 +I_NetLogonUasLogon@12 +I_NetNameCanonicalize@24 +I_NetNameCompare@20 +I_NetNameValidate@16 +I_NetPathCanonicalize@28 +I_NetPathCompare@20 +I_NetPathType@16 +I_NetServerAuthenticate2@28 +I_NetServerAuthenticate3@32 +I_NetServerAuthenticate@24 +I_NetServerGetTrustInfo@36 +I_NetServerPasswordGet@28 +I_NetServerPasswordSet2@28 +I_NetServerPasswordSet@28 +I_NetServerReqChallenge@16 +I_NetServerSetServiceBits@16 +I_NetServerSetServiceBitsEx@24 +I_NetServerTrustPasswordsGet@32 +I_NetlogonComputeClientDigest@24 +I_NetlogonComputeServerDigest@24 +I_NetlogonGetTrustRid@12 +NetAccessAdd@16 +NetAccessDel@8 +NetAccessEnum@36 +NetAccessGetInfo@16 +NetAccessGetUserPerms@16 +NetAccessSetInfo@20 +NetAddAlternateComputerName@20 +NetAddServiceAccount@16 +NetAlertRaise@12 +NetAlertRaiseEx@16 +NetApiBufferAllocate@8 +NetApiBufferFree@4 +NetApiBufferReallocate@12 +NetApiBufferSize@8 +NetAuditClear@12 +NetAuditRead@44 +NetAuditWrite@20 +NetBrowserStatisticsGet@12 +NetConfigGet@16 +NetConfigGetAll@12 +NetConfigSet@28 +NetConnectionEnum@32 +NetDfsAdd@20 +NetDfsAddFtRoot@20 +NetDfsAddRootTarget@20 +NetDfsAddStdRoot@16 +NetDfsAddStdRootForced@16 +NetDfsEnum@24 +NetDfsGetClientInfo@20 +NetDfsGetDcAddress@16 +NetDfsGetFtContainerSecurity@16 +NetDfsGetInfo@20 +NetDfsGetSecurity@16 +NetDfsGetStdContainerSecurity@16 +NetDfsGetSupportedNamespaceVersion@12 +NetDfsManagerGetConfigInfo@28 +NetDfsManagerInitialize@8 +NetDfsManagerSendSiteInfo@12 +NetDfsMove@12 +NetDfsRemove@12 +NetDfsRemoveFtRoot@16 +NetDfsRemoveFtRootForced@20 +NetDfsRemoveRootTarget@12 +NetDfsRemoveStdRoot@12 +NetDfsRename@8 +NetDfsSetClientInfo@20 +NetDfsSetFtContainerSecurity@12 +NetDfsSetInfo@20 +NetDfsSetSecurity@12 +NetDfsSetStdContainerSecurity@12 +NetEnumerateComputerNames@20 +NetEnumerateServiceAccounts@16 +NetEnumerateTrustedDomains@8 +NetErrorLogClear@12 +NetErrorLogRead@44 +NetErrorLogWrite@32 +NetFileClose@8 +NetFileEnum@36 +NetFileGetInfo@16 +NetGetAnyDCName@12 +NetGetDCName@12 +NetGetDisplayInformationIndex@16 +NetGetJoinInformation@12 +NetGetJoinableOUs@24 +NetGroupAdd@16 +NetGroupAddUser@12 +NetGroupDel@8 +NetGroupDelUser@12 +NetGroupEnum@28 +NetGroupGetInfo@16 +NetGroupGetUsers@32 +NetGroupSetInfo@20 +NetGroupSetUsers@20 +NetIsServiceAccount@12 +NetJoinDomain@24 +NetLocalGroupAdd@16 +NetLocalGroupAddMember@12 +NetLocalGroupAddMembers@20 +NetLocalGroupDel@8 +NetLocalGroupDelMember@12 +NetLocalGroupDelMembers@20 +NetLocalGroupEnum@28 +NetLocalGroupGetInfo@16 +NetLocalGroupGetMembers@32 +NetLocalGroupSetInfo@20 +NetLocalGroupSetMembers@20 +NetLogonGetTimeServiceParentDomain@12 +NetLogonSetServiceBits@12 +NetMessageBufferSend@20 +NetMessageNameAdd@8 +NetMessageNameDel@8 +NetMessageNameEnum@28 +NetMessageNameGetInfo@16 +NetProvisionComputerAccount@32 +NetQueryDisplayInformation@28 +NetQueryServiceAccount@16 +NetRegisterDomainNameChangeNotification@4 +NetRemoteComputerSupports@12 +NetRemoteTOD@8 +NetRemoveAlternateComputerName@20 +NetRemoveServiceAccount@12 +NetRenameMachineInDomain@20 +NetReplExportDirAdd@16 +NetReplExportDirDel@8 +NetReplExportDirEnum@28 +NetReplExportDirGetInfo@16 +NetReplExportDirLock@8 +NetReplExportDirSetInfo@20 +NetReplExportDirUnlock@12 +NetReplGetInfo@12 +NetReplImportDirAdd@16 +NetReplImportDirDel@8 +NetReplImportDirEnum@28 +NetReplImportDirGetInfo@16 +NetReplImportDirLock@8 +NetReplImportDirUnlock@12 +NetReplSetInfo@16 +NetRplAdapterAdd@16 +NetRplAdapterDel@8 +NetRplAdapterEnum@28 +NetRplBootAdd@16 +NetRplBootDel@12 +NetRplBootEnum@28 +NetRplClose@4 +NetRplConfigAdd@16 +NetRplConfigDel@8 +NetRplConfigEnum@32 +NetRplGetInfo@12 +NetRplOpen@8 +NetRplProfileAdd@16 +NetRplProfileClone@16 +NetRplProfileDel@8 +NetRplProfileEnum@32 +NetRplProfileGetInfo@16 +NetRplProfileSetInfo@20 +NetRplSetInfo@16 +NetRplSetSecurity@16 +NetRplVendorAdd@16 +NetRplVendorDel@8 +NetRplVendorEnum@28 +NetRplWkstaAdd@16 +NetRplWkstaClone@24 +NetRplWkstaDel@8 +NetRplWkstaEnum@32 +NetRplWkstaGetInfo@16 +NetRplWkstaSetInfo@20 +NetRequestOfflineDomainJoin@16 +NetScheduleJobAdd@12 +NetScheduleJobDel@12 +NetScheduleJobEnum@24 +NetScheduleJobGetInfo@12 +NetServerAliasAdd@12 +NetServerAliasDel@12 +NetServerAliasEnum@28 +NetServerComputerNameAdd@12 +NetServerComputerNameDel@8 +NetServerDiskEnum@28 +NetServerEnum@36 +NetServerEnumEx@36 +NetServerGetInfo@12 +NetServerSetInfo@16 +NetServerTransportAdd@12 +NetServerTransportAddEx@12 +NetServerTransportDel@12 +NetServerTransportEnum@28 +NetServiceControl@20 +NetServiceEnum@28 +NetServiceGetInfo@16 +NetServiceInstall@20 +NetSessionDel@12 +NetSessionEnum@36 +NetSessionGetInfo@20 +NetSetPrimaryComputerName@20 +NetShareAdd@16 +NetShareCheck@12 +NetShareDel@12 +NetShareDelEx@12 +NetShareDelSticky@12 +NetShareEnum@28 +NetShareEnumSticky@28 +NetShareGetInfo@16 +NetShareSetInfo@20 +NetStatisticsGet@20 +NetUnjoinDomain@16 +NetUnregisterDomainNameChangeNotification@4 +NetUseAdd@16 +NetUseDel@12 +NetUseEnum@28 +NetUseGetInfo@16 +NetUserAdd@16 +NetUserChangePassword@16 +NetUserDel@8 +NetUserEnum@32 +NetUserGetGroups@28 +NetUserGetInfo@16 +NetUserGetLocalGroups@32 +NetUserModalsGet@12 +NetUserModalsSet@16 +NetUserSetGroups@20 +NetUserSetInfo@20 +NetValidateName@20 +NetValidatePasswordPolicy@20 +NetValidatePasswordPolicyFree@4 +NetWkstaGetInfo@12 +NetWkstaSetInfo@16 +NetWkstaTransportAdd@16 +NetWkstaTransportDel@12 +NetWkstaTransportEnum@28 +NetWkstaUserEnum@28 +NetWkstaUserGetInfo@12 +NetWkstaUserSetInfo@16 +NetapipBufferAllocate@8 +Netbios@4 +NetpAccessCheck@12 +NetpAccessCheckAndAudit@20 +NetpAccessCheckAndAuditEx@28 +NetpAddTlnFtinfoEntry@8 +NetpAllocConfigName@16 +NetpAllocFtinfoEntry@20 +NetpAllocStrFromWStr@4 +NetpAllocWStrFromStr@4 +NetpAllocWStrFromWStr@4 +NetpApiStatusToNtStatus@4 +NetpAssertFailed@16 +NetpCleanFtinfoContext@4 +NetpCloseConfigData@4 +NetpCopyFtinfoContext@4 +NetpCopyStringToBuffer@20 +NetpCreateSecurityObject@24 +NetpDbgDisplayServerInfo@8 +NetpDbgPrint@0 +NetpDeleteSecurityObject@4 +NetpEventlogClose@4 +NetpEventlogOpen@8 +NetpEventlogWriteEx@32 +NetpGetComputerName@4 +NetpGetConfigBool@16 +NetpGetConfigDword@16 +NetpGetConfigTStrArray@12 +NetpGetConfigValue@12 +NetpGetDomainName@4 +NetpGetFileSecurity@16 +NetpGetPrivilege@8 +NetpHexDump@8 +NetpInitOemString@8 +NetpInitFtinfoContext@4 +NetpIsRemote@20 +NetpIsUncComputerNameValid@4 +NetpLocalTimeZoneOffset@0 +NetpLogonPutUnicodeString@12 +NetpNetBiosAddName@12 +NetpNetBiosCall@16 +NetpNetBiosDelName@8 +NetpNetBiosGetAdapterNumbers@8 +NetpNetBiosHangup@8 +NetpNetBiosReceive@24 +NetpMergeFtinfo@16 +NetpNetBiosReset@4 +NetpNetBiosSend@16 +NetpNetBiosStatusToApiStatus@4 +NetpNtStatusToApiStatus@4 +NetpOpenConfigData@16 +NetpPackString@12 +NetpReleasePrivilege@0 +NetpSetConfigBool@12 +NetpSetConfigDword@12 +NetpSetConfigTStrArray@12 +NetpParmsQueryUserProperty@16 +NetpParmsQueryUserPropertyWithLength@16 +NetpParmsSetUserProperty@28 +NetpParmsUserPropertyFree@4 +NetpOpenConfigData@12 +NetpSetFileSecurity@12 +NetpSmbCheck@20 +NetpStringToNetBiosName@16 +NetpStoreIntialDcRecord@4 +NetpTStrArrayEntryCount@4 +NetpUpgradePreNT5JoinInfo@0 +NetpwNameCanonicalize@20 +NetpwNameCompare@16 +NetpwNameValidate@12 +NetpwPathCanonicalize@24 +NetpwPathCompare@16 +NetpwPathType@12 +NlBindingAddServerToCache@8 +NlBindingRemoveServerFromCache@8 +NlBindingSetAuthInfo@20 +RxNetAccessAdd@16 +RxNetAccessDel@8 +RxNetAccessEnum@36 +RxNetAccessGetInfo@16 +RxNetAccessGetUserPerms@16 +RxNetAccessSetInfo@20 +RxNetServerEnum@40 +RxNetUserPasswordSet@16 +RxRemoteApi diff --git a/lib/libc/mingw/lib32/ole32.def b/lib/libc/mingw/lib32/ole32.def new file mode 100644 index 000000000..7f5b37c57 --- /dev/null +++ b/lib/libc/mingw/lib32/ole32.def @@ -0,0 +1,405 @@ +; +; Definition file of ole32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "ole32.dll" +EXPORTS +CoVrfCheckThreadState@4 +CoVrfGetThreadState@4 +CoVrfReleaseThreadState@4 +HRGN_UserFree@8 +HRGN_UserMarshal@12 +HRGN_UserSize@12 +HRGN_UserUnmarshal@12 +NdrOleInitializeExtension@8 +PropVariantChangeType@20 +BindMoniker@16 +CLIPFORMAT_UserFree@8 +CLIPFORMAT_UserMarshal@12 +CLIPFORMAT_UserSize@12 +CLIPFORMAT_UserUnmarshal@12 +CLSIDFromOle1Class@12 +CLSIDFromProgID@8 +CLSIDFromProgIDEx@8 +CLSIDFromString@8 +CoAddRefServerProcess@0 +CoAllowSetForegroundWindow@8 +CoBuildVersion@0 +CoCancelCall@8 +CoCopyProxy@8 +CoCreateFreeThreadedMarshaler@8 +CoCreateGuid@4 +CoCreateInstance@20 +CoCreateInstanceEx@24 +CoCreateInstanceFromApp@24 +CoCreateObjectInContext@16 +CoDeactivateObject@8 +CoDisableCallCancellation@4 +CoDisconnectContext@4 +CoDisconnectObject@8 +CoDosDateTimeToFileTime@12 +CoEnableCallCancellation@4 +CoFileTimeNow@4 +CoFileTimeToDosDateTime@12 +CoFreeAllLibraries@0 +CoFreeLibrary@4 +CoFreeUnusedLibraries@0 +CoFreeUnusedLibrariesEx@8 +CoGetActivationState@24 +CoGetApartmentID@8 +CoGetApartmentType@8 +CoGetCallContext@8 +CoGetCallState@8 +CoGetCallerTID@4 +CoGetCancelObject@12 +CoGetClassObject@20 +CoGetClassVersion@12 +CoGetComCatalog@8 +CoGetContextToken@4 +CoGetCurrentLogicalThreadId@4 +CoGetCurrentProcess@0 +CoGetDefaultContext@12 +CoGetInstanceFromFile@32 +CoGetInstanceFromIStorage@28 +CoGetInterceptor@16 +CoGetInterceptorFromTypeInfo@20 +CoGetInterfaceAndReleaseStream@12 +CoGetMalloc@8 +CoGetMarshalSizeMax@24 +CoGetModuleType@8 +CoGetObject@16 +CoGetObjectContext@8 +CoGetPSClsid@8 +CoGetProcessIdentifier@4 +CoGetStandardMarshal@24 +CoGetState@4 +CoGetStdMarshalEx@12 +CoGetSystemSecurityPermissions@8 +CoGetTreatAsClass@8 +CoImpersonateClient@0 +CoInitialize@4 +CoInitializeEx@8 +CoInitializeSecurity@36 +CoInitializeWOW@8 +CoInstall@20 +CoInvalidateRemoteMachineBindings@4 +CoIsHandlerConnected@4 +CoIsOle1Class@4 +CoLoadLibrary@8 +CoLockObjectExternal@12 +CoMarshalHresult@8 +CoMarshalInterThreadInterfaceInStream@12 +CoMarshalInterface@24 +CoPopServiceDomain@4 +CoPushServiceDomain@4 +CoQueryAuthenticationServices@8 +CoQueryClientBlanket@28 +CoQueryProxyBlanket@32 +CoQueryReleaseObject@4 +CoReactivateObject@8 +CoRegisterChannelHook@8 +CoRegisterClassObject@20 +CoRegisterInitializeSpy@8 +CoRegisterMallocSpy@4 +CoRegisterMessageFilter@8 +CoRegisterPSClsid@8 +CoRegisterSurrogate@4 +CoRegisterSurrogateEx@8 +CoReleaseMarshalData@4 +CoReleaseServerProcess@0 +CoResumeClassObjects@0 +CoRetireServer@4 +CoRevertToSelf@0 +CoRevokeClassObject@4 +CoRevokeInitializeSpy@8 +CoRevokeMallocSpy@0 +CoSetCancelObject@4 +CoSetProxyBlanket@32 +CoSetState@4 +CoSuspendClassObjects@0 +CoSwitchCallContext@8 +CoTaskMemAlloc@4 +CoTaskMemFree@4 +CoTaskMemRealloc@8 +CoTestCancel@0 +CoTreatAsClass@8 +CoUninitialize@0 +CoUnloadingWOW@4 +CoUnmarshalHresult@8 +CoUnmarshalInterface@12 +CoWaitForMultipleHandles@20 +ComPs_NdrDllCanUnloadNow@4 +ComPs_NdrDllGetClassObject@24 +ComPs_NdrDllRegisterProxy@20 +ComPs_NdrDllUnregisterProxy@20 +CreateAntiMoniker@4 +CreateBindCtx@8 +CreateClassMoniker@8 +CreateDataAdviseHolder@4 +CreateDataCache@16 +CreateErrorInfo@4 +CreateFileMoniker@8 +CreateGenericComposite@12 +CreateILockBytesOnHGlobal@12 +CreateItemMoniker@12 +CreateObjrefMoniker@8 +CreateOleAdviseHolder@4 +CreatePointerMoniker@8 +CreateStdProgressIndicator@16 +CreateStreamOnHGlobal@12 +DcomChannelSetHResult@12 +DllDebugObjectRPCHook@8 +DllGetClassObject@12 +DllGetClassObjectWOW@12 +DllRegisterServer@0 +DoDragDrop@16 +EnableHookObject@8 +FmtIdToPropStgName@8 +FreePropVariantArray@8 +GetClassFile@8 +GetConvertStg@4 +GetDocumentBitStg@4 +GetErrorInfo@8 +GetHGlobalFromILockBytes@8 +GetHGlobalFromStream@8 +GetHookInterface@4 +GetRunningObjectTable@8 +HACCEL_UserFree@8 +HACCEL_UserMarshal@12 +HACCEL_UserSize@12 +HACCEL_UserUnmarshal@12 +HBITMAP_UserFree@8 +HBITMAP_UserMarshal@12 +HBITMAP_UserSize@12 +HBITMAP_UserUnmarshal@12 +HBRUSH_UserFree@8 +HBRUSH_UserMarshal@12 +HBRUSH_UserSize@12 +HBRUSH_UserUnmarshal@12 +HDC_UserFree@8 +HDC_UserMarshal@12 +HDC_UserSize@12 +HDC_UserUnmarshal@12 +HENHMETAFILE_UserFree@8 +HENHMETAFILE_UserMarshal@12 +HENHMETAFILE_UserSize@12 +HENHMETAFILE_UserUnmarshal@12 +HGLOBAL_UserFree@8 +HGLOBAL_UserMarshal@12 +HGLOBAL_UserSize@12 +HGLOBAL_UserUnmarshal@12 +HICON_UserFree@8 +HICON_UserMarshal@12 +HICON_UserSize@12 +HICON_UserUnmarshal@12 +HMENU_UserFree@8 +HMENU_UserMarshal@12 +HMENU_UserSize@12 +HMENU_UserUnmarshal@12 +HMETAFILEPICT_UserFree@8 +HMETAFILEPICT_UserMarshal@12 +HMETAFILEPICT_UserSize@12 +HMETAFILEPICT_UserUnmarshal@12 +HMETAFILE_UserFree@8 +HMETAFILE_UserMarshal@12 +HMETAFILE_UserSize@12 +HMETAFILE_UserUnmarshal@12 +HPALETTE_UserFree@8 +HPALETTE_UserMarshal@12 +HPALETTE_UserSize@12 +HPALETTE_UserUnmarshal@12 +HWND_UserFree@8 +HWND_UserMarshal@12 +HWND_UserSize@12 +HWND_UserUnmarshal@12 +HkOleRegisterObject@16 +IIDFromString@8 +IsAccelerator@16 +IsEqualGUID@8 +IsValidIid@4 +IsValidInterface@4 +IsValidPtrIn@8 +IsValidPtrOut@8 +MkParseDisplayName@16 +MonikerCommonPrefixWith@12 +MonikerRelativePathTo@16 +;NdrProxyForwardingFunction10 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction11 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction12 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction13 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction14 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction15 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction16 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction17 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction18 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction19 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction20 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction21 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction22 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction23 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction24 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction25 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction26 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction27 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction28 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction29 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction30 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction31 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction32 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction3 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction4 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction5 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction6 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction7 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction8 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;NdrProxyForwardingFunction9 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient10 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient11 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient12 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient13 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient14 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient15 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient16 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient17 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient18 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient19 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient20 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient21 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient22 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient23 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient24 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient25 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient26 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient27 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient28 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient29 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient30 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient31 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient32 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient3 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient4 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient5 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient6 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient7 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient8 ; Check!!! Couldn't determine function argument count. Function doesn't return. +;ObjectStublessClient9 ; Check!!! Couldn't determine function argument count. Function doesn't return. +OleBuildVersion@0 +OleConvertIStorageToOLESTREAM@8 +OleConvertIStorageToOLESTREAMEx@28 +OleConvertOLESTREAMToIStorage@12 +OleConvertOLESTREAMToIStorageEx@28 +OleCreate@28 +OleCreateDefaultHandler@16 +OleCreateEmbeddingHelper@24 +OleCreateEx@48 +OleCreateFromData@28 +OleCreateFromDataEx@48 +OleCreateFromFile@32 +OleCreateFromFileEx@52 +OleCreateLink@28 +OleCreateLinkEx@48 +OleCreateLinkFromData@28 +OleCreateLinkFromDataEx@48 +OleCreateLinkToFile@28 +OleCreateLinkToFileEx@48 +OleCreateMenuDescriptor@8 +OleCreateStaticFromData@28 +OleDestroyMenuDescriptor@4 +OleDoAutoConvert@8 +OleDraw@16 +OleDuplicateData@12 +OleFlushClipboard@0 +OleGetAutoConvert@8 +OleGetClipboard@4 +OleGetIconOfClass@12 +OleGetIconOfFile@8 +OleInitialize@4 +OleInitializeWOW@8 +OleIsCurrentClipboard@4 +OleIsRunning@4 +OleLoad@16 +OleLoadFromStream@12 +OleLockRunning@12 +OleMetafilePictFromIconAndLabel@16 +OleNoteObjectVisible@8 +OleQueryCreateFromData@4 +OleQueryLinkFromData@4 +OleRegEnumFormatEtc@12 +OleRegEnumVerbs@8 +OleRegGetMiscStatus@12 +OleRegGetUserType@12 +OleRun@4 +OleSave@12 +OleSaveToStream@8 +OleSetAutoConvert@8 +OleSetClipboard@4 +OleSetContainedObject@8 +OleSetMenuDescriptor@20 +OleTranslateAccelerator@12 +OleUninitialize@0 +OpenOrCreateStream@12 +ProgIDFromCLSID@8 +PropStgNameToFmtId@8 +PropSysAllocString@4 +PropSysFreeString@4 +PropVariantClear@4 +PropVariantCopy@8 +ReadClassStg@8 +ReadClassStm@8 +ReadFmtUserTypeStg@12 +ReadOleStg@24 +ReadStringStream@8 +RegisterDragDrop@8 +ReleaseStgMedium@4 +RevokeDragDrop@4 +SNB_UserFree@8 +SNB_UserMarshal@12 +SNB_UserSize@12 +SNB_UserUnmarshal@12 +STGMEDIUM_UserFree@8 +STGMEDIUM_UserMarshal@12 +STGMEDIUM_UserSize@12 +STGMEDIUM_UserUnmarshal@12 +SetConvertStg@8 +SetDocumentBitStg@8 +SetErrorInfo@8 +StgConvertPropertyToVariant@16 +StgConvertVariantToProperty@28 +StgCreateDocfile@16 +StgCreateDocfileOnILockBytes@16 +StgCreatePropSetStg@12 +StgCreatePropStg@24 +StgCreateStorageEx@32 +StgGetIFillLockBytesOnFile@8 +StgGetIFillLockBytesOnILockBytes@8 +StgIsStorageFile@4 +StgIsStorageILockBytes@4 +StgOpenAsyncDocfileOnIFillLockBytes@16 +StgOpenPropStg@20 +StgOpenStorage@24 +StgOpenStorageEx@32 +StgOpenStorageOnHandle@24 +StgOpenStorageOnILockBytes@24 +StgPropertyLengthAsVariant@16 +StgSetTimes@16 +StgCreateStorageEx@32 +StgOpenStorageEx@32 +StringFromCLSID@8 +StringFromGUID2@12 +StringFromIID@8 +UpdateDCOMSettings@0 +UpdateProcessTracing@8 +UtConvertDvtd16toDvtd32@12 +UtConvertDvtd32toDvtd16@12 +UtGetDvtd16Info@8 +UtGetDvtd32Info@8 +WdtpInterfacePointer_UserFree@4 +WdtpInterfacePointer_UserMarshal@20 +WdtpInterfacePointer_UserSize@20 +WdtpInterfacePointer_UserUnmarshal@16 +WriteClassStg@8 +WriteClassStm@8 +WriteFmtUserTypeStg@12 +WriteOleStg@16 +WriteStringStream@8 diff --git a/lib/libc/mingw/lib32/oleaut32.def b/lib/libc/mingw/lib32/oleaut32.def new file mode 100644 index 000000000..c0b69ebc4 --- /dev/null +++ b/lib/libc/mingw/lib32/oleaut32.def @@ -0,0 +1,427 @@ +; +; Definition file of OLEAUT32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "OLEAUT32.dll" +EXPORTS +SysAllocString@4 +SysReAllocString@8 +SysAllocStringLen@8 +SysReAllocStringLen@12 +SysFreeString@4 +SysStringLen@4 +VariantInit@4 +VariantClear@4 +VariantCopy@8 +VariantCopyInd@8 +VariantChangeType@16 +VariantTimeToDosDateTime@16 +DosDateTimeToVariantTime@12 +SafeArrayCreate@12 +SafeArrayDestroy@4 +SafeArrayGetDim@4 +SafeArrayGetElemsize@4 +SafeArrayGetUBound@12 +SafeArrayGetLBound@12 +SafeArrayLock@4 +SafeArrayUnlock@4 +SafeArrayAccessData@8 +SafeArrayUnaccessData@4 +SafeArrayGetElement@12 +SafeArrayPutElement@12 +SafeArrayCopy@8 +DispGetParam@20 +DispGetIDsOfNames@16 +DispInvoke@32 +CreateDispTypeInfo@12 +CreateStdDispatch@16 +RegisterActiveObject@16 +RevokeActiveObject@8 +GetActiveObject@12 +SafeArrayAllocDescriptor@8 +SafeArrayAllocData@4 +SafeArrayDestroyDescriptor@4 +SafeArrayDestroyData@4 +SafeArrayRedim@8 +SafeArrayAllocDescriptorEx@12 +SafeArrayCreateEx@16 +SafeArrayCreateVectorEx@16 +SafeArraySetRecordInfo@8 +SafeArrayGetRecordInfo@8 +VarParseNumFromStr@20 +VarNumFromParseNum@16 +VarI2FromUI1@8 +VarI2FromI4@8 +VarI2FromR4@8 +VarI2FromR8@12 +VarI2FromCy@12 +VarI2FromDate@12 +VarI2FromStr@16 +VarI2FromDisp@12 +VarI2FromBool@8 +SafeArraySetIID@8 +VarI4FromUI1@8 +VarI4FromI2@8 +VarI4FromR4@8 +VarI4FromR8@12 +VarI4FromCy@12 +VarI4FromDate@12 +VarI4FromStr@16 +VarI4FromDisp@12 +VarI4FromBool@8 +SafeArrayGetIID@8 +VarR4FromUI1@8 +VarR4FromI2@8 +VarR4FromI4@8 +VarR4FromR8@12 +VarR4FromCy@12 +VarR4FromDate@12 +VarR4FromStr@16 +VarR4FromDisp@12 +VarR4FromBool@8 +SafeArrayGetVartype@8 +VarR8FromUI1@8 +VarR8FromI2@8 +VarR8FromI4@8 +VarR8FromR4@8 +VarR8FromCy@12 +VarR8FromDate@12 +VarR8FromStr@16 +VarR8FromDisp@12 +VarR8FromBool@8 +VarFormat@24 +VarDateFromUI1@8 +VarDateFromI2@8 +VarDateFromI4@8 +VarDateFromR4@8 +VarDateFromR8@12 +VarDateFromCy@12 +VarDateFromStr@16 +VarDateFromDisp@12 +VarDateFromBool@8 +VarFormatDateTime@16 +VarCyFromUI1@8 +VarCyFromI2@8 +VarCyFromI4@8 +VarCyFromR4@8 +VarCyFromR8@12 +VarCyFromDate@12 +VarCyFromStr@16 +VarCyFromDisp@12 +VarCyFromBool@8 +VarFormatNumber@28 +VarBstrFromUI1@16 +VarBstrFromI2@16 +VarBstrFromI4@16 +VarBstrFromR4@16 +VarBstrFromR8@20 +VarBstrFromCy@20 +VarBstrFromDate@20 +VarBstrFromDisp@16 +VarBstrFromBool@16 +VarFormatPercent@28 +VarBoolFromUI1@8 +VarBoolFromI2@8 +VarBoolFromI4@8 +VarBoolFromR4@8 +VarBoolFromR8@12 +VarBoolFromDate@12 +VarBoolFromCy@12 +VarBoolFromStr@16 +VarBoolFromDisp@12 +VarFormatCurrency@28 +VarWeekdayName@20 +VarMonthName@16 +VarUI1FromI2@8 +VarUI1FromI4@8 +VarUI1FromR4@8 +VarUI1FromR8@12 +VarUI1FromCy@12 +VarUI1FromDate@12 +VarUI1FromStr@16 +VarUI1FromDisp@12 +VarUI1FromBool@8 +VarFormatFromTokens@24 +VarTokenizeFormatString@28 +VarAdd@12 +VarAnd@12 +VarDiv@12 +;DllCanUnloadNow@0 +DllGetClassObject@12 +DispCallFunc@32 +VariantChangeTypeEx@20 +SafeArrayPtrOfIndex@12 +SysStringByteLen@4 +SysAllocStringByteLen@8 +DllRegisterServer@0 +VarEqv@12 +VarIdiv@12 +VarImp@12 +VarMod@12 +VarMul@12 +VarOr@12 +VarPow@12 +VarSub@12 +CreateTypeLib@12 +LoadTypeLib@8 +LoadRegTypeLib@20 +RegisterTypeLib@12 +QueryPathOfRegTypeLib@20 +LHashValOfNameSys@12 +LHashValOfNameSysA@12 +VarXor@12 +VarAbs@8 +VarFix@8 +OaBuildVersion@0 +ClearCustData@4 +VarInt@8 +VarNeg@8 +VarNot@8 +VarRound@12 +VarCmp@16 +VarDecAdd@12 +VarDecDiv@12 +VarDecMul@12 +CreateTypeLib2@12 +VarDecSub@12 +VarDecAbs@8 +LoadTypeLibEx@12 +SystemTimeToVariantTime@8 +VariantTimeToSystemTime@12 +UnRegisterTypeLib@20 +UserBSTR_free_inst@4 +UserBSTR_free_local@4 +UserBSTR_from_local@8 +UserBSTR_to_local@8 +UserEXCEPINFO_free_inst@4 +UserEXCEPINFO_free_local@4 +UserEXCEPINFO_from_local@8 +UserEXCEPINFO_to_local@8 +UserHWND_free_inst@4 +UserHWND_free_local@4 +UserHWND_from_local@8 +UserHWND_to_local@8 +UserMSG_free_inst@4 +UserMSG_free_local@4 +UserMSG_from_local@8 +UserMSG_to_local@8 +UserVARIANT_free_inst@4 +UserVARIANT_free_local@4 +UserVARIANT_from_local@8 +UserVARIANT_to_local@8 +VarDecFix@8 +VarDecInt@8 +VarDecNeg@8 +VarDecFromUI1@8 +VarDecFromI2@8 +VarDecFromI4@8 +VarDecFromR4@8 +VarDecFromR8@12 +VarDecFromDate@12 +VarDecFromCy@12 +VarDecFromStr@16 +VarDecFromDisp@12 +VarDecFromBool@8 +GetErrorInfo@8 +SetErrorInfo@8 +CreateErrorInfo@4 +VarDecRound@12 +VarDecCmp@8 +VarI2FromI1@8 +VarI2FromUI2@8 +VarI2FromUI4@8 +VarI2FromDec@8 +VarI4FromI1@8 +VarI4FromUI2@8 +VarI4FromUI4@8 +VarI4FromDec@8 +VarR4FromI1@8 +VarR4FromUI2@8 +VarR4FromUI4@8 +VarR4FromDec@8 +VarR8FromI1@8 +VarR8FromUI2@8 +VarR8FromUI4@8 +VarR8FromDec@8 +VarDateFromI1@8 +VarDateFromUI2@8 +VarDateFromUI4@8 +VarDateFromDec@8 +VarCyFromI1@8 +VarCyFromUI2@8 +VarCyFromUI4@8 +VarCyFromDec@8 +VarBstrFromI1@16 +VarBstrFromUI2@16 +VarBstrFromUI4@16 +VarBstrFromDec@16 +VarBoolFromI1@8 +VarBoolFromUI2@8 +VarBoolFromUI4@8 +VarBoolFromDec@8 +VarUI1FromI1@8 +VarUI1FromUI2@8 +VarUI1FromUI4@8 +VarUI1FromDec@8 +VarDecFromI1@8 +VarDecFromUI2@8 +VarDecFromUI4@8 +VarI1FromUI1@8 +VarI1FromI2@8 +VarI1FromI4@8 +VarI1FromR4@8 +VarI1FromR8@12 +VarI1FromDate@12 +VarI1FromCy@12 +VarI1FromStr@16 +VarI1FromDisp@12 +VarI1FromBool@8 +VarI1FromUI2@8 +VarI1FromUI4@8 +VarI1FromDec@8 +VarUI2FromUI1@8 +VarUI2FromI2@8 +VarUI2FromI4@8 +VarUI2FromR4@8 +VarUI2FromR8@12 +VarUI2FromDate@12 +VarUI2FromCy@12 +VarUI2FromStr@16 +VarUI2FromDisp@12 +VarUI2FromBool@8 +VarUI2FromI1@8 +VarUI2FromUI4@8 +VarUI2FromDec@8 +VarUI4FromUI1@8 +VarUI4FromI2@8 +VarUI4FromI4@8 +VarUI4FromR4@8 +VarUI4FromR8@12 +VarUI4FromDate@12 +VarUI4FromCy@12 +VarUI4FromStr@16 +VarUI4FromDisp@12 +VarUI4FromBool@8 +VarUI4FromI1@8 +VarUI4FromUI2@8 +VarUI4FromDec@8 +BSTR_UserSize@12 +BSTR_UserMarshal@12 +BSTR_UserUnmarshal@12 +BSTR_UserFree@8 +VARIANT_UserSize@12 +VARIANT_UserMarshal@12 +VARIANT_UserUnmarshal@12 +VARIANT_UserFree@8 +LPSAFEARRAY_UserSize@12 +LPSAFEARRAY_UserMarshal@12 +LPSAFEARRAY_UserUnmarshal@12 +LPSAFEARRAY_UserFree@8 +LPSAFEARRAY_Size@16 +LPSAFEARRAY_Marshal@16 +LPSAFEARRAY_Unmarshal@16 +VarDecCmpR8@12 +VarCyAdd@20 +DllUnregisterServer@0 +OACreateTypeLib2@12 +VarCyMul@20 +VarCyMulI4@16 +VarCySub@20 +VarCyAbs@12 +VarCyFix@12 +VarCyInt@12 +VarCyNeg@12 +VarCyRound@16 +VarCyCmp@16 +VarCyCmpR8@16 +VarBstrCat@12 +VarBstrCmp@16 +VarR8Pow@20 +VarR4CmpR8@12 +VarR8Round@16 +VarCat@12 +VarDateFromUdateEx@16 +GetRecordInfoFromGuids@24 +GetRecordInfoFromTypeInfo@8 +SetVarConversionLocaleSetting@4 +GetVarConversionLocaleSetting@4 +SetOaNoCache +VarCyMulI8@20 +VarDateFromUdate@12 +VarUdateFromDate@16 +GetAltMonthNames@8 +VarI8FromUI1@8 +VarI8FromI2@8 +VarI8FromR4@8 +VarI8FromR8@12 +VarI8FromCy@12 +VarI8FromDate@12 +VarI8FromStr@16 +VarI8FromDisp@12 +VarI8FromBool@8 +VarI8FromI1@8 +VarI8FromUI2@8 +VarI8FromUI4@8 +VarI8FromDec@8 +VarI2FromI8@12 +VarI2FromUI8@12 +VarI4FromI8@12 +VarI4FromUI8@12 +VarR4FromI8@12 +VarR4FromUI8@12 +VarR8FromI8@12 +VarR8FromUI8@12 +VarDateFromI8@12 +VarDateFromUI8@12 +VarCyFromI8@12 +VarCyFromUI8@12 +VarBstrFromI8@20 +VarBstrFromUI8@20 +VarBoolFromI8@12 +VarBoolFromUI8@12 +VarUI1FromI8@12 +VarUI1FromUI8@12 +VarDecFromI8@12 +VarDecFromUI8@12 +VarI1FromI8@12 +VarI1FromUI8@12 +VarUI2FromI8@12 +VarUI2FromUI8@12 +OleLoadPictureEx@32 +OleLoadPictureFileEx@32 +SafeArrayCreateVector@12 +SafeArrayCopyData@8 +VectorFromBstr@8 +BstrFromVector@8 +OleIconToCursor@8 +OleCreatePropertyFrameIndirect@4 +OleCreatePropertyFrame@44 +OleLoadPicture@20 +OleCreatePictureIndirect@16 +OleCreateFontIndirect@12 +OleTranslateColor@12 +OleLoadPictureFile@20 +OleSavePictureFile@8 +OleLoadPicturePath@24 +VarUI4FromI8@12 +VarUI4FromUI8@12 +VarI8FromUI8@12 +VarUI8FromI8@12 +VarUI8FromUI1@8 +VarUI8FromI2@8 +VarUI8FromR4@8 +VarUI8FromR8@12 +VarUI8FromCy@12 +VarUI8FromDate@12 +VarUI8FromStr@16 +VarUI8FromDisp@12 +VarUI8FromBool@8 +VarUI8FromI1@8 +VarUI8FromUI2@8 +VarUI8FromUI4@8 +VarUI8FromDec@8 +RegisterTypeLibForUser@12 +UnRegisterTypeLibForUser@20 +OaEnablePerUserTLibRegistration@0 +OACleanup@0 diff --git a/lib/libc/mingw/lib32/opengl32.def b/lib/libc/mingw/lib32/opengl32.def new file mode 100644 index 000000000..254e80a02 --- /dev/null +++ b/lib/libc/mingw/lib32/opengl32.def @@ -0,0 +1,370 @@ +LIBRARY OPENGL32.DLL +EXPORTS +GlmfBeginGlsBlock@4 +GlmfCloseMetaFile@4 +GlmfEndGlsBlock@4 +GlmfEndPlayback@4 +GlmfInitPlayback@12 +GlmfPlayGlsRecord@16 +glAccum@8 +glAlphaFunc@8 +glAreTexturesResident@12 +glArrayElement@4 +glBegin@4 +glBindTexture@8 +glBitmap@28 +glBlendFunc@8 +glCallList@4 +glCallLists@12 +glClear@4 +glClearAccum@16 +glClearColor@16 +glClearDepth@8 +glClearIndex@4 +glClearStencil@4 +glClipPlane@8 +glColor3b@12 +glColor3bv@4 +glColor3d@24 +glColor3dv@4 +glColor3f@12 +glColor3fv@4 +glColor3i@12 +glColor3iv@4 +glColor3s@12 +glColor3sv@4 +glColor3ub@12 +glColor3ubv@4 +glColor3ui@12 +glColor3uiv@4 +glColor3us@12 +glColor3usv@4 +glColor4b@16 +glColor4bv@4 +glColor4d@32 +glColor4dv@4 +glColor4f@16 +glColor4fv@4 +glColor4i@16 +glColor4iv@4 +glColor4s@16 +glColor4sv@4 +glColor4ub@16 +glColor4ubv@4 +glColor4ui@16 +glColor4uiv@4 +glColor4us@16 +glColor4usv@4 +glColorMask@16 +glColorMaterial@8 +glColorPointer@16 +glCopyPixels@20 +glCopyTexImage1D@28 +glCopyTexImage2D@32 +glCopyTexSubImage1D@24 +glCopyTexSubImage2D@32 +glCullFace@4 +glDebugEntry@8 +glDeleteLists@8 +glDeleteTextures@8 +glDepthFunc@4 +glDepthMask@4 +glDepthRange@16 +glDisable@4 +glDisableClientState@4 +glDrawArrays@12 +glDrawBuffer@4 +glDrawElements@16 +glDrawPixels@20 +glEdgeFlag@4 +glEdgeFlagPointer@8 +glEdgeFlagv@4 +glEnable@4 +glEnableClientState@4 +glEnd@0 +glEndList@0 +glEvalCoord1d@8 +glEvalCoord1dv@4 +glEvalCoord1f@4 +glEvalCoord1fv@4 +glEvalCoord2d@16 +glEvalCoord2dv@4 +glEvalCoord2f@8 +glEvalCoord2fv@4 +glEvalMesh1@12 +glEvalMesh2@20 +glEvalPoint1@4 +glEvalPoint2@8 +glFeedbackBuffer@12 +glFinish@0 +glFlush@0 +glFogf@8 +glFogfv@8 +glFogi@8 +glFogiv@8 +glFrontFace@4 +glFrustum@48 +glGenLists@4 +glGenTextures@8 +glGetBooleanv@8 +glGetClipPlane@8 +glGetDoublev@8 +glGetError@0 +glGetFloatv@8 +glGetIntegerv@8 +glGetLightfv@12 +glGetLightiv@12 +glGetMapdv@12 +glGetMapfv@12 +glGetMapiv@12 +glGetMaterialfv@12 +glGetMaterialiv@12 +glGetPixelMapfv@8 +glGetPixelMapuiv@8 +glGetPixelMapusv@8 +glGetPointerv@8 +glGetPolygonStipple@4 +glGetString@4 +glGetTexEnvfv@12 +glGetTexEnviv@12 +glGetTexGendv@12 +glGetTexGenfv@12 +glGetTexGeniv@12 +glGetTexImage@20 +glGetTexLevelParameterfv@16 +glGetTexLevelParameteriv@16 +glGetTexParameterfv@12 +glGetTexParameteriv@12 +glHint@8 +glIndexMask@4 +glIndexPointer@12 +glIndexd@8 +glIndexdv@4 +glIndexf@4 +glIndexfv@4 +glIndexi@4 +glIndexiv@4 +glIndexs@4 +glIndexsv@4 +glIndexub@4 +glIndexubv@4 +glInitNames@0 +glInterleavedArrays@12 +glIsEnabled@4 +glIsList@4 +glIsTexture@4 +glLightModelf@8 +glLightModelfv@8 +glLightModeli@8 +glLightModeliv@8 +glLightf@12 +glLightfv@12 +glLighti@12 +glLightiv@12 +glLineStipple@8 +glLineWidth@4 +glListBase@4 +glLoadIdentity@0 +glLoadMatrixd@4 +glLoadMatrixf@4 +glLoadName@4 +glLogicOp@4 +glMap1d@32 +glMap1f@24 +glMap2d@56 +glMap2f@40 +glMapGrid1d@20 +glMapGrid1f@12 +glMapGrid2d@40 +glMapGrid2f@24 +glMaterialf@12 +glMaterialfv@12 +glMateriali@12 +glMaterialiv@12 +glMatrixMode@4 +glMultMatrixd@4 +glMultMatrixf@4 +glNewList@8 +glNormal3b@12 +glNormal3bv@4 +glNormal3d@24 +glNormal3dv@4 +glNormal3f@12 +glNormal3fv@4 +glNormal3i@12 +glNormal3iv@4 +glNormal3s@12 +glNormal3sv@4 +glNormalPointer@12 +glOrtho@48 +glPassThrough@4 +glPixelMapfv@12 +glPixelMapuiv@12 +glPixelMapusv@12 +glPixelStoref@8 +glPixelStorei@8 +glPixelTransferf@8 +glPixelTransferi@8 +glPixelZoom@8 +glPointSize@4 +glPolygonMode@8 +glPolygonOffset@8 +glPolygonStipple@4 +glPopAttrib@0 +glPopClientAttrib@0 +glPopMatrix@0 +glPopName@0 +glPrioritizeTextures@12 +glPushAttrib@4 +glPushClientAttrib@4 +glPushMatrix@0 +glPushName@4 +glRasterPos2d@16 +glRasterPos2dv@4 +glRasterPos2f@8 +glRasterPos2fv@4 +glRasterPos2i@8 +glRasterPos2iv@4 +glRasterPos2s@8 +glRasterPos2sv@4 +glRasterPos3d@24 +glRasterPos3dv@4 +glRasterPos3f@12 +glRasterPos3fv@4 +glRasterPos3i@12 +glRasterPos3iv@4 +glRasterPos3s@12 +glRasterPos3sv@4 +glRasterPos4d@32 +glRasterPos4dv@4 +glRasterPos4f@16 +glRasterPos4fv@4 +glRasterPos4i@16 +glRasterPos4iv@4 +glRasterPos4s@16 +glRasterPos4sv@4 +glReadBuffer@4 +glReadPixels@28 +glRectd@32 +glRectdv@8 +glRectf@16 +glRectfv@8 +glRecti@16 +glRectiv@8 +glRects@16 +glRectsv@8 +glRenderMode@4 +glRotated@32 +glRotatef@16 +glScaled@24 +glScalef@12 +glScissor@16 +glSelectBuffer@8 +glShadeModel@4 +glStencilFunc@12 +glStencilMask@4 +glStencilOp@12 +glTexCoord1d@8 +glTexCoord1dv@4 +glTexCoord1f@4 +glTexCoord1fv@4 +glTexCoord1i@4 +glTexCoord1iv@4 +glTexCoord1s@4 +glTexCoord1sv@4 +glTexCoord2d@16 +glTexCoord2dv@4 +glTexCoord2f@8 +glTexCoord2fv@4 +glTexCoord2i@8 +glTexCoord2iv@4 +glTexCoord2s@8 +glTexCoord2sv@4 +glTexCoord3d@24 +glTexCoord3dv@4 +glTexCoord3f@12 +glTexCoord3fv@4 +glTexCoord3i@12 +glTexCoord3iv@4 +glTexCoord3s@12 +glTexCoord3sv@4 +glTexCoord4d@32 +glTexCoord4dv@4 +glTexCoord4f@16 +glTexCoord4fv@4 +glTexCoord4i@16 +glTexCoord4iv@4 +glTexCoord4s@16 +glTexCoord4sv@4 +glTexCoordPointer@16 +glTexEnvf@12 +glTexEnvfv@12 +glTexEnvi@12 +glTexEnviv@12 +glTexGend@16 +glTexGendv@12 +glTexGenf@12 +glTexGenfv@12 +glTexGeni@12 +glTexGeniv@12 +glTexImage1D@32 +glTexImage2D@36 +glTexParameterf@12 +glTexParameterfv@12 +glTexParameteri@12 +glTexParameteriv@12 +glTexSubImage1D@28 +glTexSubImage2D@36 +glTranslated@24 +glTranslatef@12 +glVertex2d@16 +glVertex2dv@4 +glVertex2f@8 +glVertex2fv@4 +glVertex2i@8 +glVertex2iv@4 +glVertex2s@8 +glVertex2sv@4 +glVertex3d@24 +glVertex3dv@4 +glVertex3f@12 +glVertex3fv@4 +glVertex3i@12 +glVertex3iv@4 +glVertex3s@12 +glVertex3sv@4 +glVertex4d@32 +glVertex4dv@4 +glVertex4f@16 +glVertex4fv@4 +glVertex4i@16 +glVertex4iv@4 +glVertex4s@16 +glVertex4sv@4 +glVertexPointer@16 +glViewport@16 +wglChoosePixelFormat@8 +wglCopyContext@12 +wglCreateContext@4 +wglCreateLayerContext@8 +wglDeleteContext@4 +wglDescribeLayerPlane@20 +wglDescribePixelFormat@16 +wglGetCurrentContext@0 +wglGetCurrentDC@0 +wglGetDefaultProcAddress@4 +wglGetLayerPaletteEntries@20 +wglGetPixelFormat@4 +wglGetProcAddress@4 +wglMakeCurrent@8 +wglRealizeLayerPalette@12 +wglSetLayerPaletteEntries@20 +wglSetPixelFormat@12 +wglShareLists@8 +wglSwapBuffers@4 +wglSwapLayerBuffers@8 +wglSwapMultipleBuffers@8 +wglUseFontBitmapsA@16 +wglUseFontBitmapsW@16 +wglUseFontOutlinesA@32 +wglUseFontOutlinesW@32 diff --git a/lib/libc/mingw/lib32/rpcns4.def b/lib/libc/mingw/lib32/rpcns4.def new file mode 100644 index 000000000..c0404c2ed --- /dev/null +++ b/lib/libc/mingw/lib32/rpcns4.def @@ -0,0 +1,70 @@ +; +; Definition file of RPCNS4.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "RPCNS4.dll" +EXPORTS +I_GetDefaultEntrySyntax@0 +I_RpcNsGetBuffer@4 +I_RpcNsNegotiateTransferSyntax@4 +I_RpcNsRaiseException@8 +I_RpcNsSendReceive@8 +I_RpcReBindBuffer@4 +RpcIfIdVectorFree@4 +RpcNsBindingExportA@20 +RpcNsBindingExportPnPA@16 +RpcNsBindingExportPnPW@16 +RpcNsBindingExportW@20 +RpcNsBindingImportBeginA@20 +RpcNsBindingImportBeginW@20 +RpcNsBindingImportDone@4 +RpcNsBindingImportNext@8 +RpcNsBindingLookupBeginA@24 +RpcNsBindingLookupBeginW@24 +RpcNsBindingLookupDone@4 +RpcNsBindingLookupNext@8 +RpcNsBindingSelect@8 +RpcNsBindingUnexportA@16 +RpcNsBindingUnexportPnPA@16 +RpcNsBindingUnexportPnPW@16 +RpcNsBindingUnexportW@16 +RpcNsEntryExpandNameA@12 +RpcNsEntryExpandNameW@12 +RpcNsEntryObjectInqBeginA@12 +RpcNsEntryObjectInqBeginW@12 +RpcNsEntryObjectInqDone@4 +RpcNsEntryObjectInqNext@8 +RpcNsGroupDeleteA@8 +RpcNsGroupDeleteW@8 +RpcNsGroupMbrAddA@16 +RpcNsGroupMbrAddW@16 +RpcNsGroupMbrInqBeginA@16 +RpcNsGroupMbrInqBeginW@16 +RpcNsGroupMbrInqDone@4 +RpcNsGroupMbrInqNextA@8 +RpcNsGroupMbrInqNextW@8 +RpcNsGroupMbrRemoveA@16 +RpcNsGroupMbrRemoveW@16 +RpcNsMgmtBindingUnexportA@20 +RpcNsMgmtBindingUnexportW@20 +RpcNsMgmtEntryCreateA@8 +RpcNsMgmtEntryCreateW@8 +RpcNsMgmtEntryDeleteA@8 +RpcNsMgmtEntryDeleteW@8 +RpcNsMgmtEntryInqIfIdsA@12 +RpcNsMgmtEntryInqIfIdsW@12 +RpcNsMgmtHandleSetExpAge@8 +RpcNsMgmtInqExpAge@4 +RpcNsMgmtSetExpAge@4 +RpcNsProfileDeleteA@8 +RpcNsProfileDeleteW@8 +RpcNsProfileEltAddA@28 +RpcNsProfileEltAddW@28 +RpcNsProfileEltInqBeginA@32 +RpcNsProfileEltInqBeginW@32 +RpcNsProfileEltInqDone@4 +RpcNsProfileEltInqNextA@20 +RpcNsProfileEltInqNextW@20 +RpcNsProfileEltRemoveA@20 +RpcNsProfileEltRemoveW@20 diff --git a/lib/libc/mingw/lib32/rpcrt4.def b/lib/libc/mingw/lib32/rpcrt4.def new file mode 100644 index 000000000..62d030499 --- /dev/null +++ b/lib/libc/mingw/lib32/rpcrt4.def @@ -0,0 +1,568 @@ +; +; Definition file of RPCRT4.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "RPCRT4.dll" +EXPORTS +I_RpcFwThisIsTheManager@0 +I_RpcInitHttpImports@4 +I_RpcOpenClientProcess@12 +I_RpcServerTurnOnOffKeepalives@16 +I_RpcVerifierCorruptionExpected@0 +NdrFullPointerFree@8 +NdrFullPointerInsertRefId@12 +NdrFullPointerQueryPointer@16 +NdrFullPointerQueryRefId@16 +NdrGetBaseInterfaceFromStub@12 +RpcCertMatchPrincipalName@8 +CStdStubBuffer_AddRef@4 +CStdStubBuffer_Connect@8 +CStdStubBuffer_CountRefs@4 +CStdStubBuffer_DebugServerQueryInterface@8 +CStdStubBuffer_DebugServerRelease@8 +CStdStubBuffer_Disconnect@4 +CStdStubBuffer_Invoke@12 +CStdStubBuffer_IsIIDSupported@8 +CStdStubBuffer_QueryInterface@12 +CreateProxyFromTypeInfo@20 +CreateStubFromTypeInfo@16 +DceErrorInqTextA@8 +DceErrorInqTextW@8 +DllGetClassObject@12 +DllRegisterServer@0 +IUnknown_AddRef_Proxy@4 +IUnknown_QueryInterface_Proxy@12 +IUnknown_Release_Proxy@4 +I_RpcAllocate@4 +I_RpcAsyncAbortCall@8 +I_RpcAsyncSetHandle@8 +I_RpcBCacheAllocate@4 +I_RpcBCacheFree@4 +I_RpcBindingCopy@8 +I_RpcBindingInqDynamicEndpoint@8 +I_RpcBindingCreateNP@16 +I_RpcBindingHandleToAsyncHandle@8 +I_RpcBindingInqDynamicEndpoint@8 +I_RpcBindingInqDynamicEndpointA@8 +I_RpcBindingInqDynamicEndpointW@8 +I_RpcBindingInqLocalClientPID@8 +I_RpcBindingInqMarshalledTargetInfo@12 +I_RpcBindingInqSecurityContext@8 +I_RpcBindingInqSecurityContextKeyInfo@8 +I_RpcBindingInqTransportType@8 +I_RpcBindingInqWireIdForSnego@8 +I_RpcBindingIsClientLocal@8 +I_RpcBindingToStaticStringBindingW@8 +I_RpcClearMutex@4 +I_RpcConnectionInqSockBuffSize2@4 +I_RpcCompleteAndFree@12 +I_RpcConnectionInqSockBuffSize@8 +I_RpcConnectionSetSockBuffSize@8 +I_RpcDeleteMutex@4 +I_RpcEnableWmiTrace@8 +I_RpcExceptionFilter@4 +I_RpcFilterDCOMActivation@20 +I_RpcFree@4 +I_RpcFreeBuffer@4 +I_RpcGetAssociationContext@4 +I_RpcFreePipeBuffer@4 +I_RpcGetBuffer@4 +I_RpcGetBufferWithObject@8 +I_RpcGetCurrentCallHandle@0 +I_RpcIOAlerted@4 +I_RpcGetExtendedError@0 +I_RpcGetPortAllocationData@4 +I_RpcIfInqTransferSyntaxes@16 +I_RpcLogEvent@28 +I_RpcMapWin32Status@4 +I_RpcMonitorAssociation@12 +I_RpcMarshalBindingHandleAndInterfaceForNDF@24 +I_RpcNDRCGetWireRepresentation@8 +I_RpcNDRSContextEmergencyCleanup@8 +I_RpcNegotiateTransferSyntax@4 +I_RpcNsBindingSetEntryName@12 +I_RpcNsBindingSetEntryNameA@12 +I_RpcNsBindingSetEntryNameW@12 +I_RpcNsInterfaceExported@12 +I_RpcNsInterfaceUnexported@12 +I_RpcParseSecurity@8 +I_RpcPauseExecution@4 +I_RpcReallocPipeBuffer@8 +I_RpcReceive@8 +I_RpcRecordCalloutFailure@12 +I_RpcRequestMutex@4 +I_RpcSNCHOption@8 +I_RpcSend@4 +I_RpcSendReceive@4 +I_RpcServerAllocateIpPort@8 +I_RpcServerCheckClientRestriction@4 +I_RpcServerInqAddressChangeFn@0 +I_RpcServerInqLocalConnAddress@16 +I_RpcServerInqRemoteConnAddress@16 +I_RpcServerInqTransportType@4 +I_RpcServerIsClientDisconnected@8 +I_RpcServerRegisterForwardFunction@4 +I_RpcSetAssociationContext@4 +I_RpcServerSetAddressChangeFn@4 +I_RpcServerStartService@12 +I_RpcServerUseProtseq2A@20 +I_RpcServerUseProtseq2W@20 +I_RpcServerUseProtseqEp2A@24 +I_RpcServerUseProtseqEp2W@24 +I_RpcSessionStrictContextHandle@0 +I_RpcSetDCOMAppId@4 +I_RpcSsDontSerializeContext@0 +I_RpcStopMonitorAssociation@4 +I_RpcTransClientMaxFrag@4 +I_RpcTransClientReallocBuffer@16 +I_RpcTransServerFindConnection@8 +I_RpcTransServerFreeBuffer@8 +I_RpcTransServerMaxFrag@4 +I_RpcTransServerNewConnection@12 +I_RpcTransServerProtectThread@0 +I_RpcTransServerReallocBuffer@16 +I_RpcTransServerReceiveDirectReady@4 +I_RpcTransServerUnprotectThread@4 +I_RpcSystemFunction001@12 +I_RpcTransConnectionAllocatePacket@8 +I_RpcTransConnectionFreePacket@8 +I_RpcTransConnectionReallocPacket@16 +I_RpcTransDatagramAllocate2@16 +I_RpcTransDatagramAllocate@16 +I_RpcTransDatagramFree@8 +I_RpcTransGetThreadEvent@0 +I_RpcTransGetThreadEventThreadOptional@0 +I_RpcTransIoCancelled@8 +I_RpcTransServerNewConnection@4 +I_RpcTurnOnEEInfoPropagation@0 +I_UuidCreate@4 +MIDL_wchar_strcpy@8 +MIDL_wchar_strlen@4 +MesBufferHandleReset@24 +MesDecodeBufferHandleCreate@12 +MesDecodeIncrementalHandleCreate@12 +MesEncodeDynBufferHandleCreate@12 +MesEncodeFixedBufferHandleCreate@16 +MesEncodeIncrementalHandleCreate@16 +MesHandleFree@4 +MesIncrementalHandleReset@24 +MesInqProcEncodingId@12 +NDRCContextBinding@4 +NDRCContextMarshall@8 +NDRCContextUnmarshall@16 +NDRSContextMarshall2@24 +NDRSContextMarshall@12 +NDRSContextMarshallEx@16 +NDRSContextUnmarshall2@20 +NDRSContextUnmarshall@8 +NDRSContextUnmarshallEx@12 +NDRcopy@12 +NdrAllocate@8 +NdrAsyncClientCall@0 +NdrAsyncServerCall@4 +NdrByteCountPointerBufferSize@12 +NdrByteCountPointerFree@12 +NdrByteCountPointerMarshall@12 +NdrByteCountPointerUnmarshall@16 +NdrCStdStubBuffer2_Release@8 +NdrCStdStubBuffer_Release@8 +NdrClearOutParameters@12 +NdrClientCall +NdrClientCall2 +NdrClientContextMarshall@12 +NdrClientContextUnmarshall@12 +NdrClientInitialize@16 +NdrClientInitializeNew@16 +NdrComplexArrayBufferSize@12 +NdrComplexArrayFree@12 +NdrComplexArrayMarshall@12 +NdrComplexArrayMemorySize@8 +NdrComplexArrayUnmarshall@16 +NdrComplexStructBufferSize@12 +NdrComplexStructFree@12 +NdrComplexStructMarshall@12 +NdrComplexStructMemorySize@8 +NdrComplexStructUnmarshall@16 +NdrConformantArrayBufferSize@12 +NdrConformantArrayFree@12 +NdrConformantArrayMarshall@12 +NdrConformantArrayMemorySize@8 +NdrConformantArrayUnmarshall@16 +NdrConformantStringBufferSize@12 +NdrConformantStringMarshall@12 +NdrConformantStringMemorySize@8 +NdrConformantStringUnmarshall@16 +NdrConformantStructBufferSize@12 +NdrConformantStructFree@12 +NdrConformantStructMarshall@12 +NdrConformantStructMemorySize@8 +NdrConformantStructUnmarshall@16 +NdrConformantVaryingArrayBufferSize@12 +NdrConformantVaryingArrayFree@12 +NdrConformantVaryingArrayMarshall@12 +NdrConformantVaryingArrayMemorySize@8 +NdrConformantVaryingArrayUnmarshall@16 +NdrConformantVaryingStructBufferSize@12 +NdrConformantVaryingStructFree@12 +NdrConformantVaryingStructMarshall@12 +NdrConformantVaryingStructMemorySize@8 +NdrConformantVaryingStructUnmarshall@16 +NdrContextHandleInitialize@8 +NdrContextHandleSize@12 +NdrConvert2@12 +NdrConvert@8 +NdrCorrelationFree@4 +NdrCorrelationInitialize@16 +NdrCorrelationPass@4 +NdrCreateServerInterfaceFromStub@8 +NdrDcomAsyncClientCall@0 +NdrDcomAsyncStubCall@16 +NdrDllCanUnloadNow@4 +NdrDllGetClassObject@24 +NdrDllRegisterProxy@12 +NdrDllUnregisterProxy@12 +NdrEncapsulatedUnionBufferSize@12 +NdrEncapsulatedUnionFree@12 +NdrEncapsulatedUnionMarshall@12 +NdrEncapsulatedUnionMemorySize@8 +NdrEncapsulatedUnionUnmarshall@16 +NdrFixedArrayBufferSize@12 +NdrFixedArrayFree@12 +NdrFixedArrayMarshall@12 +NdrFixedArrayMemorySize@8 +NdrFixedArrayUnmarshall@16 +NdrFreeBuffer@4 +NdrFullPointerFree@8 +NdrFullPointerInsertRefId@12 +NdrFullPointerQueryPointer@16 +NdrFullPointerQueryRefId@16 +NdrFullPointerInsertRefId@12 +NdrFullPointerQueryPointer@16 +NdrFullPointerQueryRefId@16 +NdrFullPointerXlatFree@4 +NdrFullPointerXlatInit@8 +NdrGetBuffer@12 +NdrHardStructBufferSize@12 +NdrHardStructFree@12 +NdrHardStructMarshall@12 +NdrHardStructMemorySize@8 +NdrHardStructUnmarshall@16 +NdrGetDcomProtocolVersion@8 +NdrGetSimpleTypeBufferAlignment@4 +NdrGetSimpleTypeBufferSize@4 +NdrGetSimpleTypeMemorySize@4 +NdrGetTypeFlags@4 +NdrGetUserMarshalInfo@12 +NdrInterfacePointerBufferSize@12 +NdrInterfacePointerFree@12 +NdrInterfacePointerMarshall@12 +NdrInterfacePointerMemorySize@8 +NdrInterfacePointerUnmarshall@16 +NdrMapCommAndFaultStatus@16 +NdrMesProcEncodeDecode +NdrMesProcEncodeDecode2 +NdrMesSimpleTypeAlignSize@4 +NdrMesSimpleTypeDecode@12 +NdrMesSimpleTypeEncode@16 +NdrMesTypeAlignSize2@20 +NdrMesTypeAlignSize@16 +NdrMesTypeDecode2@20 +NdrMesTypeDecode@16 +NdrMesTypeEncode2@20 +NdrMesTypeEncode@16 +NdrMesTypeFree2@20 +NdrNonConformantStringBufferSize@12 +NdrNonConformantStringMarshall@12 +NdrNonConformantStringMemorySize@8 +NdrNonConformantStringUnmarshall@16 +NdrNonEncapsulatedUnionBufferSize@12 +NdrNonEncapsulatedUnionFree@12 +NdrNonEncapsulatedUnionMarshall@12 +NdrNonEncapsulatedUnionMemorySize@8 +NdrNonEncapsulatedUnionUnmarshall@16 +NdrNsGetBuffer@12 +NdrNsSendReceive@12 +NdrOleAllocate@4 +NdrOleFree@4 +NdrOutInit@12 +NdrPartialIgnoreClientBufferSize@8 +NdrPartialIgnoreClientMarshall@8 +NdrPartialIgnoreServerInitialize@12 +NdrPartialIgnoreServerUnmarshall@8 +NdrPointerBufferSize@12 +NdrPointerFree@12 +NdrPointerMarshall@12 +NdrPointerMemorySize@8 +NdrPointerUnmarshall@16 +NdrProxyErrorHandler@4 +NdrProxyFreeBuffer@8 +NdrProxyGetBuffer@8 +NdrProxyInitialize@20 +NdrProxySendReceive@8 +NdrRangeUnmarshall@16 +NdrRpcSmClientAllocate@4 +NdrRpcSmClientFree@4 +NdrRpcSmSetClientToOsf@4 +NdrRpcSsDefaultAllocate@4 +NdrRpcSsDefaultFree@4 +NdrRpcSsDisableAllocate@4 +NdrRpcSsEnableAllocate@4 +NdrSendReceive@8 +NdrServerCall2@4 +NdrServerCall@4 +NdrServerContextMarshall@12 +NdrServerContextNewMarshall@16 +NdrServerContextNewUnmarshall@8 +NdrServerContextUnmarshall@4 +NdrServerInitialize@12 +NdrServerInitializeMarshall@8 +NdrServerInitializeNew@12 +NdrServerInitializePartial@16 +NdrServerInitializeUnmarshall@12 +NdrServerMarshall@16 +NdrServerUnmarshall@24 +NdrSimpleStructBufferSize@12 +NdrSimpleStructFree@12 +NdrSimpleStructMarshall@12 +NdrSimpleStructMemorySize@8 +NdrSimpleStructUnmarshall@16 +NdrSimpleTypeMarshall@12 +NdrSimpleTypeUnmarshall@12 +NdrStubCall2@16 +NdrStubCall@16 +NdrStubForwardingFunction@16 +NdrStubGetBuffer@12 +NdrStubInitialize@16 +NdrStubInitializeMarshall@12 +NdrTypeFlags@60029 +NdrTypeFree@12 +NdrTypeMarshall@12 +NdrTypeSize@12 +NdrTypeUnmarshall@16 +NdrUnmarshallBasetypeInline@12 +NdrUserMarshalBufferSize@12 +NdrUserMarshalFree@12 +NdrUserMarshalMarshall@12 +NdrUserMarshalMemorySize@8 +NdrUserMarshalSimpleTypeConvert@12 +NdrUserMarshalUnmarshall@16 +NdrVaryingArrayBufferSize@12 +NdrVaryingArrayFree@12 +NdrVaryingArrayMarshall@12 +NdrVaryingArrayMemorySize@8 +NdrVaryingArrayUnmarshall@16 +NdrXmitOrRepAsBufferSize@12 +NdrXmitOrRepAsFree@12 +NdrXmitOrRepAsMarshall@12 +NdrXmitOrRepAsMemorySize@8 +NdrXmitOrRepAsUnmarshall@16 +NdrpCreateProxy@16 +NdrpCreateStub@12 +NdrpGetProcFormatString@24 +NdrpGetTypeFormatString@12 +NdrpGetTypeGenCookie@4 +NdrpMemoryIncrement@12 +NdrpReleaseTypeFormatString@4 +NdrpReleaseTypeGenCookie@4 +NdrpVarVtOfTypeDesc@12 +RpcAsyncAbortCall@8 +RpcAsyncCancelCall@8 +RpcAsyncCompleteCall@8 +RpcAsyncGetCallStatus@4 +RpcAsyncInitializeHandle@8 +RpcAsyncRegisterInfo@4 +RpcBindingBind@12 +RpcBindingCopy@8 +RpcBindingCreateA@16 +RpcBindingCreateW@16 +RpcBindingFree@4 +RpcBindingFromStringBindingA@8 +RpcBindingFromStringBindingW@8 +RpcBindingInqAuthClientA@24 +RpcBindingInqAuthClientExA@28 +RpcBindingInqAuthClientExW@28 +RpcBindingInqAuthClientW@24 +RpcBindingInqAuthInfoA@24 +RpcBindingInqAuthInfoExA@32 +RpcBindingInqAuthInfoExW@32 +RpcBindingInqAuthInfoW@24 +RpcBindingInqObject@8 +RpcBindingInqOption@12 +RpcBindingReset@4 +RpcBindingServerFromClient@8 +RpcBindingSetAuthInfoA@24 +RpcBindingSetAuthInfoExA@28 +RpcBindingSetAuthInfoExW@28 +RpcBindingSetAuthInfoW@24 +RpcBindingSetObject@8 +RpcBindingSetOption@12 +RpcBindingToStringBindingA@8 +RpcBindingToStringBindingW@8 +RpcBindingUnbind@4 +RpcBindingVectorFree@4 +RpcCancelThread@4 +RpcCancelThreadEx@8 +RpcCertGeneratePrincipalNameA@12 +RpcCertGeneratePrincipalNameW@12 +RpcEpRegisterA@16 +RpcEpRegisterNoReplaceA@16 +RpcEpRegisterNoReplaceW@16 +RpcEpRegisterW@16 +RpcEpResolveBinding@8 +RpcEpUnregister@12 +RpcErrorAddRecord@4 +RpcErrorClearInformation@0 +RpcErrorEndEnumeration@4 +RpcErrorGetNextRecord@12 +RpcErrorGetNumberOfRecords@8 +RpcErrorLoadErrorInfo@12 +RpcErrorResetEnumeration@4 +RpcErrorSaveErrorInfo@12 +RpcErrorStartEnumeration@4 +RpcExceptionFilter@4 +RpcFreeAuthorizationContext@4 +RpcGetAuthorizationContextForClient@36 +RpcIfIdVectorFree@4 +RpcIfInqId@8 +RpcImpersonateClient@4 +RpcMgmtEnableIdleCleanup@0 +RpcMgmtEpEltInqBegin@24 +RpcMgmtEpEltInqDone@4 +RpcMgmtEpEltInqNextA@20 +RpcMgmtEpEltInqNextW@20 +RpcMgmtEpUnregister@16 +RpcMgmtInqComTimeout@8 +RpcMgmtInqDefaultProtectLevel@8 +RpcMgmtInqIfIds@8 +RpcMgmtInqServerPrincNameA@12 +RpcMgmtInqServerPrincNameW@12 +RpcMgmtInqStats@8 +RpcMgmtIsServerListening@4 +RpcMgmtSetAuthorizationFn@4 +RpcMgmtSetCancelTimeout@4 +RpcMgmtSetComTimeout@8 +RpcMgmtSetServerStackSize@4 +RpcMgmtStatsVectorFree@4 +RpcMgmtStopServerListening@4 +RpcMgmtWaitServerListen@0 +RpcNetworkInqProtseqsA@4 +RpcNetworkInqProtseqsW@4 +RpcNetworkIsProtseqValidA@4 +RpcNetworkIsProtseqValidW@4 +RpcNsBindingInqEntryNameA@12 +RpcNsBindingInqEntryNameW@12 +RpcObjectInqType@8 +RpcObjectSetInqFn@4 +RpcObjectSetType@8 +RpcProtseqVectorFreeA@4 +RpcProtseqVectorFreeW@4 +RpcRaiseException@4 +RpcRevertToSelf@0 +RpcRevertToSelfEx@4 +RpcServerCompleteSecurityCallback@8 +RpcServerInqBindingHandle@4 +RpcServerInqBindings@4 +RpcServerInqCallAttributesA@8 +RpcServerInqCallAttributesW@8 +RpcServerInqDefaultPrincNameA@8 +RpcServerInqDefaultPrincNameW@8 +RpcServerInqIf@12 +RpcServerListen@12 +RpcServerRegisterAuthInfoA@16 +RpcServerRegisterAuthInfoW@16 +RpcServerRegisterIf2@28 +RpcServerRegisterIf@12 +RpcServerRegisterIfEx@24 +RpcServerSubscribeForNotification@16 +RpcServerTestCancel@4 +RpcServerUnregisterIf@12 +RpcServerUnregisterIfEx@12 +RpcServerUnsubscribeForNotification@12 +RpcServerUseAllProtseqs@8 +RpcServerUseAllProtseqsEx@12 +RpcServerUseAllProtseqsIf@12 +RpcServerUseAllProtseqsIfEx@16 +RpcServerUseProtseqA@12 +RpcServerUseProtseqEpA@16 +RpcServerUseProtseqEpExA@20 +RpcServerUseProtseqEpExW@20 +RpcServerUseProtseqEpW@16 +RpcServerUseProtseqExA@16 +RpcServerUseProtseqExW@16 +RpcServerUseProtseqIfA@16 +RpcServerUseProtseqIfExA@20 +RpcServerUseProtseqIfExW@20 +RpcServerUseProtseqIfW@16 +RpcServerUseProtseqW@12 +RpcServerYield@0 +RpcSmAllocate@8 +RpcSmClientFree@4 +RpcSmDestroyClientContext@4 +RpcSmDisableAllocate@0 +RpcSmEnableAllocate@0 +RpcSmFree@4 +RpcSmGetThreadHandle@4 +RpcSmSetClientAllocFree@8 +RpcSmSetThreadHandle@4 +RpcSmSwapClientAllocFree@16 +RpcSsAllocate@4 +RpcSsContextLockExclusive@8 +RpcSsContextLockShared@8 +RpcSsDestroyClientContext@4 +RpcSsDisableAllocate@0 +RpcSsDontSerializeContext@0 +RpcSsEnableAllocate@0 +RpcSsFree@4 +RpcSsGetContextBinding@8 +RpcSsGetThreadHandle@0 +RpcSsSetClientAllocFree@8 +RpcSsSetThreadHandle@4 +RpcSsSwapClientAllocFree@16 +RpcStringBindingComposeA@24 +RpcStringBindingComposeW@24 +RpcStringBindingParseA@24 +RpcStringBindingParseW@24 +RpcStringFreeA@4 +RpcStringFreeW@4 +RpcTestCancel@0 +RpcUserFree@8 +SimpleTypeAlignment@1526 +SimpleTypeBufferSize@1526 +SimpleTypeMemorySize@1526 +TowerConstruct@24 +TowerExplode@24 +UuidCompare@12 +UuidCreate@4 +UuidCreateNil@4 +UuidCreateSequential@4 +UuidEqual@12 +UuidFromStringA@8 +UuidFromStringW@8 +UuidHash@8 +UuidIsNil@8 +UuidToStringA@8 +UuidToStringW@8 +char_array_from_ndr@16 +char_from_ndr@8 +data_from_ndr@16 +data_into_ndr@16 +data_size_ndr@16 +double_array_from_ndr@16 +double_from_ndr@8 +enum_from_ndr@8 +float_array_from_ndr@16 +float_from_ndr@8 +long_array_from_ndr@16 +long_from_ndr@8 +long_from_ndr_temp@12 +pfnFreeRoutines DATA +pfnMarshallRoutines DATA +pfnSizeRoutines DATA +pfnUnmarshallRoutines DATA +short_array_from_ndr@16 +short_from_ndr@8 +short_from_ndr_temp@12 +tree_into_ndr@16 +tree_peek_ndr@16 +tree_size_ndr@16 diff --git a/lib/libc/mingw/lib32/shell32.def b/lib/libc/mingw/lib32/shell32.def new file mode 100644 index 000000000..7a366030f --- /dev/null +++ b/lib/libc/mingw/lib32/shell32.def @@ -0,0 +1,356 @@ +; +; Definition file of SHELL32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "SHELL32.dll" +EXPORTS +SHChangeNotifyRegister@24 +SHDefExtractIconA@24 +SHChangeNotifyDeregister@4 +SHDefExtractIconW@24 +PifMgr_OpenProperties@16 +PifMgr_GetProperties@20 +PifMgr_SetProperties@20 +PifMgr_CloseProperties@8 +SHStartNetConnectionDialogW@12 +ILFindLastID@4 +ILRemoveLastID@4 +ILClone@4 +ILCloneFirst@4 +ILIsEqual@8 +DAD_DragEnterEx2@16 +ILIsParent@12 +ILFindChild@8 +ILCombine@8 +ILSaveToStream@8 +SHILCreateFromPath@12 +IsLFNDriveA@4 +IsLFNDriveW@4 +PathIsExe@4 +PathMakeUniqueName@20 +PathQualify@4 +PathResolve@12 +RestartDialog@12 +PickIconDlg@16 +GetFileNameFromBrowse@28 +DriveType@4 +IsNetDrive@4 +Shell_MergeMenus@24 +SHGetSetSettings@12 +Shell_GetImageLists@8 +Shell_GetCachedImageIndex@12 +SHShellFolderView_Message@12 +SHCreateStdEnumFmtEtc@12 +PathYetAnotherMakeUniqueName@16 +SHMapPIDLToSystemImageListIndex@12 +SHOpenPropSheetW@28 +OpenAs_RunDLL@16 +CIDLData_CreateFromIDArray@16 +OpenRegStream@16 +SHDoDragDrop@20 +SHCloneSpecialIDList@12 +SHFindFiles@8 +PathGetShortPath@4 +SHGetRealIDL@12 +SHRestricted@4 +SHCoCreateInstance@20 +SignalFileOpen@4 +IsLFNDrive@4 +OpenAs_RunDLLA@16 +DAD_AutoScroll@12 +DAD_DragEnterEx@12 +DAD_DragLeave@0 +OpenAs_RunDLLW@16 +DAD_DragMove@8 +PrepareDiscForBurnRunDllW@16 +DAD_SetDragImage@8 +DAD_ShowDragImage@4 +PrintersGetCommand_RunDLL@16 +PrintersGetCommand_RunDLLA@16 +SHCLSIDFromString@8 +SHFind_InitMenuPopup@16 +PrintersGetCommand_RunDLLW@16 +ILGetSize@4 +ILGetNext@4 +ILAppendID@12 +ILFree@4 +ILCreateFromPath@4 +SHSimpleIDListFromPath@4 +Win32DeleteFile@4 +SHCreateDirectory@8 +SHAddFromPropSheetExtArray@12 +SHCreatePropSheetExtArray@12 +SHDestroyPropSheetExtArray@4 +SHReplaceFromPropSheetExtArray@16 +PathCleanupSpec@8 +SHValidateUNC@12 +SHCreateShellFolderViewEx@8 +SHSetInstanceExplorer@4 +SHObjectProperties@16 +SHGetNewLinkInfoA@20 +SHGetNewLinkInfoW@20 +ShellMessageBoxW +ShellMessageBoxA +ILCreateFromPathA@4 +ILCreateFromPathW@4 +SHUpdateImageA@16 +SHUpdateImageW@16 +SHHandleUpdateImage@4 +SHFree@4 +SHAlloc@4 +SHHelpShortcuts_RunDLL@16 +SHHelpShortcuts_RunDLLA@16 +SHHelpShortcuts_RunDLLW@16 +AppCompat_RunDLLW@16 +AssocCreateForClasses@16 +AssocGetDetailsOfPropKey@20 +CheckEscapesW@8 +SHSetFolderPathA@16 +SHSetFolderPathW@16 +CommandLineToArgvW@8 +PathIsSlowW@8 +PathIsSlowA@8 +SHTestTokenMembership@8 +Control_RunDLL@16 +SHCreateShellFolderView@8 +Control_RunDLLA@16 +Control_RunDLLAsUserW@16 +Control_RunDLLW@16 +DllCanUnloadNow@0 +DllGetClassObject@12 +DllGetVersion@4 +DllInstall@8 +DllRegisterServer@0 +DllUnregisterServer@0 +DoEnvironmentSubstA@8 +DoEnvironmentSubstW@8 +DragAcceptFiles@8 +DragFinish@4 +DragQueryFile@16 +DragQueryFileA@16 +DragQueryFileAorW@24 +DragQueryFileW@16 +DragQueryPoint@8 +DuplicateIcon@8 +ExtractAssociatedIconA@12 +ExtractAssociatedIconExA@16 +ExtractAssociatedIconExW@16 +ExtractAssociatedIconW@12 +ExtractIconA@12 +ExtractIconEx@20 +ExtractIconExA@20 +ExtractIconExW@20 +ExtractIconW@12 +FindExecutableA@12 +FindExecutableW@12 +FreeIconList@8 +GetCurrentProcessExplicitAppUserModelID@4 +InitNetworkAddressControl@0 +InternalExtractIconListA@12 +InternalExtractIconListW@12 +LaunchMSHelp_RunDLLW@16 +Options_RunDLL@16 +Options_RunDLLA@16 +Options_RunDLLW@16 +RealShellExecuteA@40 +RealShellExecuteExA@44 +RealShellExecuteExW@44 +RealShellExecuteW@40 +RegenerateUserEnvironment@8 +RunAsNewUser_RunDLLW@16 +SHAddDefaultPropertiesByExt@8 +SHAddToRecentDocs@8 +SHAppBarMessage@8 +SHAssocEnumHandlers@12 +SHAssocEnumHandlersForProtocolByApplication@12 +SHBindToFolderIDListParent@20 +SHBindToFolderIDListParentEx@24 +SHBindToObject@20 +SHBindToParent@16 +SHBrowseForFolder@4 +SHBrowseForFolderA@4 +SHBrowseForFolderW@4 +SHChangeNotify@16 +SHChangeNotifyRegisterThread@4 +SHChangeNotifySuspendResume@16 +SHCreateAssociationRegistration@8 +SHCreateDataObject@24 +SHCreateDefaultContextMenu@12 +SHCreateDefaultExtractIcon@8 +SHCreateDefaultPropertiesOp@8 +SHCreateDirectoryExA@12 +SHCreateDirectoryExW@12 +SHCreateItemFromIDList@12 +SHCreateItemFromParsingName@16 +SHCreateItemFromRelativeName@20 +SHCreateItemInKnownFolder@20 +SHCreateItemWithParent@20 +SHCreateLocalServerRunDll@16 +SHCreateProcessAsUserW@4 +SHCreateQueryCancelAutoPlayMoniker@4 +SHCreateShellItem@16 +SHCreateShellItemArray@20 +SHCreateShellItemArrayFromDataObject@12 +SHCreateShellItemArrayFromIDLists@12 +SHCreateShellItemArrayFromShellItem@12 +SHEmptyRecycleBinA@12 +SHEmptyRecycleBinW@12 +SHEnableServiceObject@8 +SHEnumerateUnreadMailAccountsW@16 +SHEvaluateSystemCommandTemplate@16 +SHExtractIconsW@32 +SHFileOperation@4 +SHFileOperationA@4 +SHFileOperationW@4 +SHFormatDrive@16 +SHFreeNameMappings@4 +SHGetDataFromIDListA@20 +SHGetDataFromIDListW@20 +SHGetDesktopFolder@4 +SHGetDiskFreeSpaceA@16 +SHGetDiskFreeSpaceExA@16 +SHGetDiskFreeSpaceExW@16 +SHGetDriveMedia@8 +SHGetFileInfo@20 +SHGetFileInfoA@20 +SHGetFileInfoW@20 +SHGetFolderLocation@20 +SHGetFolderPathA@20 +SHGetFolderPathAndSubDirA@24 +SHGetFolderPathAndSubDirW@24 +SHGetFolderPathEx@20 +SHGetFolderPathW@20 +SheShortenPathW@8 +SheShortenPathA@8 +SHGetIDListFromObject@8 +SHGetIconOverlayIndexA@8 +SHGetIconOverlayIndexW@8 +SHGetInstanceExplorer@4 +SHGetItemFromDataObject@16 +SHGetItemFromObject@12 +SHGetKnownFolderIDList@16 +SHGetKnownFolderItem@20 +SHGetKnownFolderPath@16 +SHGetLocalizedName@16 +SHGetMalloc@4 +SHGetNameFromIDList@12 +SHGetNewLinkInfo@20 +SHGetPathFromIDList@8 +SHGetPathFromIDListA@8 +SHGetPathFromIDListEx@16 +SHGetPathFromIDListW@8 +SHGetPropertyStoreForWindow@12 +SHGetPropertyStoreFromIDList@16 +SHGetPropertyStoreFromParsingName@20 +SHGetSettings@8 +SHGetSpecialFolderLocation@12 +SHGetSpecialFolderPathA@16 +SHGetSpecialFolderPathW@16 +SHGetStockIconInfo@12 +SHGetTemporaryPropertyForItem@12 +SHGetUnreadMailCountW@24 +SHInvokePrinterCommandA@20 +SHInvokePrinterCommandW@20 +SHIsFileAvailableOffline@8 +SHLoadInProc@4 +SHLoadNonloadedIconOverlayIdentifiers@0 +SHOpenFolderAndSelectItems@16 +SHOpenWithDialog@8 +SHParseDisplayName@20 +SHPathPrepareForWriteA@16 +SHPathPrepareForWriteW@16 +SHQueryRecycleBinA@8 +SHQueryRecycleBinW@8 +SHQueryUserNotificationState@4 +SHRemoveLocalizedName@4 +SHResolveLibrary@4 +SHSetDefaultProperties@16 +SHSetKnownFolderPath@16 +SHSetLocalizedName@12 +SHSetTemporaryPropertyForItem@12 +SHSetUnreadMailCountW@12 +SHShowManageLibraryUI@20 +SHUpdateRecycleBinIcon@0 +SetCurrentProcessExplicitAppUserModelID@4 +SheChangeDirA@4 +SheChangeDirExW@4 +SheChangeDirExA@4 +SheGetDirA@8 +SheSetCurDrive@4 +SheRemoveQuotesW@4 +SheRemoveQuotesA@4 +ShellAboutA@16 +ShellAboutW@16 +ShellExec_RunDLL@16 +ShellExec_RunDLLA@16 +ShellExec_RunDLLW@16 +ShellExecuteA@24 +ShellExecuteEx@4 +ShellExecuteExA@4 +ShellExecuteExW@4 +ShellExecuteW@24 +ShellHookProc@12 +Shell_GetCachedImageIndexA@12 +Shell_GetCachedImageIndexW@12 +Shell_NotifyIcon@8 +Shell_NotifyIconA@8 +Shell_NotifyIconGetRect@8 +Shell_NotifyIconW@8 +SheGetPathOffsetW@4 +SheGetCurDrive@0 +SheGetDirW@8 +SheFullPathW@12 +SheFullPathA@12 +SheConvertPathW@12 +SheChangeDirW@4 +ExtractIconW@ +ExtractIconResInfoW@20 +ExtractIconResInfoA@20 +SheGetDirExW@12 +StrChrA@8 +StrChrIA@8 +StrChrIW@8 +StrChrW@8 +StrCmpNA@12 +StrCmpNIA@12 +StrCmpNIW@12 +StrCmpNW@12 +StrNCmpA@12 +StrNCmpIA@12 +StrNCmpIW@12 +StrNCmpW@12 +StrRChrA@12 +StrRChrIA@12 +StrRChrIW@12 +StrRChrW@12 +StrRStrA@12 +StrRStrIA@12 +StrRStrIW@12 +StrRStrW@12 +StrStrA@8 +StrStrIA@8 +StrStrIW@8 +StrStrW@8 +WOWShellExecute@28 +WaitForExplorerRestartW@16 +RealDriveType@8 +SHFlushSFCache@0 +SHChangeNotification_Lock@16 +SHChangeNotification_Unlock@4 +WriteCabinetState@4 +ReadCabinetState@8 +IsUserAnAdmin@0 +StgMakeUniqueName@20 +SHPropStgCreate@32 +SHPropStgReadMultiple@20 +SHPropStgWriteMultiple@24 +CDefFolderMenu_Create2@36 +SHGetSetFolderCustomSettings@12 +SHMultiFileProperties@8 +SHGetImageList@12 +RestartDialogEx@16 +SHCreateFileExtractIconW@16 +SHLimitInputEdit@8 +SHGetAttributesFromDataObject@16 +ILLoadFromStreamEx@8 diff --git a/lib/libc/mingw/lib32/urlmon.def b/lib/libc/mingw/lib32/urlmon.def new file mode 100644 index 000000000..31e750d76 --- /dev/null +++ b/lib/libc/mingw/lib32/urlmon.def @@ -0,0 +1,230 @@ +; +; Definition file of urlmon.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "urlmon.dll" +EXPORTS +ord_100@16 @100 +ord_101 @101 +ord_102 @102 +ord_103@8 @103 +ord_104@24 @104 +ord_105@12 @105 +ord_106@4 @106 +ord_107@8 @107 +ord_108 @108 +AsyncGetClassBits@44 +AsyncInstallDistributionUnit@36 +ord_111@8 @111 +ord_112@8 @112 +ord_113@8 @113 +ord_114 @114 +ord_115@4 @115 +ord_116@8 @116 +BindAsyncMoniker@20 +CDLGetLongPathNameA@12 +CDLGetLongPathNameW@12 +CORPolicyProvider@4 +CoGetClassObjectFromURL@40 +CoInstall@20 +CoInternetCanonicalizeIUri@12 +CoInternetCombineIUri@20 +CoInternetCombineUrl@28 +CoInternetCombineUrlEx@20 +CoInternetCompareUrl@12 +CoInternetCreateSecurityManager@12 +CoInternetCreateZoneManager@12 +CoInternetFeatureSettingsChanged +CoInternetGetProtocolFlags@12 +CoInternetGetSecurityUrl@16 +CoInternetGetSecurityUrlEx@16 +CoInternetGetSession@12 +CoInternetIsFeatureEnabled@8 +CoInternetIsFeatureEnabledForIUri@16 +CoInternetIsFeatureEnabledForUrl@16 +CoInternetIsFeatureZoneElevationEnabled@16 +CoInternetParseIUri@28 +CoInternetParseUrl@28 +CoInternetQueryInfo@28 +CoInternetSetFeatureEnabled@12 +CompareSecurityIds@20 +CompatFlagsFromClsid@12 +CopyBindInfo@8 +CopyStgMedium@8 +CreateAsyncBindCtx@16 +CreateAsyncBindCtxEx@24 +CreateFormatEnumerator@12 +CreateIUriBuilder@16 +CreateURLMoniker@12 +CreateURLMonikerEx2@16 +CreateURLMonikerEx@16 +CreateUri@16 +CreateUriFromMultiByteString@24 +CreateUriPriv@32 +CreateUriWithFragment@20 +Extract@8 +FaultInIEFeature@16 +FindMediaType@8 +FindMediaTypeClass@16 +FindMimeFromData@32 +GetAddSitesFileUrl@4 +GetClassFileOrMime@28 +GetClassURL@8 +GetComponentIDFromCLSSPEC@8 +GetIDNFlagsForUri@4 +GetIUriPriv2@8 +GetIUriPriv@8 +GetLabelsFromNamedHost@20 +GetMarkOfTheWeb@16 +GetPortFromUrlScheme@8 +GetPropertyFromName@8 +GetPropertyName@4 +GetSoftwareUpdateInfo@8 +GetUrlmonThreadNotificationHwnd@0 +HlinkGoBack@4 +HlinkGoForward@4 +HlinkNavigateMoniker@8 +HlinkNavigateString@8 +HlinkSimpleNavigateToMoniker@32 +HlinkSimpleNavigateToString@32 +IEDllLoader@8 +IEInstallScope@4 +IntlPercentEncodeNormalize@16 +IsAsyncMoniker@4 +IsDWORDProperty@4 +IsIntranetAvailable +IsJITInProgress +IsLoggingEnabledA@4 +IsLoggingEnabledW@4 +IsStringProperty@4 +IsValidURL@12 +MkParseDisplayNameEx@16 +ObtainUserAgentString@12 +PrivateCoInstall@20 +QueryAssociations@16 +QueryClsidAssociation@12 +RegisterBindStatusCallback@16 +RegisterFormatEnumerator@12 +RegisterMediaTypeClass@20 +RegisterMediaTypes@12 +ReleaseBindInfo@4 +ResetUrlmonLanguageData@0 +RevokeBindStatusCallback@8 +RevokeFormatEnumerator@8 +SetSoftwareUpdateAdvertisementState@16 +ShouldDisplayPunycodeForUri@4 +ShouldShowIntranetWarningSecband@4 +ShowTrustAlertDialog@16 +URLDownloadA@20 +URLDownloadToCacheFileA@24 +URLDownloadToCacheFileW@24 +URLDownloadToFileA@20 +URLDownloadToFileW@20 +URLDownloadW@20 +URLOpenBlockingStreamA@20 +URLOpenBlockingStreamW@20 +URLOpenPullStreamA@16 +URLOpenPullStreamW@16 +URLOpenStreamA@16 +URLOpenStreamW@16 +UrlMkBuildVersion +UrlMkGetSessionOption@20 +UrlMkSetSessionOption@16 +WriteHitLogging@4 +ZonesReInit@4 +ord_304@28 @304 +ord_305@12 @305 +ord_306@4 @306 +ord_307@20 @307 +ord_308@4 @308 +ord_309@4 @309 +ord_310@12 @310 +ord_311@12 @311 +ord_312@4 @312 +ord_313@8 @313 +ord_314@4 @314 +ord_315@8 @315 +ord_316@8 @316 +ord_318@8 @318 +ord_319@8 @319 +ord_320@12 @320 +ord_321@8 @321 +IECompatLogCSSFix@12 +ord_323@12 @323 +ord_324 @324 +; ord_395 @395 Couldn't determine function argument count. Function doesn't return. +ord_400@8 @400 +ord_401@4 @401 +ord_403@4 @403 +ord_404 @404 +ord_406 @406 +ord_407 @407 +ord_408@8 @408 +ord_409@8 @409 +ord_410@8 @410 +ord_411@8 @411 +ord_412@8 @412 +ord_413@8 @413 +ord_414@8 @414 +ord_415@8 @415 +ord_416@12 @416 +ord_417@12 @417 +ord_420@24 @420 +ord_421@8 @421 +; ord_422 @422 Check!!! forwards to CloseHandle in KERNEL32.dll (ordinal 82) +ord_423@16 @423 +ord_430 @430 +ord_431@4 @431 +ord_432@8 @432 +ord_433@4 @433 +ord_434@12 @434 +ord_435@12 @435 +ord_436@8 @436 +ord_437@8 @437 +ord_438@24 @438 +ord_439@4 @439 +ord_440@4 @440 +ord_441@16 @441 +ord_442@8 @442 +ord_443@8 @443 +ord_444@12 @444 +ord_445@8 @445 +ord_446@4 @446 +ord_447@4 @447 +ord_448@4 @448 +ord_449@4 @449 +ord_450@4 @450 +ord_451@4 @451 +ord_452@8 @452 +ord_453@8 @453 +ord_454@8 @454 +ord_455 @455 +ord_456 @456 +ord_457@4 @457 +ord_458 @458 +ord_460@4 @460 +ord_461@8 @461 +ord_462@20 @462 +ord_463@20 @463 +ord_470@4 @470 +ord_471 @471 +ord_473@4 @473 +ord_474@16 @474 +ord_475@16 @475 +ord_476@16 @476 +ord_477@16 @477 +ord_478@32 @478 +ord_479@32 @479 +ord_480@36 @480 +ord_481@36 @481 +ord_482@12 @482 +ord_483@28 @483 +ord_484@8 @484 +ord_485@8 @485 +ord_486@8 @486 +ord_487@4 @487 +ord_488 @488 +ord_489@16 @489 +ord_490@44 @490 +ord_491@4 @491 diff --git a/lib/libc/mingw/lib32/winmm.def b/lib/libc/mingw/lib32/winmm.def new file mode 100644 index 000000000..4de3638e5 --- /dev/null +++ b/lib/libc/mingw/lib32/winmm.def @@ -0,0 +1,196 @@ +LIBRARY WINMM.DLL +EXPORTS +CloseDriver@12 +DefDriverProc@20 +DriverCallback@28 +DrvGetModuleHandle@4 +GetDriverModuleHandle@4 +NotifyCallbackData@20 +OpenDriver@12 +PlaySound@12 +PlaySoundA@12 +PlaySoundW@12 +SendDriverMessage@16 +WOW32DriverCallback@28 +WOW32ResolveMultiMediaHandle@24 +WOWAppExit@4 +aux32Message@20 +auxGetDevCapsA@12 +auxGetDevCapsW@12 +auxGetNumDevs@0 +auxGetVolume@8 +auxOutMessage@16 +auxSetVolume@8 +joy32Message@20 +joyConfigChanged@4 +joyGetDevCapsA@12 +joyGetDevCapsW@12 +joyGetNumDevs@0 +joyGetPos@8 +joyGetPosEx@8 +joyGetThreshold@8 +joyReleaseCapture@4 +joySetCapture@16 +joySetThreshold@8 +mci32Message@20 +mciDriverNotify@12 +mciDriverYield@4 +mciExecute@4 +mciFreeCommandResource@4 +mciGetCreatorTask@4 +mciGetDeviceIDA@4 +mciGetDeviceIDFromElementIDA@8 +mciGetDeviceIDFromElementIDW@8 +mciGetDeviceIDW@4 +mciGetDriverData@4 +mciGetErrorStringA@12 +mciGetErrorStringW@12 +mciGetYieldProc@8 +mciLoadCommandResource@12 +mciSendCommandA@16 +mciSendCommandW@16 +mciSendStringA@16 +mciSendStringW@16 +mciSetDriverData@8 +mciSetYieldProc@12 +mid32Message@20 +midiConnect@12 +midiDisconnect@12 +midiInAddBuffer@12 +midiInClose@4 +midiInGetDevCapsA@12 +midiInGetDevCapsW@12 +midiInGetErrorTextA@12 +midiInGetErrorTextW@12 +midiInGetID@8 +midiInGetNumDevs@0 +midiInMessage@16 +midiInOpen@20 +midiInPrepareHeader@12 +midiInReset@4 +midiInStart@4 +midiInStop@4 +midiInUnprepareHeader@12 +midiOutCacheDrumPatches@16 +midiOutCachePatches@16 +midiOutClose@4 +midiOutGetDevCapsA@12 +midiOutGetDevCapsW@12 +midiOutGetErrorTextA@12 +midiOutGetErrorTextW@12 +midiOutGetID@8 +midiOutGetNumDevs@0 +midiOutGetVolume@8 +midiOutLongMsg@12 +midiOutMessage@16 +midiOutOpen@20 +midiOutPrepareHeader@12 +midiOutReset@4 +midiOutSetVolume@8 +midiOutShortMsg@8 +midiOutUnprepareHeader@12 +midiStreamClose@4 +midiStreamOpen@24 +midiStreamOut@12 +midiStreamPause@4 +midiStreamPosition@12 +midiStreamProperty@12 +midiStreamRestart@4 +midiStreamStop@4 +mixerClose@4 +mixerGetControlDetailsA@12 +mixerGetControlDetailsW@12 +mixerGetDevCapsA@12 +mixerGetDevCapsW@12 +mixerGetID@12 +mixerGetLineControlsA@12 +mixerGetLineControlsW@12 +mixerGetLineInfoA@12 +mixerGetLineInfoW@12 +mixerGetNumDevs@0 +mixerMessage@16 +mixerOpen@20 +mixerSetControlDetails@12 +mmDrvInstall@12 +mmGetCurrentTask@0 +mmTaskBlock@4 +mmTaskCreate@12 +mmTaskSignal@4 +mmTaskYield@0 +mmioAdvance@12 +mmioAscend@12 +mmioClose@8 +mmioCreateChunk@12 +mmioDescend@16 +mmioFlush@8 +mmioGetInfo@12 +mmioInstallIOProcA@12 +mmioInstallIOProcW@12 +mmioOpenA@12 +mmioOpenW@12 +mmioRead@12 +mmioRenameA@16 +mmioRenameW@16 +mmioSeek@12 +mmioSendMessage@16 +mmioSetBuffer@16 +mmioSetInfo@12 +mmioStringToFOURCCA@8 +mmioStringToFOURCCW@8 +mmioWrite@12 +mmsystemGetVersion@0 +mod32Message@20 +mxd32Message@20 +sndPlaySoundA@8 +sndPlaySoundW@8 +tid32Message@20 +timeBeginPeriod@4 +timeEndPeriod@4 +timeGetDevCaps@8 +timeGetSystemTime@8 +timeGetTime@0 +timeKillEvent@4 +timeSetEvent@20 +waveInAddBuffer@12 +waveInClose@4 +waveInGetDevCapsA@12 +waveInGetDevCapsW@12 +waveInGetErrorTextA@12 +waveInGetErrorTextW@12 +waveInGetID@8 +waveInGetNumDevs@0 +waveInGetPosition@12 +waveInMessage@16 +waveInOpen@24 +waveInPrepareHeader@12 +waveInReset@4 +waveInStart@4 +waveInStop@4 +waveInUnprepareHeader@12 +waveOutBreakLoop@4 +waveOutClose@4 +waveOutGetDevCapsA@12 +waveOutGetDevCapsW@12 +waveOutGetErrorTextA@12 +waveOutGetErrorTextW@12 +waveOutGetID@8 +waveOutGetNumDevs@0 +waveOutGetPitch@8 +waveOutGetPlaybackRate@8 +waveOutGetPosition@12 +waveOutGetVolume@8 +waveOutMessage@16 +waveOutOpen@24 +waveOutPause@4 +waveOutPrepareHeader@12 +waveOutReset@4 +waveOutRestart@4 +waveOutSetPitch@8 +waveOutSetPlaybackRate@8 +waveOutSetVolume@8 +waveOutUnprepareHeader@12 +waveOutWrite@12 +wid32Message@20 +winmmDbgOut +winmmSetDebugLevel@4 +wod32Message@20 diff --git a/lib/libc/mingw/lib32/winscard.def b/lib/libc/mingw/lib32/winscard.def new file mode 100644 index 000000000..7efadb4c9 --- /dev/null +++ b/lib/libc/mingw/lib32/winscard.def @@ -0,0 +1,75 @@ +; +; Definition file of WinSCard.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "WinSCard.dll" +EXPORTS +ClassInstall32@12 +SCardAccessNewReaderEvent@0 +SCardReleaseAllEvents@0 +SCardReleaseNewReaderEvent@0 +SCardAccessStartedEvent@0 +SCardAddReaderToGroupA@12 +SCardAddReaderToGroupW@12 +SCardBeginTransaction@4 +SCardCancel@4 +SCardConnectA@24 +SCardConnectW@24 +SCardControl@28 +SCardDisconnect@8 +SCardEndTransaction@8 +SCardEstablishContext@16 +SCardForgetCardTypeA@8 +SCardForgetCardTypeW@8 +SCardForgetReaderA@8 +SCardForgetReaderGroupA@8 +SCardForgetReaderGroupW@8 +SCardForgetReaderW@8 +SCardFreeMemory@8 +SCardGetAttrib@16 +SCardGetCardTypeProviderNameA@20 +SCardGetCardTypeProviderNameW@20 +SCardGetProviderIdA@12 +SCardGetProviderIdW@12 +SCardGetStatusChangeA@16 +SCardGetStatusChangeW@16 +SCardGetTransmitCount@8 +SCardIntroduceCardTypeA@32 +SCardIntroduceCardTypeW@32 +SCardIntroduceReaderA@12 +SCardIntroduceReaderGroupA@8 +SCardIntroduceReaderGroupW@8 +SCardIntroduceReaderW@12 +SCardIsValidContext@4 +SCardListCardsA@24 +SCardListCardsW@24 +SCardListInterfacesA@16 +SCardListInterfacesW@16 +SCardListReaderGroupsA@12 +SCardListReaderGroupsW@12 +SCardListReadersA@16 +SCardListReadersW@16 +SCardLocateCardsA@16 +SCardLocateCardsByATRA@20 +SCardLocateCardsByATRW@20 +SCardLocateCardsW@16 +SCardReadCacheA@24 +SCardReadCacheW@24 +SCardReconnect@20 +SCardReleaseContext@4 +SCardReleaseStartedEvent@0 +SCardRemoveReaderFromGroupA@12 +SCardRemoveReaderFromGroupW@12 +SCardSetAttrib@16 +SCardSetCardTypeProviderNameA@16 +SCardSetCardTypeProviderNameW@16 +SCardState@20 +SCardStatusA@28 +SCardStatusW@28 +SCardTransmit@28 +SCardWriteCacheA@24 +SCardWriteCacheW@24 +g_rgSCardRawPci DATA +g_rgSCardT0Pci DATA +g_rgSCardT1Pci DATA diff --git a/lib/libc/mingw/lib32/winspool.def b/lib/libc/mingw/lib32/winspool.def new file mode 100644 index 000000000..ddfd9aefd --- /dev/null +++ b/lib/libc/mingw/lib32/winspool.def @@ -0,0 +1,207 @@ +; +; Definition file of WINSPOOL.DRV +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "WINSPOOL.DRV" +EXPORTS +ADVANCEDSETUPDIALOG@16 +AdvancedSetupDialog@16 +ConvertAnsiDevModeToUnicodeDevmode@16 +ConvertUnicodeDevModeToAnsiDevmode@16 +DEVICEMODE@16 +DeviceMode@16 +DocumentEvent@28 +PerfClose@0 +PerfCollect@16 +PerfOpen@4 +QueryColorProfile@24 +QueryRemoteFonts@12 +QuerySpoolMode@12 +SpoolerDevQueryPrintW@20 +StartDocDlgW@8 +AbortPrinter@4 +AddFormA@12 +AddFormW@12 +AddJobA@20 +AddJobW@20 +AddMonitorA@12 +AddMonitorW@12 +AddPortA@12 +AddPortExA@16 +AddPortExW@16 +AddPortW@12 +AddPrintProcessorA@16 +AddPrintProcessorW@16 +AddPrintProvidorA@12 +AddPrintProvidorW@12 +AddPrinterA@12 +AddPrinterConnection2A@16 +AddPrinterConnection2W@16 +AddPrinterConnectionA@4 +AddPrinterConnectionW@4 +AddPrinterDriverA@12 +AddPrinterDriverExA@16 +AddPrinterDriverExW@16 +AddPrinterDriverW@12 +AddPrinterW@12 +AdvancedDocumentPropertiesA@20 +AdvancedDocumentPropertiesW@20 +ClosePrinter@4 +CloseSpoolFileHandle@8 +CommitSpoolData@12 +ConfigurePortA@12 +ConfigurePortW@12 +ConnectToPrinterDlg@8 +CorePrinterDriverInstalledA@44 +CorePrinterDriverInstalledW@44 +CreatePrintAsyncNotifyChannel@24 +CreatePrinterIC@8 +DEVICECAPABILITIES@20 +DeleteFormA@8 +DeleteFormW@8 +DeleteJobNamedProperty@12 +DeleteMonitorA@12 +DeleteMonitorW@12 +DeletePortA@12 +DeletePortW@12 +DeletePrintProcessorA@12 +DeletePrintProcessorW@12 +DeletePrintProvidorA@12 +DeletePrintProvidorW@12 +DeletePrinter@4 +DeletePrinterConnectionA@4 +DeletePrinterConnectionW@4 +DeletePrinterDataA@8 +DeletePrinterDataExA@12 +DeletePrinterDataExW@12 +DeletePrinterDataW@8 +DeletePrinterDriverA@12 +DeletePrinterDriverExA@20 +DeletePrinterDriverExW@20 +DeletePrinterDriverPackageA@12 +DeletePrinterDriverPackageW@12 +DeletePrinterDriverW@12 +DeletePrinterIC@4 +DeletePrinterKeyA@8 +DeletePrinterKeyW@8 +DevQueryPrint@12 +DevQueryPrintEx@4 +DeviceCapabilities@20 +DeviceCapabilitiesA@20 +DeviceCapabilitiesW@20 +DevicePropertySheets@8 +DocumentPropertiesA@24 +DocumentPropertiesW@24 +DocumentPropertySheets@8 +EXTDEVICEMODE@32 +EndDocPrinter@4 +EndPagePrinter@4 +EnumFormsA@24 +EnumFormsW@24 +EnumJobNamedProperties@16 +EnumJobsA@32 +EnumJobsW@32 +EnumMonitorsA@24 +EnumMonitorsW@24 +EnumPortsA@24 +GetDefaultPrinterA@8 +SetDefaultPrinterA@4 +GetDefaultPrinterW@8 +SetDefaultPrinterW@4 +EnumPortsW@24 +EnumPrintProcessorDatatypesA@28 +EnumPrintProcessorDatatypesW@28 +EnumPrintProcessorsA@28 +EnumPrintProcessorsW@28 +EnumPrinterDataA@36 +EnumPrinterDataExA@24 +EnumPrinterDataExW@24 +EnumPrinterDataW@36 +EnumPrinterDriversA@28 +EnumPrinterDriversW@28 +EnumPrinterKeyA@20 +EnumPrinterKeyW@20 +EnumPrintersA@28 +EnumPrintersW@28 +ExtDeviceMode@32 +FindClosePrinterChangeNotification@4 +FindFirstPrinterChangeNotification@16 +FindNextPrinterChangeNotification@16 +FlushPrinter@20 +FreePrintNamedPropertyArray@8 +FreePrintPropertyValue@4 +FreePrinterNotifyInfo@4 +GetCorePrinterDriversA@20 +GetCorePrinterDriversW@20 +GetFormA@24 +GetFormW@24 +GetJobA@24 +GetJobNamedPropertyValue@16 +GetJobW@24 +GetPrintExecutionData@4 +GetPrintOutputInfo@16 +GetPrintProcessorDirectoryA@24 +GetPrintProcessorDirectoryW@24 +GetPrinterA@20 +GetPrinterDataA@24 +GetPrinterDataExA@28 +GetPrinterDataExW@28 +GetPrinterDataW@24 +GetPrinterDriver2A@28 +GetPrinterDriver2W@28 +GetPrinterDriverA@24 +GetPrinterDriverDirectoryA@24 +GetPrinterDriverDirectoryW@24 +GetPrinterDriverPackagePathA@28 +GetPrinterDriverPackagePathW@28 +GetPrinterDriverW@24 +GetPrinterW@20 +GetSpoolFileHandle@4 +InstallPrinterDriverFromPackageA@20 +InstallPrinterDriverFromPackageW@20 +IsValidDevmodeA@8 +IsValidDevmodeW@8 +OpenPrinter2A@16 +OpenPrinter2W@16 +OpenPrinterA@12 +OpenPrinterW@12 +PlayGdiScriptOnPrinterIC@24 +PrinterMessageBoxA@24 +PrinterMessageBoxW@24 +PrinterProperties@8 +ReadPrinter@16 +RegisterForPrintAsyncNotifications@24 +ReportJobProcessingProgress@16 +ResetPrinterA@8 +ResetPrinterW@8 +ScheduleJob@8 +SeekPrinter@24 +SetAllocFailCount@20 +SetFormA@16 +SetFormW@16 +SetJobA@20 +SetJobNamedProperty@12 +SetJobW@20 +SetPortA@16 +SetPortW@16 +SetPrinterA@16 +SetPrinterDataA@20 +SetPrinterDataExA@24 +SetPrinterDataExW@24 +SetPrinterDataW@20 +SetPrinterW@16 +SpoolerInit@0 +SplDriverUnloadComplete@4 +SpoolerPrinterEvent@20 +StartDocDlgA@8 +StartDocDlgW@8 +StartDocPrinterA@12 +StartDocPrinterW@12 +StartPagePrinter@4 +UnRegisterForPrintAsyncNotifications@4 +UploadPrinterDriverPackageA@28 +UploadPrinterDriverPackageW@28 +WaitForPrinterChange@8 +WritePrinter@16 +XcvDataW@32 diff --git a/lib/libc/mingw/lib32/wintrust.def b/lib/libc/mingw/lib32/wintrust.def new file mode 100644 index 000000000..fbff4e75d --- /dev/null +++ b/lib/libc/mingw/lib32/wintrust.def @@ -0,0 +1,137 @@ +; +; Definition file of WINTRUST.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "WINTRUST.dll" +EXPORTS +CryptCATVerifyMember@12 +CryptSIPGetInfo@4 +CryptSIPGetRegWorkingFlags@4 +GenericChainCertificateTrust@4 +GenericChainFinalProv@4 +HTTPSCertificateTrust@4 +SoftpubDefCertInit@4 +SoftpubFreeDefUsageCallData@8 +SoftpubLoadDefUsageCallData@8 +WTHelperCertFindIssuerCertificate@28 +AddPersonalTrustDBPages@12 +CatalogCompactHashDatabase@16 +CryptCATAdminAcquireContext@12 +CryptCATAdminAddCatalog@16 +CryptCATAdminCalcHashFromFileHandle@16 +CryptCATAdminEnumCatalogFromHash@20 +CryptCATAdminPauseServiceForBackup@8 +CryptCATAdminReleaseCatalogContext@12 +CryptCATAdminReleaseContext@8 +CryptCATAdminRemoveCatalog@12 +CryptCATAdminResolveCatalogPath@16 +CryptCATAllocSortedMemberInfo@8 +CryptCATCDFClose@4 +CryptCATCDFEnumAttributes@16 +CryptCATCDFEnumAttributesWithCDFTag@20 +CryptCATCDFEnumCatAttributes@12 +CryptCATCDFEnumMembers@12 +CryptCATCDFEnumMembersByCDFTag@16 +CryptCATCDFEnumMembersByCDFTagEx@24 +CryptCATCDFOpen@8 +CryptCATCatalogInfoFromContext@12 +CryptCATClose@4 +CryptCATEnumerateAttr@12 +CryptCATEnumerateCatAttr@8 +CryptCATEnumerateMember@8 +CryptCATFreeSortedMemberInfo@8 +CryptCATGetAttrInfo@12 +CryptCATGetCatAttrInfo@8 +CryptCATGetMemberInfo@8 +CryptCATHandleFromStore@4 +CryptCATOpen@20 +CryptCATPersistStore@4 +CryptCATPutAttrInfo@24 +CryptCATPutCatAttrInfo@20 +CryptCATPutMemberInfo@28 +CryptCATStoreFromHandle@4 +CryptSIPCreateIndirectData@12 +CryptSIPGetSignedDataMsg@20 +CryptSIPPutSignedDataMsg@20 +CryptSIPRemoveSignedDataMsg@8 +CryptSIPVerifyIndirectData@8 +DllRegisterServer +DllUnregisterServer +DriverCleanupPolicy@4 +DriverFinalPolicy@4 +DriverInitializePolicy@4 +FindCertsByIssuer@28 +HTTPSFinalProv@4 +IsCatalogFile@8 +MsCatConstructHashTag@12 +MsCatFreeHashTag@4 +OfficeCleanupPolicy@4 +OfficeInitializePolicy@4 +OpenPersonalTrustDBDialog@4 +OpenPersonalTrustDBDialogEx@12 +SoftpubAuthenticode@4 +SoftpubCheckCert@16 +SoftpubCleanup@4 +SoftpubDllRegisterServer +SoftpubDllUnregisterServer +SoftpubDumpStructure@4 +SoftpubInitialize@4 +SoftpubLoadMessage@4 +SoftpubLoadSignature@4 +TrustDecode@36 +TrustFindIssuerCertificate@32 +TrustFreeDecode@8 +TrustIsCertificateSelfSigned@12 +TrustOpenStores@16 +WTHelperCertCheckValidSignature@4 +WTHelperCertIsSelfSigned@8 +WTHelperCheckCertUsage@8 +WTHelperGetAgencyInfo@12 +WTHelperGetFileHandle@4 +WTHelperGetFileHash@24 +WTHelperGetFileName@4 +WTHelperGetKnownUsages@8 +WTHelperGetProvCertFromChain@8 +WTHelperGetProvPrivateDataFromChain@8 +WTHelperGetProvSignerFromChain@16 +WTHelperIsInRootStore@8 +WTHelperOpenKnownStores@4 +WTHelperProvDataFromStateData@4 +WVTAsn1CatMemberInfoDecode@28 +WVTAsn1CatMemberInfoEncode@20 +WVTAsn1CatNameValueDecode@28 +WVTAsn1CatNameValueEncode@20 +WVTAsn1SpcFinancialCriteriaInfoDecode@28 +WVTAsn1SpcFinancialCriteriaInfoEncode@20 +WVTAsn1SpcIndirectDataContentDecode@28 +WVTAsn1SpcIndirectDataContentEncode@20 +WVTAsn1SpcLinkDecode@28 +WVTAsn1SpcLinkEncode@20 +WVTAsn1SpcMinimalCriteriaInfoDecode@28 +WVTAsn1SpcMinimalCriteriaInfoEncode@20 +WVTAsn1SpcPeImageDataDecode@28 +WVTAsn1SpcPeImageDataEncode@20 +WVTAsn1SpcSigInfoDecode@28 +WVTAsn1SpcSigInfoEncode@20 +WVTAsn1SpcSpAgencyInfoDecode@28 +WVTAsn1SpcSpAgencyInfoEncode@20 +WVTAsn1SpcSpOpusInfoDecode@28 +WVTAsn1SpcSpOpusInfoEncode@20 +WVTAsn1SpcStatementTypeDecode@28 +WVTAsn1SpcStatementTypeEncode@20 +WinVerifyTrust@12 +WinVerifyTrustEx@12 +WintrustAddActionID@12 +WintrustAddDefaultForUsage@8 +WintrustCertificateTrust@4 +WintrustGetDefaultForUsage@12 +WintrustGetRegPolicyFlags@4 +WintrustLoadFunctionPointers@8 +WintrustRemoveActionID@4 +WintrustSetDefaultIncludePEPageHashes@4 +WintrustSetRegPolicyFlags@4 +mscat32DllRegisterServer +mscat32DllUnregisterServer +mssip32DllRegisterServer +mssip32DllUnregisterServer diff --git a/lib/libc/mingw/lib32/ws2_32.def b/lib/libc/mingw/lib32/ws2_32.def new file mode 100644 index 000000000..19f3f69db --- /dev/null +++ b/lib/libc/mingw/lib32/ws2_32.def @@ -0,0 +1,174 @@ +; +; Definition file of WS2_32.dll +; Automatic generated by gendef +; written by Kai Tietz 2008 +; +LIBRARY "WS2_32.dll" +EXPORTS +accept@12 +bind@12 +closesocket@4 +connect@12 +getpeername@12 +getsockname@12 +getsockopt@20 +htonl@4 +htons@4 +ioctlsocket@12 +inet_addr@4 +inet_ntoa@4 +listen@8 +ntohl@4 +ntohs@4 +recv@16 +recvfrom@24 +select@20 +send@16 +sendto@24 +setsockopt@20 +shutdown@8 +socket@12 +WSApSetPostRoutine@4 +FreeAddrInfoEx@4 +FreeAddrInfoExW@4 +FreeAddrInfoW@4 +GetAddrInfoExA@40 +GetAddrInfoExW@40 +GetAddrInfoW@16 +GetNameInfoW@28 +InetNtopW@16 +InetPtonW@12 +SetAddrInfoExA@48 +SetAddrInfoExW@48 +WPUCompleteOverlappedRequest@20 +WSAAccept@20 +WSAAddressToStringA@20 +WSAAddressToStringW@20 +WSAAdvertiseProvider@8 +WSACloseEvent@4 +WSAConnect@28 +WSAConnectByList@32 +WSAConnectByNameA@36 +WSAConnectByNameW@36 +WSACreateEvent@0 +WSADuplicateSocketA@12 +WSADuplicateSocketW@12 +WSAEnumNameSpaceProvidersA@8 +WSAEnumNameSpaceProvidersExA@8 +gethostbyaddr@12 +gethostbyname@4 +getprotobyname@4 +getprotobynumber@4 +getservbyname@8 +getservbyport@8 +gethostname@8 +WSAEnumNameSpaceProvidersExW@8 +WSAEnumNameSpaceProvidersW@8 +WSAEnumNetworkEvents@12 +WSAEnumProtocolsA@12 +WSAEnumProtocolsW@12 +WSAEventSelect@12 +WSAGetOverlappedResult@20 +WSAGetQOSByName@12 +WSAGetServiceClassInfoA@16 +WSAGetServiceClassInfoW@16 +WSAGetServiceClassNameByClassIdA@12 +WSAGetServiceClassNameByClassIdW@12 +WSAHtonl@12 +WSAHtons@12 +WSAInstallServiceClassA@4 +WSAInstallServiceClassW@4 +WSAIoctl@36 +WSAJoinLeaf@32 +WSALookupServiceBeginA@12 +WSALookupServiceBeginW@12 +WSALookupServiceEnd@4 +WSALookupServiceNextA@16 +WSALookupServiceNextW@16 +WSANSPIoctl@32 +WSANtohl@12 +WSANtohs@12 +WSAPoll@12 +WSAProviderCompleteAsyncCall@8 +WSAProviderConfigChange@12 +WSARecv@28 +WSARecvDisconnect@8 +WSARecvFrom@36 +WSARemoveServiceClass@4 +WSAResetEvent@4 +WSASend@28 +WSASendDisconnect@8 +WSASendMsg@24 +WSASendTo@36 +WSASetEvent@4 +WSASetServiceA@12 +WSASetServiceW@12 +WSASocketA@24 +WSASocketW@24 +WSAAsyncSelect@16 +WSAAsyncGetHostByAddr@28 +WSAAsyncGetHostByName@20 +WSAAsyncGetProtoByNumber@20 +WSAAsyncGetProtoByName@20 +WSAAsyncGetServByPort@24 +WSAAsyncGetServByName@24 +WSACancelAsyncRequest@4 +WSASetBlockingHook@4 +WSAUnhookBlockingHook@0 +WSAGetLastError@0 +WSASetLastError@4 +WSACancelBlockingCall@0 +WSAIsBlocking@0 +WSAStartup@8 +WSACleanup@0 +WSAStringToAddressA@20 +WSAStringToAddressW@20 +WSAUnadvertiseProvider@4 +WSAWaitForMultipleEvents@20 +WSCDeinstallProvider@8 +WSCEnableNSProvider@8 +WSCEnumProtocols@16 +WSCGetApplicationCategory@24 +WSCGetProviderInfo@24 +WSCGetProviderPath@16 +WSCInstallNameSpace@20 +WSCInstallNameSpaceEx@24 +WSCInstallProvider@20 +WSCInstallProviderAndChains@32 +WSCSetApplicationCategory@28 +WSCSetProviderInfo@24 +WSCUnInstallNameSpace@4 +WSCUpdateProvider@20 +WSCWriteNameSpaceOrder@8 +WSCWriteProviderOrder@8 +WahCloseApcHelper@4 +WahCloseHandleHelper@4 +WahCloseNotificationHandleHelper@4 +WahCloseSocketHandle@8 +WahCloseThread@8 +WahCompleteRequest@20 +WahCreateHandleContextTable@4 +WahCreateNotificationHandle@8 +WahCreateSocketHandle@8 +WahDestroyHandleContextTable@4 +WahDisableNonIFSHandleSupport@0 +WahEnableNonIFSHandleSupport@0 +WahEnumerateHandleContexts@12 +WahInsertHandleContext@8 +__WSAFDIsSet@8 +WahNotifyAllProcesses@4 +WahOpenApcHelper@4 +WahOpenCurrentThread@8 +WahOpenHandleHelper@4 +WahOpenNotificationHandleHelper@4 +WahQueueUserApc@16 +WahReferenceContextByHandle@8 +WahRemoveHandleContext@8 +WahWaitForNotification@16 +WahWriteLSPEvent@8 +freeaddrinfo@4 +getaddrinfo@16 +getnameinfo@28 +inet_ntop@16 +inet_pton@12 +WEP@0 diff --git a/lib/libc/mingw/lib64/mswsock.def b/lib/libc/mingw/lib64/mswsock.def new file mode 100644 index 000000000..7abab873b --- /dev/null +++ b/lib/libc/mingw/lib64/mswsock.def @@ -0,0 +1,40 @@ +; +; Exports of file MSWSOCK.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY MSWSOCK.dll +EXPORTS +ServiceMain +SvchostPushServiceGlobals +AcceptEx +EnumProtocolsA +EnumProtocolsW +GetAcceptExSockaddrs +GetAddressByNameA +GetAddressByNameW +GetNameByTypeA +GetNameByTypeW +GetServiceA +GetServiceW +GetTypeByNameA +GetTypeByNameW +MigrateWinsockConfiguration +NPLoadNameSpaces +NSPStartup +SetServiceA +SetServiceW +StartWsdpService +StopWsdpService +TransmitFile +WSARecvEx +WSPStartup +dn_expand +getnetbyname +inet_network +rcmd +rexec +rresvport +s_perror +sethostname diff --git a/lib/libc/mingw/lib64/opengl32.def b/lib/libc/mingw/lib64/opengl32.def new file mode 100644 index 000000000..2111eb047 --- /dev/null +++ b/lib/libc/mingw/lib64/opengl32.def @@ -0,0 +1,376 @@ +; +; Exports of file OPENGL32.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY OPENGL32.dll +EXPORTS +GlmfBeginGlsBlock +GlmfCloseMetaFile +GlmfEndGlsBlock +GlmfEndPlayback +GlmfInitPlayback +GlmfPlayGlsRecord +glAccum +glAlphaFunc +glAreTexturesResident +glArrayElement +glBegin +glBindTexture +glBitmap +glBlendFunc +glCallList +glCallLists +glClear +glClearAccum +glClearColor +glClearDepth +glClearIndex +glClearStencil +glClipPlane +glColor3b +glColor3bv +glColor3d +glColor3dv +glColor3f +glColor3fv +glColor3i +glColor3iv +glColor3s +glColor3sv +glColor3ub +glColor3ubv +glColor3ui +glColor3uiv +glColor3us +glColor3usv +glColor4b +glColor4bv +glColor4d +glColor4dv +glColor4f +glColor4fv +glColor4i +glColor4iv +glColor4s +glColor4sv +glColor4ub +glColor4ubv +glColor4ui +glColor4uiv +glColor4us +glColor4usv +glColorMask +glColorMaterial +glColorPointer +glCopyPixels +glCopyTexImage1D +glCopyTexImage2D +glCopyTexSubImage1D +glCopyTexSubImage2D +glCullFace +glDebugEntry +glDeleteLists +glDeleteTextures +glDepthFunc +glDepthMask +glDepthRange +glDisable +glDisableClientState +glDrawArrays +glDrawBuffer +glDrawElements +glDrawPixels +glEdgeFlag +glEdgeFlagPointer +glEdgeFlagv +glEnable +glEnableClientState +glEnd +glEndList +glEvalCoord1d +glEvalCoord1dv +glEvalCoord1f +glEvalCoord1fv +glEvalCoord2d +glEvalCoord2dv +glEvalCoord2f +glEvalCoord2fv +glEvalMesh1 +glEvalMesh2 +glEvalPoint1 +glEvalPoint2 +glFeedbackBuffer +glFinish +glFlush +glFogf +glFogfv +glFogi +glFogiv +glFrontFace +glFrustum +glGenLists +glGenTextures +glGetBooleanv +glGetClipPlane +glGetDoublev +glGetError +glGetFloatv +glGetIntegerv +glGetLightfv +glGetLightiv +glGetMapdv +glGetMapfv +glGetMapiv +glGetMaterialfv +glGetMaterialiv +glGetPixelMapfv +glGetPixelMapuiv +glGetPixelMapusv +glGetPointerv +glGetPolygonStipple +glGetString +glGetTexEnvfv +glGetTexEnviv +glGetTexGendv +glGetTexGenfv +glGetTexGeniv +glGetTexImage +glGetTexLevelParameterfv +glGetTexLevelParameteriv +glGetTexParameterfv +glGetTexParameteriv +glHint +glIndexMask +glIndexPointer +glIndexd +glIndexdv +glIndexf +glIndexfv +glIndexi +glIndexiv +glIndexs +glIndexsv +glIndexub +glIndexubv +glInitNames +glInterleavedArrays +glIsEnabled +glIsList +glIsTexture +glLightModelf +glLightModelfv +glLightModeli +glLightModeliv +glLightf +glLightfv +glLighti +glLightiv +glLineStipple +glLineWidth +glListBase +glLoadIdentity +glLoadMatrixd +glLoadMatrixf +glLoadName +glLogicOp +glMap1d +glMap1f +glMap2d +glMap2f +glMapGrid1d +glMapGrid1f +glMapGrid2d +glMapGrid2f +glMaterialf +glMaterialfv +glMateriali +glMaterialiv +glMatrixMode +glMultMatrixd +glMultMatrixf +glNewList +glNormal3b +glNormal3bv +glNormal3d +glNormal3dv +glNormal3f +glNormal3fv +glNormal3i +glNormal3iv +glNormal3s +glNormal3sv +glNormalPointer +glOrtho +glPassThrough +glPixelMapfv +glPixelMapuiv +glPixelMapusv +glPixelStoref +glPixelStorei +glPixelTransferf +glPixelTransferi +glPixelZoom +glPointSize +glPolygonMode +glPolygonOffset +glPolygonStipple +glPopAttrib +glPopClientAttrib +glPopMatrix +glPopName +glPrioritizeTextures +glPushAttrib +glPushClientAttrib +glPushMatrix +glPushName +glRasterPos2d +glRasterPos2dv +glRasterPos2f +glRasterPos2fv +glRasterPos2i +glRasterPos2iv +glRasterPos2s +glRasterPos2sv +glRasterPos3d +glRasterPos3dv +glRasterPos3f +glRasterPos3fv +glRasterPos3i +glRasterPos3iv +glRasterPos3s +glRasterPos3sv +glRasterPos4d +glRasterPos4dv +glRasterPos4f +glRasterPos4fv +glRasterPos4i +glRasterPos4iv +glRasterPos4s +glRasterPos4sv +glReadBuffer +glReadPixels +glRectd +glRectdv +glRectf +glRectfv +glRecti +glRectiv +glRects +glRectsv +glRenderMode +glRotated +glRotatef +glScaled +glScalef +glScissor +glSelectBuffer +glShadeModel +glStencilFunc +glStencilMask +glStencilOp +glTexCoord1d +glTexCoord1dv +glTexCoord1f +glTexCoord1fv +glTexCoord1i +glTexCoord1iv +glTexCoord1s +glTexCoord1sv +glTexCoord2d +glTexCoord2dv +glTexCoord2f +glTexCoord2fv +glTexCoord2i +glTexCoord2iv +glTexCoord2s +glTexCoord2sv +glTexCoord3d +glTexCoord3dv +glTexCoord3f +glTexCoord3fv +glTexCoord3i +glTexCoord3iv +glTexCoord3s +glTexCoord3sv +glTexCoord4d +glTexCoord4dv +glTexCoord4f +glTexCoord4fv +glTexCoord4i +glTexCoord4iv +glTexCoord4s +glTexCoord4sv +glTexCoordPointer +glTexEnvf +glTexEnvfv +glTexEnvi +glTexEnviv +glTexGend +glTexGendv +glTexGenf +glTexGenfv +glTexGeni +glTexGeniv +glTexImage1D +glTexImage2D +glTexParameterf +glTexParameterfv +glTexParameteri +glTexParameteriv +glTexSubImage1D +glTexSubImage2D +glTranslated +glTranslatef +glVertex2d +glVertex2dv +glVertex2f +glVertex2fv +glVertex2i +glVertex2iv +glVertex2s +glVertex2sv +glVertex3d +glVertex3dv +glVertex3f +glVertex3fv +glVertex3i +glVertex3iv +glVertex3s +glVertex3sv +glVertex4d +glVertex4dv +glVertex4f +glVertex4fv +glVertex4i +glVertex4iv +glVertex4s +glVertex4sv +glVertexPointer +glViewport +wglChoosePixelFormat +wglCopyContext +wglCreateContext +wglCreateLayerContext +wglDeleteContext +wglDescribeLayerPlane +wglDescribePixelFormat +wglGetCurrentContext +wglGetCurrentDC +wglGetDefaultProcAddress +wglGetLayerPaletteEntries +wglGetPixelFormat +wglGetProcAddress +wglMakeCurrent +wglRealizeLayerPalette +wglSetLayerPaletteEntries +wglSetPixelFormat +wglShareLists +wglSwapBuffers +wglSwapLayerBuffers +wglSwapMultipleBuffers +wglUseFontBitmapsA +wglUseFontBitmapsW +wglUseFontOutlinesA +wglUseFontOutlinesW diff --git a/lib/libc/mingw/lib64/urlmon.def b/lib/libc/mingw/lib64/urlmon.def new file mode 100644 index 000000000..2a10219a3 --- /dev/null +++ b/lib/libc/mingw/lib64/urlmon.def @@ -0,0 +1,96 @@ +; +; Exports of file urlmon.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY urlmon.dll +EXPORTS +AsyncGetClassBits +AsyncInstallDistributionUnit +BindAsyncMoniker +CDLGetLongPathNameA +CDLGetLongPathNameW +CoGetClassObjectFromURL +CoInstall +CoInternetCombineUrl +CoInternetCompareUrl +CoInternetCreateSecurityManager +CoInternetCreateZoneManager +CoInternetFeatureSettingsChanged +CoInternetGetProtocolFlags +CoInternetGetSecurityUrl +CoInternetGetSession +CoInternetIsFeatureEnabled +CoInternetIsFeatureEnabledForUrl +CoInternetIsFeatureZoneElevationEnabled +CoInternetParseUrl +CoInternetQueryInfo +CoInternetSetFeatureEnabled +CompareSecurityIds +CompatFlagsFromClsid +CopyBindInfo +CopyStgMedium +CreateAsyncBindCtx +CreateAsyncBindCtxEx +CreateFormatEnumerator +CreateURLMoniker +CreateURLMonikerEx +DllCanUnloadNow +DllGetClassObject +DllInstall +DllRegisterServer +DllRegisterServerEx +DllUnregisterServer +Extract +FaultInIEFeature +FindMediaType +FindMediaTypeClass +FindMimeFromData +GetAddSitesFileUrl +GetClassFileOrMime +GetClassURL +GetComponentIDFromCLSSPEC +GetMarkOfTheWeb +GetSoftwareUpdateInfo +HlinkGoBack +HlinkGoForward +HlinkNavigateMoniker +HlinkNavigateString +HlinkSimpleNavigateToMoniker +HlinkSimpleNavigateToString +InstallFlash +IsAsyncMoniker +IsJITInProgress +IsLoggingEnabledA +IsLoggingEnabledW +IsValidURL +MkParseDisplayNameEx +ObtainUserAgentString +PrivateCoInstall +RegisterBindStatusCallback +RegisterFormatEnumerator +RegisterMediaTypeClass +RegisterMediaTypes +ReleaseBindInfo +RevokeBindStatusCallback +RevokeFormatEnumerator +SetSoftwareUpdateAdvertisementState +ShowTrustAlertDialog +URLDownloadA +URLDownloadToCacheFileA +URLDownloadToCacheFileW +URLDownloadToFileA +URLDownloadToFileW +URLDownloadW +URLOpenBlockingStreamA +URLOpenBlockingStreamW +URLOpenPullStreamA +URLOpenPullStreamW +URLOpenStreamA +URLOpenStreamW +UrlMkBuildVersion +UrlMkGetSessionOption +UrlMkSetSessionOption +WriteHitLogging +ZonesReInit diff --git a/lib/libc/mingw/lib64/wintrust.def b/lib/libc/mingw/lib64/wintrust.def new file mode 100644 index 000000000..c2d68ffee --- /dev/null +++ b/lib/libc/mingw/lib64/wintrust.def @@ -0,0 +1,136 @@ +; +; Exports of file WINTRUST.dll +; +; Autogenerated by gen_exportdef +; Written by Kai Tietz, 2007 +; +LIBRARY WINTRUST.dll +EXPORTS +CryptCATVerifyMember +CryptSIPGetInfo +CryptSIPGetRegWorkingFlags +GenericChainCertificateTrust +GenericChainFinalProv +HTTPSCertificateTrust +SoftpubDefCertInit +SoftpubFreeDefUsageCallData +SoftpubLoadDefUsageCallData +WTHelperCertFindIssuerCertificate +AddPersonalTrustDBPages +CatalogCompactHashDatabase +CryptCATAdminAcquireContext +CryptCATAdminAddCatalog +CryptCATAdminCalcHashFromFileHandle +CryptCATAdminEnumCatalogFromHash +CryptCATAdminPauseServiceForBackup +CryptCATAdminReleaseCatalogContext +CryptCATAdminReleaseContext +CryptCATAdminRemoveCatalog +CryptCATAdminResolveCatalogPath +CryptCATCDFClose +CryptCATCDFEnumAttributes +CryptCATCDFEnumAttributesWithCDFTag +CryptCATCDFEnumCatAttributes +CryptCATCDFEnumMembers +CryptCATCDFEnumMembersByCDFTag +CryptCATCDFEnumMembersByCDFTagEx +CryptCATCDFOpen +CryptCATCatalogInfoFromContext +CryptCATClose +CryptCATEnumerateAttr +CryptCATEnumerateCatAttr +CryptCATEnumerateMember +CryptCATGetAttrInfo +CryptCATGetCatAttrInfo +CryptCATGetMemberInfo +CryptCATHandleFromStore +CryptCATOpen +CryptCATPersistStore +CryptCATPutAttrInfo +CryptCATPutCatAttrInfo +CryptCATPutMemberInfo +CryptCATStoreFromHandle +CryptSIPCreateIndirectData +CryptSIPGetSignedDataMsg +CryptSIPPutSignedDataMsg +CryptSIPRemoveSignedDataMsg +CryptSIPVerifyIndirectData +DllRegisterServer +DllUnregisterServer +DriverCleanupPolicy +DriverFinalPolicy +DriverInitializePolicy +FindCertsByIssuer +HTTPSFinalProv +I_CryptCatAdminMigrateToNewCatDB +IsCatalogFile +MsCatConstructHashTag +MsCatFreeHashTag +OfficeCleanupPolicy +OfficeInitializePolicy +OpenPersonalTrustDBDialog +OpenPersonalTrustDBDialogEx +SoftpubAuthenticode +SoftpubCheckCert +SoftpubCleanup +SoftpubDllRegisterServer +SoftpubDllUnregisterServer +SoftpubDumpStructure +SoftpubInitialize +SoftpubLoadMessage +SoftpubLoadSignature +TrustDecode +TrustFindIssuerCertificate +TrustFreeDecode +TrustIsCertificateSelfSigned +TrustOpenStores +WTHelperCertCheckValidSignature +WTHelperCertIsSelfSigned +WTHelperCheckCertUsage +WTHelperGetAgencyInfo +WTHelperGetFileHandle +WTHelperGetFileHash +WTHelperGetFileName +WTHelperGetKnownUsages +WTHelperGetProvCertFromChain +WTHelperGetProvPrivateDataFromChain +WTHelperGetProvSignerFromChain +WTHelperIsInRootStore +WTHelperOpenKnownStores +WTHelperProvDataFromStateData +WVTAsn1CatMemberInfoDecode +WVTAsn1CatMemberInfoEncode +WVTAsn1CatNameValueDecode +WVTAsn1CatNameValueEncode +WVTAsn1SpcFinancialCriteriaInfoDecode +WVTAsn1SpcFinancialCriteriaInfoEncode +WVTAsn1SpcIndirectDataContentDecode +WVTAsn1SpcIndirectDataContentEncode +WVTAsn1SpcLinkDecode +WVTAsn1SpcLinkEncode +WVTAsn1SpcMinimalCriteriaInfoDecode +WVTAsn1SpcMinimalCriteriaInfoEncode +WVTAsn1SpcPeImageDataDecode +WVTAsn1SpcPeImageDataEncode +WVTAsn1SpcSigInfoDecode +WVTAsn1SpcSigInfoEncode +WVTAsn1SpcSpAgencyInfoDecode +WVTAsn1SpcSpAgencyInfoEncode +WVTAsn1SpcSpOpusInfoDecode +WVTAsn1SpcSpOpusInfoEncode +WVTAsn1SpcStatementTypeDecode +WVTAsn1SpcStatementTypeEncode +WinVerifyTrust +WinVerifyTrustEx +WintrustAddActionID +WintrustAddDefaultForUsage +WintrustCertificateTrust +WintrustGetDefaultForUsage +WintrustGetRegPolicyFlags +WintrustLoadFunctionPointers +WintrustRemoveActionID +WintrustSetRegPolicyFlags +mscat32DllRegisterServer +mscat32DllUnregisterServer +mssip32DllRegisterServer +mssip32DllUnregisterServer diff --git a/lib/libc/mingw/libarm32/mswsock.def b/lib/libc/mingw/libarm32/mswsock.def new file mode 100644 index 000000000..889f2f1f7 --- /dev/null +++ b/lib/libc/mingw/libarm32/mswsock.def @@ -0,0 +1,70 @@ +; +; Definition file of MSWSOCK.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "MSWSOCK.dll" +EXPORTS +AcceptEx +EnumProtocolsA +EnumProtocolsW +GetAcceptExSockaddrs +GetAddressByNameA +GetAddressByNameW +GetNameByTypeA +GetNameByTypeW +GetServiceA +GetServiceW +GetSocketErrorMessageW +GetTypeByNameA +GetTypeByNameW +MigrateWinsockConfiguration +MigrateWinsockConfigurationEx +NPLoadNameSpaces +NSPStartup +SetServiceA +SetServiceW +StartWsdpService +StopWsdpService +Tcpip4_WSHAddressToString +Tcpip4_WSHEnumProtocols +Tcpip4_WSHGetBroadcastSockaddr +Tcpip4_WSHGetProviderGuid +Tcpip4_WSHGetSockaddrType +Tcpip4_WSHGetSocketInformation +Tcpip4_WSHGetWSAProtocolInfo +Tcpip4_WSHGetWildcardSockaddr +Tcpip4_WSHGetWinsockMapping +Tcpip4_WSHIoctl +Tcpip4_WSHJoinLeaf +Tcpip4_WSHNotify +Tcpip4_WSHOpenSocket +Tcpip4_WSHOpenSocket2 +Tcpip4_WSHSetSocketInformation +Tcpip4_WSHStringToAddress +Tcpip6_WSHAddressToString +Tcpip6_WSHEnumProtocols +Tcpip6_WSHGetProviderGuid +Tcpip6_WSHGetSockaddrType +Tcpip6_WSHGetSocketInformation +Tcpip6_WSHGetWSAProtocolInfo +Tcpip6_WSHGetWildcardSockaddr +Tcpip6_WSHGetWinsockMapping +Tcpip6_WSHIoctl +Tcpip6_WSHJoinLeaf +Tcpip6_WSHNotify +Tcpip6_WSHOpenSocket +Tcpip6_WSHOpenSocket2 +Tcpip6_WSHSetSocketInformation +Tcpip6_WSHStringToAddress +TransmitFile +WSARecvEx +WSPStartup +dn_expand +getnetbyname +inet_network +rcmd +rexec +rresvport +s_perror +sethostname diff --git a/lib/libc/mingw/libarm32/urlmon.def b/lib/libc/mingw/libarm32/urlmon.def new file mode 100644 index 000000000..2e0f14988 --- /dev/null +++ b/lib/libc/mingw/libarm32/urlmon.def @@ -0,0 +1,292 @@ +; +; Definition file of urlmon.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "urlmon.dll" +EXPORTS +ord_100 @100 +ord_101 @101 +ord_102 @102 +ord_103 @103 +ord_104 @104 +ord_105 @105 +ord_106 @106 +ord_107 @107 +ord_108 @108 +FileBearsMarkOfTheWeb +GetPortFromUrlScheme +ord_111 @111 +ord_112 @112 +ord_113 @113 +ord_114 @114 +ord_115 @115 +ord_116 @116 +ord_117 @117 +GetPropertyFromName +GetPropertyName +IsDWORDProperty +IsStringProperty +AsyncGetClassBits +AsyncInstallDistributionUnit +BindAsyncMoniker +CDLGetLongPathNameA +CDLGetLongPathNameW +CORPolicyProvider +CoGetClassObjectFromURL +CoInstall +CoInternetCanonicalizeIUri +CoInternetCombineIUri +CoInternetCombineUrl +CoInternetCombineUrlEx +CoInternetCompareUrl +CoInternetCreateSecurityManager +CoInternetCreateZoneManager +CoInternetFeatureSettingsChanged +CoInternetGetProtocolFlags +CoInternetGetSecurityUrl +CoInternetGetSecurityUrlEx +CoInternetGetSession +CoInternetIsFeatureEnabled +CoInternetIsFeatureEnabledForIUri +CoInternetIsFeatureEnabledForUrl +CoInternetIsFeatureZoneElevationEnabled +CoInternetParseIUri +CoInternetParseUrl +CoInternetQueryInfo +CoInternetSetFeatureEnabled +CompareSecurityIds +CompatFlagsFromClsid +CopyBindInfo +CopyStgMedium +CreateAsyncBindCtx +CreateAsyncBindCtxEx +CreateFormatEnumerator +CreateIUriBuilder +CreateURLMoniker +CreateURLMonikerEx +CreateURLMonikerEx2 +CreateUri +CreateUriFromMultiByteString +CreateUriPriv +CreateUriWithFragment +Extract +FaultInIEFeature +FindMediaType +FindMediaTypeClass +FindMimeFromData +GetAddSitesFileUrl +GetClassFileOrMime +GetClassURL +GetComponentIDFromCLSSPEC +GetIDNFlagsForUri +GetIUriPriv +GetIUriPriv2 +GetLabelsFromNamedHost +GetMarkOfTheWeb +GetSoftwareUpdateInfo +GetUrlmonThreadNotificationHwnd +GetZoneFromAlternateDataStreamEx +HlinkGoBack +HlinkGoForward +HlinkNavigateMoniker +HlinkNavigateString +HlinkSimpleNavigateToMoniker +HlinkSimpleNavigateToString +IEDllLoader +IEGetUserPrivateNamespaceName +IEInstallScope +IntlPercentEncodeNormalize +IsAsyncMoniker +IsIntranetAvailable +IsJITInProgress +IsLoggingEnabledA +IsLoggingEnabledW +IsValidURL +MkParseDisplayNameEx +ObtainUserAgentString +PrivateCoInstall +QueryAssociations +QueryClsidAssociation +RegisterBindStatusCallback +RegisterFormatEnumerator +RegisterMediaTypeClass +RegisterMediaTypes +RegisterWebPlatformPermanentSecurityManager +ReleaseBindInfo +RevokeBindStatusCallback +RevokeFormatEnumerator +SetAccessForIEAppContainer +SetSoftwareUpdateAdvertisementState +ShouldDisplayPunycodeForUri +ShouldShowIntranetWarningSecband +ShowTrustAlertDialog +URLDownloadA +URLDownloadToCacheFileA +URLDownloadToCacheFileW +URLDownloadToFileA +URLDownloadToFileW +URLDownloadW +URLOpenBlockingStreamA +URLOpenBlockingStreamW +URLOpenPullStreamA +URLOpenPullStreamW +URLOpenStreamA +URLOpenStreamW +UnregisterWebPlatformPermanentSecurityManager +UrlMkBuildVersion +UrlMkGetSessionOption +UrlMkSetSessionOption +UrlmonCleanupCurrentThread +WriteHitLogging +ZonesReInit +ord_304 @304 +ord_305 @305 +ord_306 @306 +ord_307 @307 +ord_308 @308 +ord_309 @309 +ord_310 @310 +ord_311 @311 +ord_312 @312 +ord_313 @313 +ord_314 @314 +ord_315 @315 +ord_316 @316 +ord_318 @318 +ord_319 @319 +ord_320 @320 +ord_321 @321 +IECompatLogCSSFix +ord_323 @323 +ord_324 @324 +ord_395 @395 +ord_400 @400 +ord_401 @401 +ord_403 @403 +ord_404 @404 +ord_406 @406 +ord_407 @407 +ord_408 @408 +ord_409 @409 +ord_410 @410 +ord_411 @411 +ord_412 @412 +ord_413 @413 +ord_414 @414 +ord_415 @415 +ord_416 @416 +ord_417 @417 +ord_421 @421 +ord_422 @422 +ord_423 @423 +ord_430 @430 +ord_431 @431 +ord_432 @432 +ord_433 @433 +ord_434 @434 +ord_435 @435 +ord_436 @436 +ord_437 @437 +ord_438 @438 +ord_439 @439 +ord_440 @440 +ord_441 @441 +ord_442 @442 +ord_443 @443 +ord_444 @444 +ord_445 @445 +ord_446 @446 +ord_447 @447 +ord_448 @448 +ord_449 @449 +ord_450 @450 +ord_451 @451 +ord_452 @452 +ord_453 @453 +ord_454 @454 +ord_455 @455 +ord_456 @456 +ord_457 @457 +ord_458 @458 +ord_460 @460 +ord_461 @461 +ord_462 @462 +ord_463 @463 +ord_470 @470 +ord_471 @471 +ord_473 @473 +ord_474 @474 +ord_475 @475 +ord_476 @476 +ord_477 @477 +ord_478 @478 +ord_479 @479 +ord_480 @480 +ord_481 @481 +ord_482 @482 +ord_483 @483 +ord_484 @484 +ord_485 @485 +ord_486 @486 +ord_487 @487 +ord_488 @488 +ord_489 @489 +ord_490 @490 +ord_491 @491 +ord_492 @492 +ord_494 @494 +ord_495 @495 +ord_496 @496 +ord_497 @497 +ord_498 @498 +ord_499 @499 +ord_500 @500 +ord_501 @501 +ord_502 @502 +ord_503 @503 +ord_504 @504 +ord_505 @505 +ord_506 @506 +ord_507 @507 +ord_508 @508 +ord_509 @509 +ord_510 @510 +ord_511 @511 +ord_512 @512 +ord_513 @513 +ord_514 @514 +ord_515 @515 +ord_516 @516 +ord_517 @517 +ord_518 @518 +ord_519 @519 +ord_520 @520 +ord_521 @521 +ord_522 @522 +ord_523 @523 +ord_524 @524 +ord_525 @525 +ord_526 @526 +ord_527 @527 +ord_528 @528 +ord_529 @529 +ord_530 @530 +ord_531 @531 +ord_532 @532 +ord_533 @533 +ord_534 @534 +ord_535 @535 +ord_536 @536 +ord_537 @537 +ord_538 @538 +ord_539 @539 +ord_540 @540 +ord_541 @541 +ord_542 @542 +ord_543 @543 +ord_544 @544 +ord_545 @545 +ord_546 @546 +ord_547 @547 +ord_548 @548 diff --git a/lib/libc/mingw/libarm32/wintrust.def b/lib/libc/mingw/libarm32/wintrust.def new file mode 100644 index 000000000..4ec8fc4e8 --- /dev/null +++ b/lib/libc/mingw/libarm32/wintrust.def @@ -0,0 +1,150 @@ +; +; Definition file of WINTRUST.dll +; Automatic generated by gendef +; written by Kai Tietz 2008-2014 +; +LIBRARY "WINTRUST.dll" +EXPORTS +CryptCATVerifyMember +CryptSIPGetInfo +CryptSIPGetRegWorkingFlags +GenericChainCertificateTrust +GenericChainFinalProv +HTTPSCertificateTrust +SoftpubDefCertInit +SoftpubFreeDefUsageCallData +SoftpubLoadDefUsageCallData +WTHelperCertFindIssuerCertificate +AddPersonalTrustDBPages +CatalogCompactHashDatabase +CryptCATAdminAcquireContext +CryptCATAdminAcquireContext2 +CryptCATAdminAddCatalog +CryptCATAdminCalcHashFromFileHandle +CryptCATAdminCalcHashFromFileHandle2 +CryptCATAdminEnumCatalogFromHash +CryptCATAdminPauseServiceForBackup +CryptCATAdminReleaseCatalogContext +CryptCATAdminReleaseContext +CryptCATAdminRemoveCatalog +CryptCATAdminResolveCatalogPath +CryptCATAllocSortedMemberInfo +CryptCATCDFClose +CryptCATCDFEnumAttributes +CryptCATCDFEnumAttributesWithCDFTag +CryptCATCDFEnumCatAttributes +CryptCATCDFEnumMembers +CryptCATCDFEnumMembersByCDFTag +CryptCATCDFEnumMembersByCDFTagEx +CryptCATCDFOpen +CryptCATCatalogInfoFromContext +CryptCATClose +CryptCATEnumerateAttr +CryptCATEnumerateCatAttr +CryptCATEnumerateMember +CryptCATFreeSortedMemberInfo +CryptCATGetAttrInfo +CryptCATGetCatAttrInfo +CryptCATGetMemberInfo +CryptCATHandleFromStore +CryptCATOpen +CryptCATPersistStore +CryptCATPutAttrInfo +CryptCATPutCatAttrInfo +CryptCATPutMemberInfo +CryptCATStoreFromHandle +CryptSIPCreateIndirectData +CryptSIPGetCaps +CryptSIPGetSealedDigest +CryptSIPGetSignedDataMsg +CryptSIPPutSignedDataMsg +CryptSIPRemoveSignedDataMsg +CryptSIPVerifyIndirectData +DriverCleanupPolicy +DriverFinalPolicy +DriverInitializePolicy +FindCertsByIssuer +HTTPSFinalProv +IsCatalogFile +MsCatConstructHashTag +MsCatFreeHashTag +OfficeCleanupPolicy +OfficeInitializePolicy +OpenPersonalTrustDBDialog +OpenPersonalTrustDBDialogEx +SoftpubAuthenticode +SoftpubCheckCert +SoftpubCleanup +SoftpubDllRegisterServer +SoftpubDllUnregisterServer +SoftpubDumpStructure +SoftpubInitialize +SoftpubLoadMessage +SoftpubLoadSignature +TrustDecode +TrustFindIssuerCertificate +TrustFreeDecode +TrustIsCertificateSelfSigned +TrustOpenStores +WTGetSignatureInfo +WTHelperCertCheckValidSignature +WTHelperCertIsSelfSigned +WTHelperCheckCertUsage +WTHelperGetAgencyInfo +WTHelperGetFileHandle +WTHelperGetFileHash +WTHelperGetFileName +WTHelperGetKnownUsages +WTHelperGetProvCertFromChain +WTHelperGetProvPrivateDataFromChain +WTHelperGetProvSignerFromChain +WTHelperIsChainedToMicrosoft +WTHelperIsChainedToMicrosoftFromStateData +WTHelperIsInRootStore +WTHelperOpenKnownStores +WTHelperProvDataFromStateData +WVTAsn1CatMemberInfo2Decode +WVTAsn1CatMemberInfo2Encode +WVTAsn1CatMemberInfoDecode +WVTAsn1CatMemberInfoEncode +WVTAsn1CatNameValueDecode +WVTAsn1CatNameValueEncode +WVTAsn1IntentToSealAttributeDecode +WVTAsn1IntentToSealAttributeEncode +WVTAsn1SealingSignatureAttributeDecode +WVTAsn1SealingSignatureAttributeEncode +WVTAsn1SealingTimestampAttributeDecode +WVTAsn1SealingTimestampAttributeEncode +WVTAsn1SpcFinancialCriteriaInfoDecode +WVTAsn1SpcFinancialCriteriaInfoEncode +WVTAsn1SpcIndirectDataContentDecode +WVTAsn1SpcIndirectDataContentEncode +WVTAsn1SpcLinkDecode +WVTAsn1SpcLinkEncode +WVTAsn1SpcMinimalCriteriaInfoDecode +WVTAsn1SpcMinimalCriteriaInfoEncode +WVTAsn1SpcPeImageDataDecode +WVTAsn1SpcPeImageDataEncode +WVTAsn1SpcSigInfoDecode +WVTAsn1SpcSigInfoEncode +WVTAsn1SpcSpAgencyInfoDecode +WVTAsn1SpcSpAgencyInfoEncode +WVTAsn1SpcSpOpusInfoDecode +WVTAsn1SpcSpOpusInfoEncode +WVTAsn1SpcStatementTypeDecode +WVTAsn1SpcStatementTypeEncode +WinVerifyTrust +WinVerifyTrustEx +WintrustAddActionID +WintrustAddDefaultForUsage +WintrustCertificateTrust +WintrustGetDefaultForUsage +WintrustGetRegPolicyFlags +WintrustLoadFunctionPointers +WintrustRemoveActionID +WintrustSetDefaultIncludePEPageHashes +WintrustSetRegPolicyFlags +mscat32DllRegisterServer +mscat32DllUnregisterServer +mssip32DllRegisterServer +mssip32DllUnregisterServer diff --git a/lib/libc/musl/COPYRIGHT b/lib/libc/musl/COPYRIGHT new file mode 100644 index 000000000..67802d119 --- /dev/null +++ b/lib/libc/musl/COPYRIGHT @@ -0,0 +1,190 @@ +musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright ยฉ 2005-2019 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- + +Authors/contributors include: + +A. Wilcox +Alex Dowad +Alex Suykov +Alexander Monakov +Andre McCurdy +Andrew Kelley +Anthony G. Basile +Aric Belsito +Arvid Picciani +Bartosz Brachaczek +Benjamin Peterson +Bobby Bingham +Boris Brezillon +Brent Cook +Chris Spiegel +Clรฉment Vasseur +Daniel Micay +Daniel Sabogal +Daurnimator +David Carlier +David Edelsohn +Denys Vlasenko +Dmitry Ivanov +Dmitry V. Levin +Drew DeVault +Emil Renner Berthing +Fangrui Song +Felix Fietkau +Felix Janda +Gianluca Anzolin +Hauke Mehrtens +He X +Hiltjo Posthuma +Isaac Dunham +Jaydeep Patil +Jens Gustedt +Jeremy Huntwork +Jo-Philipp Wich +Joakim Sindholt +John Spencer +Josiah Worcester +Julien Ramseier +Justin Cormack +Kaarle Ritvanen +Khem Raj +Kylie McClain +Leah Neukirchen +Luca Barbato +Luka Perkov +M Farkas-Dyck (Strake) +Mahesh Bodapati +Markus Wichmann +Masanori Ogino +Michael Clark +Michael Forney +Mikhail Kremnyov +Natanael Copa +Nicholas J. Kain +orc +Pascal Cuoq +Patrick Oppenlander +Petr Hosek +Petr Skocik +Pierre Carrier +Reini Urban +Rich Felker +Richard Pennington +Ryan Fairfax +Samuel Holland +Segev Finer +Shiz +sin +Solar Designer +Stefan Kristiansson +Stefan O'Rear +Szabolcs Nagy +Timo Terรคs +Trutz Behn +Valentin Ochs +Will Dietz +William Haddon +William Pitcock + +Portions of this software are derived from third-party works licensed +under terms compatible with the above MIT license: + +The TRE regular expression implementation (src/regex/reg* and +src/regex/tre*) is Copyright ยฉ 2001-2008 Ville Laurikari and licensed +under a 2-clause BSD license (license text in the source files). The +included version has been heavily modified by Rich Felker in 2012, in +the interests of size, simplicity, and namespace cleanliness. + +Much of the math library code (src/math/* and src/complex/*) is +Copyright ยฉ 1993,2004 Sun Microsystems or +Copyright ยฉ 2003-2011 David Schultz or +Copyright ยฉ 2003-2009 Steven G. Kargl or +Copyright ยฉ 2003-2009 Bruce D. Evans or +Copyright ยฉ 2008 Stephen L. Moshier or +Copyright ยฉ 2017-2018 Arm Limited +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. + +The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright ยฉ 2008 +The Android Open Source Project and is licensed under a two-clause BSD +license. It was taken from Bionic libc, used on Android. + +The implementation of DES for crypt (src/crypt/crypt_des.c) is +Copyright ยฉ 1994 David Burren. It is licensed under a BSD license. + +The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +originally written by Solar Designer and placed into the public +domain. The code also comes with a fallback permissive license for use +in jurisdictions that may not recognize the public domain. + +The smoothsort implementation (src/stdlib/qsort.c) is Copyright ยฉ 2011 +Valentin Ochs and is licensed under an MIT-style license. + +The x86_64 port was written by Nicholas J. Kain and is licensed under +the standard MIT terms. + +The mips and microblaze ports were originally written by Richard +Pennington for use in the ellcc project. The original code was adapted +by Rich Felker for build system and code conventions during upstream +integration. It is licensed under the standard MIT terms. + +The mips64 port was contributed by Imagination Technologies and is +licensed under the standard MIT terms. + +The powerpc port was also originally written by Richard Pennington, +and later supplemented and integrated by John Spencer. It is licensed +under the standard MIT terms. + +All other files which have no copyright comments are original works +produced specifically for use as part of this library, written either +by Rich Felker, the main author of the library, or by one or more +contibutors listed above. Details on authorship of individual files +can be found in the git version control history of the project. The +omission of copyright and license comments in each file is in the +interest of source tree size. + +In addition, permission is hereby granted for all public header files +(include/* and arch/*/bits/*) and crt files intended to be linked into +applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit +the copyright notice and permission notice otherwise required by the +license, and to use these files without any requirement of +attribution. These files include substantial contributions from: + +Bobby Bingham +John Spencer +Nicholas J. Kain +Rich Felker +Richard Pennington +Stefan Kristiansson +Szabolcs Nagy + +all of whom have explicitly granted such permission. + +This file previously contained text expressing a belief that most of +the files covered by the above exception were sufficiently trivial not +to be subject to copyright, resulting in confusion over whether it +negated the permissions granted in the license. In the spirit of +permissive licensing, and of not having licensing issues being an +obstacle to adoption, that text has been removed. diff --git a/lib/libc/musl/arch/aarch64/bits/hwcap.h b/lib/libc/musl/arch/aarch64/bits/hwcap.h index ad670914c..a7484028e 100644 --- a/lib/libc/musl/arch/aarch64/bits/hwcap.h +++ b/lib/libc/musl/arch/aarch64/bits/hwcap.h @@ -30,3 +30,11 @@ #define HWCAP_SB (1 << 29) #define HWCAP_PACA (1 << 30) #define HWCAP_PACG (1UL << 31) + +#define HWCAP2_DCPODP (1 << 0) +#define HWCAP2_SVE2 (1 << 1) +#define HWCAP2_SVEAES (1 << 2) +#define HWCAP2_SVEPMULL (1 << 3) +#define HWCAP2_SVEBITPERM (1 << 4) +#define HWCAP2_SVESHA3 (1 << 5) +#define HWCAP2_SVESM4 (1 << 6) diff --git a/lib/libc/musl/arch/aarch64/bits/ipc.h b/lib/libc/musl/arch/aarch64/bits/ipc.h deleted file mode 100644 index 6f3328a86..000000000 --- a/lib/libc/musl/arch/aarch64/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __ipc_perm_seq; - - unsigned long __pad1; - unsigned long __pad2; -}; - -#define IPC_64 0 diff --git a/lib/libc/musl/arch/aarch64/bits/msg.h b/lib/libc/musl/arch/aarch64/bits/msg.h deleted file mode 100644 index 641e17035..000000000 --- a/lib/libc/musl/arch/aarch64/bits/msg.h +++ /dev/null @@ -1,13 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __pad1; - unsigned long __pad2; -}; diff --git a/lib/libc/musl/arch/aarch64/bits/sem.h b/lib/libc/musl/arch/aarch64/bits/sem.h deleted file mode 100644 index e46ced95d..000000000 --- a/lib/libc/musl/arch/aarch64/bits/sem.h +++ /dev/null @@ -1,14 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; -#if __BYTE_ORDER == __LITTLE_ENDIAN - unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; -#else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; - unsigned short sem_nsems; -#endif - time_t __unused3; - time_t __unused4; -}; diff --git a/lib/libc/musl/arch/aarch64/bits/shm.h b/lib/libc/musl/arch/aarch64/bits/shm.h deleted file mode 100644 index 8d1937819..000000000 --- a/lib/libc/musl/arch/aarch64/bits/shm.h +++ /dev/null @@ -1,24 +0,0 @@ -#define SHMLBA 4096 - -struct shmid_ds { - struct ipc_perm shm_perm; - size_t shm_segsz; - time_t shm_atime; - time_t shm_dtime; - time_t shm_ctime; - pid_t shm_cpid; - pid_t shm_lpid; - unsigned long shm_nattch; - unsigned long __pad1; - unsigned long __pad2; -}; - -struct shminfo { - unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; -}; - -struct shm_info { - int __used_ids; - unsigned long shm_tot, shm_rss, shm_swp; - unsigned long __swap_attempts, __swap_successes; -}; diff --git a/lib/libc/musl/arch/aarch64/bits/syscall.h.in b/lib/libc/musl/arch/aarch64/bits/syscall.h.in index eed5a612c..955e2caba 100644 --- a/lib/libc/musl/arch/aarch64/bits/syscall.h.in +++ b/lib/libc/musl/arch/aarch64/bits/syscall.h.in @@ -281,4 +281,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/include/aarch64-linux-musl/bits/stat.h b/lib/libc/musl/arch/aarch64/kstat.h similarity index 61% rename from lib/libc/include/aarch64-linux-musl/bits/stat.h rename to lib/libc/musl/arch/aarch64/kstat.h index 6ce9af119..92625f363 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/stat.h +++ b/lib/libc/musl/arch/aarch64/kstat.h @@ -1,4 +1,4 @@ -struct stat { +struct kstat { dev_t st_dev; ino_t st_ino; mode_t st_mode; @@ -11,8 +11,11 @@ struct stat { blksize_t st_blksize; int __pad2; blkcnt_t st_blocks; - struct timespec st_atim; - struct timespec st_mtim; - struct timespec st_ctim; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; unsigned __unused[2]; -}; \ No newline at end of file +}; diff --git a/lib/libc/musl/arch/aarch64/syscall_arch.h b/lib/libc/musl/arch/aarch64/syscall_arch.h index 25f5ce670..504983aa2 100644 --- a/lib/libc/musl/arch/aarch64/syscall_arch.h +++ b/lib/libc/musl/arch/aarch64/syscall_arch.h @@ -74,3 +74,5 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo #define VDSO_USEFUL #define VDSO_CGT_SYM "__kernel_clock_gettime" #define VDSO_CGT_VER "LINUX_2.6.39" + +#define IPC_64 0 diff --git a/lib/libc/musl/arch/arm/bits/ipcstat.h b/lib/libc/musl/arch/arm/bits/ipcstat.h new file mode 100644 index 000000000..0018ad1e2 --- /dev/null +++ b/lib/libc/musl/arch/arm/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 diff --git a/lib/libc/musl/arch/s390x/bits/msg.h b/lib/libc/musl/arch/arm/bits/msg.h similarity index 83% rename from lib/libc/musl/arch/s390x/bits/msg.h rename to lib/libc/musl/arch/arm/bits/msg.h index 2e23ca27f..bc8436c4a 100644 --- a/lib/libc/musl/arch/s390x/bits/msg.h +++ b/lib/libc/musl/arch/arm/bits/msg.h @@ -1,8 +1,11 @@ struct msqid_ds { struct ipc_perm msg_perm; time_t msg_stime; + int __unused1; time_t msg_rtime; + int __unused2; time_t msg_ctime; + int __unused3; unsigned long msg_cbytes; msgqnum_t msg_qnum; msglen_t msg_qbytes; diff --git a/lib/libc/musl/arch/mips64/bits/sem.h b/lib/libc/musl/arch/arm/bits/sem.h similarity index 52% rename from lib/libc/musl/arch/mips64/bits/sem.h rename to lib/libc/musl/arch/arm/bits/sem.h index e46ced95d..d383d4ea5 100644 --- a/lib/libc/musl/arch/mips64/bits/sem.h +++ b/lib/libc/musl/arch/arm/bits/sem.h @@ -1,14 +1,16 @@ struct semid_ds { struct ipc_perm sem_perm; time_t sem_otime; + long __unused1; time_t sem_ctime; + long __unused2; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; #else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; unsigned short sem_nsems; #endif - time_t __unused3; - time_t __unused4; + long __unused3; + long __unused4; }; diff --git a/lib/libc/musl/arch/s390x/bits/shm.h b/lib/libc/musl/arch/arm/bits/shm.h similarity index 90% rename from lib/libc/musl/arch/s390x/bits/shm.h rename to lib/libc/musl/arch/arm/bits/shm.h index 6652d6592..81b2a29ab 100644 --- a/lib/libc/musl/arch/s390x/bits/shm.h +++ b/lib/libc/musl/arch/arm/bits/shm.h @@ -4,8 +4,11 @@ struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz; time_t shm_atime; + int __unused1; time_t shm_dtime; + int __unused2; time_t shm_ctime; + int __unused3; pid_t shm_cpid; pid_t shm_lpid; unsigned long shm_nattch; @@ -22,4 +25,3 @@ struct shm_info { unsigned long shm_tot, shm_rss, shm_swp; unsigned long __swap_attempts, __swap_successes; }; - diff --git a/lib/libc/musl/arch/arm/bits/syscall.h.in b/lib/libc/musl/arch/arm/bits/syscall.h.in index 8ce1d70a4..a565a4ee1 100644 --- a/lib/libc/musl/arch/arm/bits/syscall.h.in +++ b/lib/libc/musl/arch/arm/bits/syscall.h.in @@ -381,6 +381,12 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 #define __ARM_NR_breakpoint 0x0f0001 #define __ARM_NR_cacheflush 0x0f0002 diff --git a/lib/libc/musl/arch/arm/kstat.h b/lib/libc/musl/arch/arm/kstat.h new file mode 100644 index 000000000..af449c950 --- /dev/null +++ b/lib/libc/musl/arch/arm/kstat.h @@ -0,0 +1,21 @@ +struct kstat { + dev_t st_dev; + int __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + int __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + ino_t st_ino; +}; diff --git a/lib/libc/musl/arch/generic/bits/ipc.h b/lib/libc/musl/arch/generic/bits/ipc.h index 779c42fd7..40d6f3a25 100644 --- a/lib/libc/musl/arch/generic/bits/ipc.h +++ b/lib/libc/musl/arch/generic/bits/ipc.h @@ -9,5 +9,3 @@ struct ipc_perm { long __pad1; long __pad2; }; - -#define IPC_64 0x100 diff --git a/lib/libc/musl/arch/generic/bits/ipcstat.h b/lib/libc/musl/arch/generic/bits/ipcstat.h new file mode 100644 index 000000000..0018ad1e2 --- /dev/null +++ b/lib/libc/musl/arch/generic/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 diff --git a/lib/libc/musl/arch/generic/bits/msg.h b/lib/libc/musl/arch/generic/bits/msg.h index bc8436c4a..2e23ca27f 100644 --- a/lib/libc/musl/arch/generic/bits/msg.h +++ b/lib/libc/musl/arch/generic/bits/msg.h @@ -1,11 +1,8 @@ struct msqid_ds { struct ipc_perm msg_perm; time_t msg_stime; - int __unused1; time_t msg_rtime; - int __unused2; time_t msg_ctime; - int __unused3; unsigned long msg_cbytes; msgqnum_t msg_qnum; msglen_t msg_qbytes; diff --git a/lib/libc/musl/arch/generic/bits/sem.h b/lib/libc/musl/arch/generic/bits/sem.h index c629b81eb..5184eb597 100644 --- a/lib/libc/musl/arch/generic/bits/sem.h +++ b/lib/libc/musl/arch/generic/bits/sem.h @@ -1,16 +1,14 @@ struct semid_ds { struct ipc_perm sem_perm; time_t sem_otime; - time_t __unused1; time_t sem_ctime; - time_t __unused2; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; #else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; unsigned short sem_nsems; #endif - time_t __unused3; - time_t __unused4; + long __unused3; + long __unused4; }; diff --git a/lib/libc/musl/arch/generic/bits/shm.h b/lib/libc/musl/arch/generic/bits/shm.h index 45d1d1578..8d1937819 100644 --- a/lib/libc/musl/arch/generic/bits/shm.h +++ b/lib/libc/musl/arch/generic/bits/shm.h @@ -4,11 +4,8 @@ struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz; time_t shm_atime; - int __unused1; time_t shm_dtime; - int __unused2; time_t shm_ctime; - int __unused3; pid_t shm_cpid; pid_t shm_lpid; unsigned long shm_nattch; @@ -25,4 +22,3 @@ struct shm_info { unsigned long shm_tot, shm_rss, shm_swp; unsigned long __swap_attempts, __swap_successes; }; - diff --git a/lib/libc/musl/arch/i386/bits/ipcstat.h b/lib/libc/musl/arch/i386/bits/ipcstat.h new file mode 100644 index 000000000..0018ad1e2 --- /dev/null +++ b/lib/libc/musl/arch/i386/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 diff --git a/lib/libc/musl/arch/x86_64/bits/msg.h b/lib/libc/musl/arch/i386/bits/msg.h similarity index 83% rename from lib/libc/musl/arch/x86_64/bits/msg.h rename to lib/libc/musl/arch/i386/bits/msg.h index 2e23ca27f..bc8436c4a 100644 --- a/lib/libc/musl/arch/x86_64/bits/msg.h +++ b/lib/libc/musl/arch/i386/bits/msg.h @@ -1,8 +1,11 @@ struct msqid_ds { struct ipc_perm msg_perm; time_t msg_stime; + int __unused1; time_t msg_rtime; + int __unused2; time_t msg_ctime; + int __unused3; unsigned long msg_cbytes; msgqnum_t msg_qnum; msglen_t msg_qbytes; diff --git a/lib/libc/musl/arch/i386/bits/sem.h b/lib/libc/musl/arch/i386/bits/sem.h new file mode 100644 index 000000000..e61571c12 --- /dev/null +++ b/lib/libc/musl/arch/i386/bits/sem.h @@ -0,0 +1,11 @@ +struct semid_ds { + struct ipc_perm sem_perm; + time_t sem_otime; + long __unused1; + time_t sem_ctime; + long __unused2; + unsigned short sem_nsems; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; + long __unused3; + long __unused4; +}; diff --git a/lib/libc/musl/arch/mips64/bits/shm.h b/lib/libc/musl/arch/i386/bits/shm.h similarity index 90% rename from lib/libc/musl/arch/mips64/bits/shm.h rename to lib/libc/musl/arch/i386/bits/shm.h index 8d1937819..81b2a29ab 100644 --- a/lib/libc/musl/arch/mips64/bits/shm.h +++ b/lib/libc/musl/arch/i386/bits/shm.h @@ -4,8 +4,11 @@ struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz; time_t shm_atime; + int __unused1; time_t shm_dtime; + int __unused2; time_t shm_ctime; + int __unused3; pid_t shm_cpid; pid_t shm_lpid; unsigned long shm_nattch; diff --git a/lib/libc/musl/arch/i386/bits/syscall.h.in b/lib/libc/musl/arch/i386/bits/syscall.h.in index fdfdc7108..c95e108e5 100644 --- a/lib/libc/musl/arch/i386/bits/syscall.h.in +++ b/lib/libc/musl/arch/i386/bits/syscall.h.in @@ -418,4 +418,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/musl/arch/i386/kstat.h b/lib/libc/musl/arch/i386/kstat.h new file mode 100644 index 000000000..af449c950 --- /dev/null +++ b/lib/libc/musl/arch/i386/kstat.h @@ -0,0 +1,21 @@ +struct kstat { + dev_t st_dev; + int __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + int __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + ino_t st_ino; +}; diff --git a/lib/libc/musl/arch/mips/bits/ipcstat.h b/lib/libc/musl/arch/mips/bits/ipcstat.h new file mode 100644 index 000000000..0018ad1e2 --- /dev/null +++ b/lib/libc/musl/arch/mips/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 diff --git a/lib/libc/musl/arch/mips/bits/sem.h b/lib/libc/musl/arch/mips/bits/sem.h index e46ced95d..5184eb597 100644 --- a/lib/libc/musl/arch/mips/bits/sem.h +++ b/lib/libc/musl/arch/mips/bits/sem.h @@ -4,11 +4,11 @@ struct semid_ds { time_t sem_ctime; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; #else - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; unsigned short sem_nsems; #endif - time_t __unused3; - time_t __unused4; + long __unused3; + long __unused4; }; diff --git a/lib/libc/musl/arch/mips/bits/shm.h b/lib/libc/musl/arch/mips/bits/shm.h index 6652d6592..8d1937819 100644 --- a/lib/libc/musl/arch/mips/bits/shm.h +++ b/lib/libc/musl/arch/mips/bits/shm.h @@ -22,4 +22,3 @@ struct shm_info { unsigned long shm_tot, shm_rss, shm_swp; unsigned long __swap_attempts, __swap_successes; }; - diff --git a/lib/libc/musl/arch/mips/bits/syscall.h.in b/lib/libc/musl/arch/mips/bits/syscall.h.in index 6175a7c22..582fa3b51 100644 --- a/lib/libc/musl/arch/mips/bits/syscall.h.in +++ b/lib/libc/musl/arch/mips/bits/syscall.h.in @@ -400,4 +400,10 @@ #define __NR_io_uring_setup 4425 #define __NR_io_uring_enter 4426 #define __NR_io_uring_register 4427 +#define __NR_open_tree 4428 +#define __NR_move_mount 4429 +#define __NR_fsopen 4430 +#define __NR_fsconfig 4431 +#define __NR_fsmount 4432 +#define __NR_fspick 4433 diff --git a/lib/libc/musl/arch/mips/kstat.h b/lib/libc/musl/arch/mips/kstat.h new file mode 100644 index 000000000..5e637eabf --- /dev/null +++ b/lib/libc/musl/arch/mips/kstat.h @@ -0,0 +1,22 @@ +struct kstat { + unsigned st_dev; + long __st_padding1[3]; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + unsigned st_rdev; + long __st_padding2[3]; + off_t st_size; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + blksize_t st_blksize; + long __st_padding3; + blkcnt_t st_blocks; + long __st_padding4[14]; +}; diff --git a/lib/libc/musl/arch/mips/syscall_arch.h b/lib/libc/musl/arch/mips/syscall_arch.h index 43bcdee7a..6ea73437b 100644 --- a/lib/libc/musl/arch/mips/syscall_arch.h +++ b/lib/libc/musl/arch/mips/syscall_arch.h @@ -5,27 +5,25 @@ #define SYSCALL_RLIM_INFINITY (-1UL/2) -#if _MIPSEL || __MIPSEL || __MIPSEL__ -#define __stat_fix(st) ((st),(void)0) +#if __mips_isa_rev >= 6 +#define SYSCALL_CLOBBERLIST \ + "$1", "$3", "$11", "$12", "$13", \ + "$14", "$15", "$24", "$25", "memory" #else -#include -static inline void __stat_fix(long p) -{ - struct stat *st = (struct stat *)p; - st->st_dev >>= 32; - st->st_rdev >>= 32; -} +#define SYSCALL_CLOBBERLIST \ + "$1", "$3", "$11", "$12", "$13", \ + "$14", "$15", "$24", "$25", "hi", "lo", "memory" #endif static inline long __syscall0(long n) { register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "addu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); + "syscall" + : "+r"(r2), "=r"(r7) + : + : SYSCALL_CLOBBERLIST, "$8", "$9", "$10"); return r7 ? -r2 : r2; } @@ -33,13 +31,12 @@ static inline long __syscall1(long n, long a) { register long r4 __asm__("$4") = a; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "addu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); + "syscall" + : "+r"(r2), "=r"(r7) + : "r"(r4) + : SYSCALL_CLOBBERLIST, "$8", "$9", "$10"); return r7 ? -r2 : r2; } @@ -48,17 +45,13 @@ static inline long __syscall2(long n, long a, long b) register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "addu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - return ret; + "syscall" + : "+r"(r2), "=r"(r7) + : "r"(r4), "r"(r5) + : SYSCALL_CLOBBERLIST, "$8", "$9", "$10"); + return r7 ? -r2 : r2; } static inline long __syscall3(long n, long a, long b, long c) @@ -67,17 +60,13 @@ static inline long __syscall3(long n, long a, long b, long c) register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "addu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - return ret; + "syscall" + : "+r"(r2), "=r"(r7) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST, "$8", "$9", "$10"); + return r7 ? -r2 : r2; } static inline long __syscall4(long n, long a, long b, long c, long d) @@ -86,18 +75,13 @@ static inline long __syscall4(long n, long a, long b, long c, long d) register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7") = d; - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "addu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - if (n == SYS_fstatat64) __stat_fix(c); - return ret; + "syscall" + : "+r"(r2), "+r"(r7) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST, "$8", "$9", "$10"); + return r7 ? -r2 : r2; } static inline long __syscall5(long n, long a, long b, long c, long d, long e) @@ -107,20 +91,15 @@ static inline long __syscall5(long n, long a, long b, long c, long d, long e) register long r6 __asm__("$6") = c; register long r7 __asm__("$7") = d; register long r8 __asm__("$8") = e; - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( "subu $sp,$sp,32 ; sw $8,16($sp) ; " - "addu $2,$0,%3 ; syscall ;" + "syscall ;" "addu $sp,$sp,32" - : "=&r"(r2), "=r"(r7), "+r"(r8) - : "ir"(n), "0"(r2), "1"(r7), "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - if (n == SYS_fstatat64) __stat_fix(c); - return r2; + : "+r"(r2), "+r"(r7), "+r"(r8) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST, "$9", "$10"); + return r7 ? -r2 : r2; } static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f) @@ -131,20 +110,15 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo register long r7 __asm__("$7") = d; register long r8 __asm__("$8") = e; register long r9 __asm__("$9") = f; - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( "subu $sp,$sp,32 ; sw $8,16($sp) ; sw $9,20($sp) ; " - "addu $2,$0,%4 ; syscall ;" + "syscall ;" "addu $sp,$sp,32" - : "=&r"(r2), "=r"(r7), "+r"(r8), "+r"(r9) - : "ir"(n), "0"(r2), "1"(r7), "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - if (n == SYS_fstatat64) __stat_fix(c); - return r2; + : "+r"(r2), "+r"(r7), "+r"(r8), "+r"(r9) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST, "$10"); + return r7 ? -r2 : r2; } static inline long __syscall7(long n, long a, long b, long c, long d, long e, long f, long g) @@ -156,22 +130,20 @@ static inline long __syscall7(long n, long a, long b, long c, long d, long e, lo register long r8 __asm__("$8") = e; register long r9 __asm__("$9") = f; register long r10 __asm__("$10") = g; - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( "subu $sp,$sp,32 ; sw $8,16($sp) ; sw $9,20($sp) ; sw $10,24($sp) ; " - "addu $2,$0,%5 ; syscall ;" + "syscall ;" "addu $sp,$sp,32" - : "=&r"(r2), "=r"(r7), "+r"(r8), "+r"(r9), "+r"(r10) - : "ir"(n), "0"(r2), "1"(r7), "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - if (r7) return -r2; - long ret = r2; - if (n == SYS_stat64 || n == SYS_fstat64 || n == SYS_lstat64) __stat_fix(b); - if (n == SYS_fstatat64) __stat_fix(c); - return r2; + : "+r"(r2), "+r"(r7), "+r"(r8), "+r"(r9), "+r"(r10) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } #define VDSO_USEFUL #define VDSO_CGT_SYM "__vdso_clock_gettime" #define VDSO_CGT_VER "LINUX_2.6" + +#define SO_SNDTIMEO_OLD 0x1005 +#define SO_RCVTIMEO_OLD 0x1006 diff --git a/lib/libc/musl/arch/mips64/bits/ipc.h b/lib/libc/musl/arch/mips64/bits/ipc.h index 43a8314e2..df2271686 100644 --- a/lib/libc/musl/arch/mips64/bits/ipc.h +++ b/lib/libc/musl/arch/mips64/bits/ipc.h @@ -10,5 +10,3 @@ struct ipc_perm { unsigned long __unused1; unsigned long __unused2; }; - -#define IPC_64 0x100 diff --git a/lib/libc/musl/arch/mips64/bits/msg.h b/lib/libc/musl/arch/mips64/bits/msg.h deleted file mode 100644 index 641e17035..000000000 --- a/lib/libc/musl/arch/mips64/bits/msg.h +++ /dev/null @@ -1,13 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __pad1; - unsigned long __pad2; -}; diff --git a/lib/libc/musl/arch/mips64/bits/stat.h b/lib/libc/musl/arch/mips64/bits/stat.h index b46617f14..b620e142d 100644 --- a/lib/libc/musl/arch/mips64/bits/stat.h +++ b/lib/libc/musl/arch/mips64/bits/stat.h @@ -1,6 +1,3 @@ -#include -#include - struct stat { dev_t st_dev; int __pad1[3]; diff --git a/lib/libc/musl/arch/mips64/bits/syscall.h.in b/lib/libc/musl/arch/mips64/bits/syscall.h.in index ca99e453f..34b9752e6 100644 --- a/lib/libc/musl/arch/mips64/bits/syscall.h.in +++ b/lib/libc/musl/arch/mips64/bits/syscall.h.in @@ -330,4 +330,10 @@ #define __NR_io_uring_setup 5425 #define __NR_io_uring_enter 5426 #define __NR_io_uring_register 5427 +#define __NR_open_tree 5428 +#define __NR_move_mount 5429 +#define __NR_fsopen 5430 +#define __NR_fsconfig 5431 +#define __NR_fsmount 5432 +#define __NR_fspick 5433 diff --git a/lib/libc/musl/arch/mips64/kstat.h b/lib/libc/musl/arch/mips64/kstat.h new file mode 100644 index 000000000..9a4468b4f --- /dev/null +++ b/lib/libc/musl/arch/mips64/kstat.h @@ -0,0 +1,21 @@ +struct kstat { + unsigned st_dev; + int __pad1[3]; + ino_t st_ino; + mode_t st_mode; + unsigned st_nlink; + uid_t st_uid; + gid_t st_gid; + unsigned st_rdev; + int __pad2[3]; + off_t st_size; + int st_atime_sec; + int st_atime_nsec; + int st_mtime_sec; + int st_mtime_nsec; + int st_ctime_sec; + int st_ctime_nsec; + unsigned st_blksize; + unsigned __pad3; + blkcnt_t st_blocks; +}; diff --git a/lib/libc/musl/arch/mips64/syscall_arch.h b/lib/libc/musl/arch/mips64/syscall_arch.h index 99eebc326..69c429b86 100644 --- a/lib/libc/musl/arch/mips64/syscall_arch.h +++ b/lib/libc/musl/arch/mips64/syscall_arch.h @@ -3,58 +3,25 @@ #define SYSCALL_RLIM_INFINITY (-1UL/2) -#include -struct kernel_stat { - unsigned int st_dev; - unsigned int __pad1[3]; - unsigned long long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - int st_uid; - int st_gid; - unsigned int st_rdev; - unsigned int __pad2[3]; - long long st_size; - unsigned int st_atime_sec; - unsigned int st_atime_nsec; - unsigned int st_mtime_sec; - unsigned int st_mtime_nsec; - unsigned int st_ctime_sec; - unsigned int st_ctime_nsec; - unsigned int st_blksize; - unsigned int __pad3; - unsigned long long st_blocks; -}; - -static void __stat_fix(struct kernel_stat *kst, struct stat *st) -{ - st->st_dev = kst->st_dev; - st->st_ino = kst->st_ino; - st->st_mode = kst->st_mode; - st->st_nlink = kst->st_nlink; - st->st_uid = kst->st_uid; - st->st_gid = kst->st_gid; - st->st_rdev = kst->st_rdev; - st->st_size = kst->st_size; - st->st_atim.tv_sec = kst->st_atime_sec; - st->st_atim.tv_nsec = kst->st_atime_nsec; - st->st_mtim.tv_sec = kst->st_mtime_sec; - st->st_mtim.tv_nsec = kst->st_mtime_nsec; - st->st_ctim.tv_sec = kst->st_ctime_sec; - st->st_ctim.tv_nsec = kst->st_ctime_nsec; - st->st_blksize = kst->st_blksize; - st->st_blocks = kst->st_blocks; -} +#if __mips_isa_rev >= 6 +#define SYSCALL_CLOBBERLIST \ + "$1", "$3", "$10", "$11", "$12", "$13", \ + "$14", "$15", "$24", "$25", "memory" +#else +#define SYSCALL_CLOBBERLIST \ + "$1", "$3", "$10", "$11", "$12", "$13", \ + "$14", "$15", "$24", "$25", "hi", "lo", "memory" +#endif static inline long __syscall0(long n) { register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); + "syscall" + : "+&r"(r2), "=r"(r7) + : + : SYSCALL_CLOBBERLIST); return r7 ? -r2 : r2; } @@ -62,175 +29,100 @@ static inline long __syscall1(long n, long a) { register long r4 __asm__("$4") = a; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); + "syscall" + : "+&r"(r2), "=r"(r7) + : "r"(r4) + : SYSCALL_CLOBBERLIST); return r7 ? -r2 : r2; } static inline long __syscall2(long n, long a, long b) { - struct kernel_stat kst; - long ret; register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - r5 = (long) &kst; + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - if (r7) return -r2; - ret = r2; - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - __stat_fix(&kst, (struct stat *)b); - - return ret; + "syscall" + : "+&r"(r2), "=r"(r7) + : "r"(r4), "r"(r5) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } static inline long __syscall3(long n, long a, long b, long c) { - struct kernel_stat kst; - long ret; register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7"); - register long r2 __asm__("$2"); - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - r5 = (long) &kst; + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - if (r7) return -r2; - ret = r2; - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - __stat_fix(&kst, (struct stat *)b); - - return ret; + "syscall" + : "+&r"(r2), "=r"(r7) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } static inline long __syscall4(long n, long a, long b, long c, long d) { - struct kernel_stat kst; - long ret; register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7") = d; - register long r2 __asm__("$2"); - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - r5 = (long) &kst; - if (n == SYS_newfstatat) - r6 = (long) &kst; + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6) - : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - if (r7) return -r2; - ret = r2; - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - __stat_fix(&kst, (struct stat *)b); - if (n == SYS_newfstatat) - __stat_fix(&kst, (struct stat *)c); - - return ret; + "syscall" + : "+&r"(r2), "+r"(r7) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } static inline long __syscall5(long n, long a, long b, long c, long d, long e) { - struct kernel_stat kst; - long ret; register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7") = d; register long r8 __asm__("$8") = e; - register long r2 __asm__("$2"); - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - r5 = (long) &kst; - if (n == SYS_newfstatat) - r6 = (long) &kst; + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6), "r"(r8) - : "$1", "$3", "$9", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - if (r7) return -r2; - ret = r2; - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - __stat_fix(&kst, (struct stat *)b); - if (n == SYS_newfstatat) - __stat_fix(&kst, (struct stat *)c); - - return ret; + "syscall" + : "+&r"(r2), "+r"(r7) + : "r"(r4), "r"(r5), "r"(r6), "r"(r8) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f) { - struct kernel_stat kst; - long ret; register long r4 __asm__("$4") = a; register long r5 __asm__("$5") = b; register long r6 __asm__("$6") = c; register long r7 __asm__("$7") = d; register long r8 __asm__("$8") = e; register long r9 __asm__("$9") = f; - register long r2 __asm__("$2"); - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - r5 = (long) &kst; - if (n == SYS_newfstatat) - r6 = (long) &kst; + register long r2 __asm__("$2") = n; __asm__ __volatile__ ( - "daddu $2,$0,%2 ; syscall" - : "=&r"(r2), "=r"(r7) : "ir"(n), "0"(r2), "1"(r7), - "r"(r4), "r"(r5), "r"(r6), "r"(r8), "r"(r9) - : "$1", "$3", "$10", "$11", "$12", "$13", - "$14", "$15", "$24", "$25", "hi", "lo", "memory"); - - if (r7) return -r2; - ret = r2; - - if (n == SYS_stat || n == SYS_fstat || n == SYS_lstat) - __stat_fix(&kst, (struct stat *)b); - if (n == SYS_newfstatat) - __stat_fix(&kst, (struct stat *)c); - - return ret; + "syscall" + : "+&r"(r2), "+r"(r7) + : "r"(r4), "r"(r5), "r"(r6), "r"(r8), "r"(r9) + : SYSCALL_CLOBBERLIST); + return r7 ? -r2 : r2; } #define VDSO_USEFUL #define VDSO_CGT_SYM "__vdso_clock_gettime" #define VDSO_CGT_VER "LINUX_2.6" + +#define SO_SNDTIMEO_OLD 0x1005 +#define SO_RCVTIMEO_OLD 0x1006 diff --git a/lib/libc/musl/arch/powerpc/bits/alltypes.h.in b/lib/libc/musl/arch/powerpc/bits/alltypes.h.in index 37f27d6f4..8e687ef16 100644 --- a/lib/libc/musl/arch/powerpc/bits/alltypes.h.in +++ b/lib/libc/musl/arch/powerpc/bits/alltypes.h.in @@ -6,8 +6,12 @@ TYPEDEF __builtin_va_list va_list; TYPEDEF __builtin_va_list __isoc_va_list; #ifndef __cplusplus +#ifdef __WCHAR_TYPE__ +TYPEDEF __WCHAR_TYPE__ wchar_t; +#else TYPEDEF long wchar_t; #endif +#endif TYPEDEF float float_t; TYPEDEF double double_t; diff --git a/lib/libc/musl/arch/powerpc/bits/ipc.h b/lib/libc/musl/arch/powerpc/bits/ipc.h index 3f2ede07b..a388d56b5 100644 --- a/lib/libc/musl/arch/powerpc/bits/ipc.h +++ b/lib/libc/musl/arch/powerpc/bits/ipc.h @@ -10,6 +10,3 @@ struct ipc_perm { long long __pad2; long long __pad3; }; - -#define IPC_64 0x100 - diff --git a/lib/libc/musl/arch/powerpc/bits/ipcstat.h b/lib/libc/musl/arch/powerpc/bits/ipcstat.h new file mode 100644 index 000000000..0018ad1e2 --- /dev/null +++ b/lib/libc/musl/arch/powerpc/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 2 diff --git a/lib/libc/musl/arch/powerpc/bits/shm.h b/lib/libc/musl/arch/powerpc/bits/shm.h index 40e5e8bec..b19801d51 100644 --- a/lib/libc/musl/arch/powerpc/bits/shm.h +++ b/lib/libc/musl/arch/powerpc/bits/shm.h @@ -26,4 +26,3 @@ struct shm_info { unsigned long shm_tot, shm_rss, shm_swp; unsigned long __swap_attempts, __swap_successes; }; - diff --git a/lib/libc/musl/arch/powerpc/bits/syscall.h.in b/lib/libc/musl/arch/powerpc/bits/syscall.h.in index 0a6c9addf..adcf63dbb 100644 --- a/lib/libc/musl/arch/powerpc/bits/syscall.h.in +++ b/lib/libc/musl/arch/powerpc/bits/syscall.h.in @@ -407,4 +407,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/musl/arch/powerpc/bits/user.h b/lib/libc/musl/arch/powerpc/bits/user.h index 6cc8aaf72..7f5287463 100644 --- a/lib/libc/musl/arch/powerpc/bits/user.h +++ b/lib/libc/musl/arch/powerpc/bits/user.h @@ -1,10 +1,8 @@ -struct pt_regs { - unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, mq; - unsigned long trap, dar, dsisr, result; -}; - struct user { - struct pt_regs regs; + struct { + unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, mq; + unsigned long trap, dar, dsisr, result; + } regs; unsigned long u_tsize, u_dsize, u_ssize; unsigned long start_code, start_data, start_stack; long signal; diff --git a/lib/libc/musl/arch/powerpc/kstat.h b/lib/libc/musl/arch/powerpc/kstat.h new file mode 100644 index 000000000..5a611e7b3 --- /dev/null +++ b/lib/libc/musl/arch/powerpc/kstat.h @@ -0,0 +1,20 @@ +struct kstat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + short __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + unsigned __unused[2]; +}; diff --git a/lib/libc/musl/arch/powerpc/reloc.h b/lib/libc/musl/arch/powerpc/reloc.h index 1b4cab36a..527b6b7cd 100644 --- a/lib/libc/musl/arch/powerpc/reloc.h +++ b/lib/libc/musl/arch/powerpc/reloc.h @@ -9,6 +9,7 @@ #define TPOFF_K (-0x7000) #define REL_SYMBOLIC R_PPC_ADDR32 +#define REL_USYMBOLIC R_PPC_UADDR32 #define REL_GOT R_PPC_GLOB_DAT #define REL_PLT R_PPC_JMP_SLOT #define REL_RELATIVE R_PPC_RELATIVE diff --git a/lib/libc/musl/arch/powerpc/syscall_arch.h b/lib/libc/musl/arch/powerpc/syscall_arch.h index e26a3c34c..ede97c1c0 100644 --- a/lib/libc/musl/arch/powerpc/syscall_arch.h +++ b/lib/libc/musl/arch/powerpc/syscall_arch.h @@ -89,3 +89,6 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo } #define SYSCALL_FADVISE_6_ARG + +#define SO_RCVTIMEO_OLD 18 +#define SO_SNDTIMEO_OLD 19 diff --git a/lib/libc/musl/arch/powerpc64/bits/ipc.h b/lib/libc/musl/arch/powerpc64/bits/ipc.h index 3f2ede07b..a388d56b5 100644 --- a/lib/libc/musl/arch/powerpc64/bits/ipc.h +++ b/lib/libc/musl/arch/powerpc64/bits/ipc.h @@ -10,6 +10,3 @@ struct ipc_perm { long long __pad2; long long __pad3; }; - -#define IPC_64 0x100 - diff --git a/lib/libc/musl/arch/powerpc64/bits/msg.h b/lib/libc/musl/arch/powerpc64/bits/msg.h deleted file mode 100644 index 2e23ca27f..000000000 --- a/lib/libc/musl/arch/powerpc64/bits/msg.h +++ /dev/null @@ -1,12 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __unused[2]; -}; diff --git a/lib/libc/musl/arch/powerpc64/bits/sem.h b/lib/libc/musl/arch/powerpc64/bits/sem.h deleted file mode 100644 index 558184dbc..000000000 --- a/lib/libc/musl/arch/powerpc64/bits/sem.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned short __pad[3], sem_nsems; -#else - unsigned short sem_nsems, __pad[3]; -#endif - unsigned long __unused[2]; -}; diff --git a/lib/libc/musl/arch/powerpc64/bits/shm.h b/lib/libc/musl/arch/powerpc64/bits/shm.h index 8108c3a81..b7f73a8de 100644 --- a/lib/libc/musl/arch/powerpc64/bits/shm.h +++ b/lib/libc/musl/arch/powerpc64/bits/shm.h @@ -21,4 +21,3 @@ struct shm_info { unsigned long shm_tot, shm_rss, shm_swp; unsigned long __swap_attempts, __swap_successes; }; - diff --git a/lib/libc/musl/arch/powerpc64/bits/syscall.h.in b/lib/libc/musl/arch/powerpc64/bits/syscall.h.in index 0c894a232..32545b394 100644 --- a/lib/libc/musl/arch/powerpc64/bits/syscall.h.in +++ b/lib/libc/musl/arch/powerpc64/bits/syscall.h.in @@ -379,4 +379,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/musl/arch/powerpc64/bits/user.h b/lib/libc/musl/arch/powerpc64/bits/user.h index 7ca459b32..7e75d2015 100644 --- a/lib/libc/musl/arch/powerpc64/bits/user.h +++ b/lib/libc/musl/arch/powerpc64/bits/user.h @@ -1,10 +1,8 @@ -struct pt_regs { - unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, softe; - unsigned long trap, dar, dsisr, result; -}; - struct user { - struct pt_regs regs; + struct { + unsigned long gpr[32], nip, msr, orig_gpr3, ctr, link, xer, ccr, softe; + unsigned long trap, dar, dsisr, result; + } regs; unsigned long u_tsize, u_dsize, u_ssize; unsigned long start_code, start_data, start_stack; long signal; diff --git a/lib/libc/musl/arch/powerpc64/kstat.h b/lib/libc/musl/arch/powerpc64/kstat.h new file mode 100644 index 000000000..887b3e266 --- /dev/null +++ b/lib/libc/musl/arch/powerpc64/kstat.h @@ -0,0 +1,19 @@ +struct kstat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + unsigned long __unused[3]; +}; diff --git a/lib/libc/musl/arch/powerpc64/reloc.h b/lib/libc/musl/arch/powerpc64/reloc.h index faf70acd7..5bdaeede5 100644 --- a/lib/libc/musl/arch/powerpc64/reloc.h +++ b/lib/libc/musl/arch/powerpc64/reloc.h @@ -11,6 +11,7 @@ #define TPOFF_K (-0x7000) #define REL_SYMBOLIC R_PPC64_ADDR64 +#define REL_USYMBOLIC R_PPC64_UADDR64 #define REL_GOT R_PPC64_GLOB_DAT #define REL_PLT R_PPC64_JMP_SLOT #define REL_RELATIVE R_PPC64_RELATIVE diff --git a/lib/libc/musl/arch/powerpc64/syscall_arch.h b/lib/libc/musl/arch/powerpc64/syscall_arch.h index 1e7306253..76b4e335c 100644 --- a/lib/libc/musl/arch/powerpc64/syscall_arch.h +++ b/lib/libc/musl/arch/powerpc64/syscall_arch.h @@ -85,3 +85,6 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo :: "memory", "cr0", "r9", "r10", "r11", "r12"); return r3; } + +#define SO_RCVTIMEO_OLD 18 +#define SO_SNDTIMEO_OLD 19 diff --git a/lib/libc/musl/arch/riscv64/atomic_arch.h b/lib/libc/musl/arch/riscv64/atomic_arch.h index c97653428..41ad4d049 100644 --- a/lib/libc/musl/arch/riscv64/atomic_arch.h +++ b/lib/libc/musl/arch/riscv64/atomic_arch.h @@ -14,7 +14,7 @@ static inline int a_cas(volatile int *p, int t, int s) " sc.w.aqrl %1, %4, (%2)\n" " bnez %1, 1b\n" "1:" - : "=&r"(old), "=r"(tmp) + : "=&r"(old), "=&r"(tmp) : "r"(p), "r"(t), "r"(s) : "memory"); return old; @@ -31,7 +31,7 @@ static inline void *a_cas_p(volatile void *p, void *t, void *s) " sc.d.aqrl %1, %4, (%2)\n" " bnez %1, 1b\n" "1:" - : "=&r"(old), "=r"(tmp) + : "=&r"(old), "=&r"(tmp) : "r"(p), "r"(t), "r"(s) : "memory"); return old; diff --git a/lib/libc/musl/arch/riscv64/bits/ipc.h b/lib/libc/musl/arch/riscv64/bits/ipc.h deleted file mode 100644 index 6f3328a86..000000000 --- a/lib/libc/musl/arch/riscv64/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __ipc_perm_seq; - - unsigned long __pad1; - unsigned long __pad2; -}; - -#define IPC_64 0 diff --git a/lib/libc/musl/arch/riscv64/bits/msg.h b/lib/libc/musl/arch/riscv64/bits/msg.h deleted file mode 100644 index 641e17035..000000000 --- a/lib/libc/musl/arch/riscv64/bits/msg.h +++ /dev/null @@ -1,13 +0,0 @@ -struct msqid_ds { - struct ipc_perm msg_perm; - time_t msg_stime; - time_t msg_rtime; - time_t msg_ctime; - unsigned long msg_cbytes; - msgqnum_t msg_qnum; - msglen_t msg_qbytes; - pid_t msg_lspid; - pid_t msg_lrpid; - unsigned long __pad1; - unsigned long __pad2; -}; diff --git a/lib/libc/musl/arch/riscv64/bits/sem.h b/lib/libc/musl/arch/riscv64/bits/sem.h deleted file mode 100644 index 5f93c12dd..000000000 --- a/lib/libc/musl/arch/riscv64/bits/sem.h +++ /dev/null @@ -1,9 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; - unsigned short sem_nsems; - char __sem_nsems_pad[sizeof(time_t)-sizeof(short)]; - time_t __unused3; - time_t __unused4; -}; diff --git a/lib/libc/musl/arch/riscv64/bits/shm.h b/lib/libc/musl/arch/riscv64/bits/shm.h deleted file mode 100644 index 4c3c9fb75..000000000 --- a/lib/libc/musl/arch/riscv64/bits/shm.h +++ /dev/null @@ -1,25 +0,0 @@ -#define SHMLBA 4096 - -struct shmid_ds -{ - struct ipc_perm shm_perm; - size_t shm_segsz; - time_t shm_atime; - time_t shm_dtime; - time_t shm_ctime; - pid_t shm_cpid; - pid_t shm_lpid; - unsigned long shm_nattch; - unsigned long __pad1; - unsigned long __pad2; -}; - -struct shminfo { - unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; -}; - -struct shm_info { - int __used_ids; - unsigned long shm_tot, shm_rss, shm_swp; - unsigned long __swap_attempts, __swap_successes; -}; diff --git a/lib/libc/musl/arch/riscv64/bits/signal.h b/lib/libc/musl/arch/riscv64/bits/signal.h index 4c94a8f02..2ff4be302 100644 --- a/lib/libc/musl/arch/riscv64/bits/signal.h +++ b/lib/libc/musl/arch/riscv64/bits/signal.h @@ -6,46 +6,43 @@ # define SIGSTKSZ 8192 #endif -/* gregs[0] holds the program counter. */ +typedef unsigned long __riscv_mc_gp_state[32]; + +struct __riscv_mc_f_ext_state { + unsigned int __f[32]; + unsigned int __fcsr; +}; + +struct __riscv_mc_d_ext_state { + unsigned long long __f[32]; + unsigned int __fcsr; +}; + +struct __riscv_mc_q_ext_state { + unsigned long long __f[64] __attribute__((aligned(16))); + unsigned int __fcsr; + unsigned int __reserved[3]; +}; + +union __riscv_mc_fp_state { + struct __riscv_mc_f_ext_state __f; + struct __riscv_mc_d_ext_state __d; + struct __riscv_mc_q_ext_state __q; +}; + +typedef struct mcontext_t { + __riscv_mc_gp_state __gregs; + union __riscv_mc_fp_state __fpregs; +} mcontext_t; #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) typedef unsigned long greg_t; typedef unsigned long gregset_t[32]; - -struct __riscv_f_ext_state { - unsigned int f[32]; - unsigned int fcsr; -}; - -struct __riscv_d_ext_state { - unsigned long long f[32]; - unsigned int fcsr; -}; - -struct __riscv_q_ext_state { - unsigned long long f[64] __attribute__((aligned(16))); - unsigned int fcsr; - unsigned int reserved[3]; -}; - -union __riscv_fp_state { - struct __riscv_f_ext_state f; - struct __riscv_d_ext_state d; - struct __riscv_q_ext_state q; -}; - -typedef union __riscv_fp_state fpregset_t; - -typedef struct sigcontext { +typedef union __riscv_mc_fp_state fpregset_t; +struct sigcontext { gregset_t gregs; fpregset_t fpregs; -} mcontext_t; - -#else -typedef struct { - unsigned long gregs[32]; - unsigned long long fpregs[66]; -} mcontext_t; +}; #endif struct sigaltstack { @@ -54,10 +51,10 @@ struct sigaltstack { size_t ss_size; }; -typedef struct __ucontext +typedef struct ucontext_t { unsigned long uc_flags; - struct __ucontext *uc_link; + struct ucontext_t *uc_link; stack_t uc_stack; sigset_t uc_sigmask; mcontext_t uc_mcontext; diff --git a/lib/libc/musl/arch/riscv64/bits/syscall.h.in b/lib/libc/musl/arch/riscv64/bits/syscall.h.in index 03d7f86da..1db70cfbf 100644 --- a/lib/libc/musl/arch/riscv64/bits/syscall.h.in +++ b/lib/libc/musl/arch/riscv64/bits/syscall.h.in @@ -273,5 +273,20 @@ #define __NR_pkey_mprotect 288 #define __NR_pkey_alloc 289 #define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_io_pgetevents 292 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 + #define __NR_sysriscv __NR_arch_specific_syscall #define __NR_riscv_flush_icache (__NR_sysriscv + 15) diff --git a/lib/libc/musl/arch/riscv64/bits/user.h b/lib/libc/musl/arch/riscv64/bits/user.h index bd0f0fc70..2da743eaf 100644 --- a/lib/libc/musl/arch/riscv64/bits/user.h +++ b/lib/libc/musl/arch/riscv64/bits/user.h @@ -1,43 +1,5 @@ -struct user_regs_struct { - unsigned long pc; - unsigned long ra; - unsigned long sp; - unsigned long gp; - unsigned long tp; - unsigned long t0; - unsigned long t1; - unsigned long t2; - unsigned long s0; - unsigned long s1; - unsigned long a0; - unsigned long a1; - unsigned long a2; - unsigned long a3; - unsigned long a4; - unsigned long a5; - unsigned long a6; - unsigned long a7; - unsigned long s2; - unsigned long s3; - unsigned long s4; - unsigned long s5; - unsigned long s6; - unsigned long s7; - unsigned long s8; - unsigned long s9; - unsigned long s10; - unsigned long s11; - unsigned long t3; - unsigned long t4; - unsigned long t5; - unsigned long t6; -}; - -struct user_fpregs_struct { - double f[32]; - unsigned int fcsr; -}; +#include #define ELF_NGREG 32 typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NGREG]; -typedef struct user_fpregs_struct elf_fpregset_t; +typedef union __riscv_mc_fp_state elf_fpregset_t; diff --git a/lib/libc/include/riscv64-linux-musl/bits/stat.h b/lib/libc/musl/arch/riscv64/kstat.h similarity index 61% rename from lib/libc/include/riscv64-linux-musl/bits/stat.h rename to lib/libc/musl/arch/riscv64/kstat.h index 6ce9af119..92625f363 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/stat.h +++ b/lib/libc/musl/arch/riscv64/kstat.h @@ -1,4 +1,4 @@ -struct stat { +struct kstat { dev_t st_dev; ino_t st_ino; mode_t st_mode; @@ -11,8 +11,11 @@ struct stat { blksize_t st_blksize; int __pad2; blkcnt_t st_blocks; - struct timespec st_atim; - struct timespec st_mtim; - struct timespec st_ctim; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; unsigned __unused[2]; -}; \ No newline at end of file +}; diff --git a/lib/libc/musl/arch/riscv64/pthread_arch.h b/lib/libc/musl/arch/riscv64/pthread_arch.h index 1268b72dc..db414b170 100644 --- a/lib/libc/musl/arch/riscv64/pthread_arch.h +++ b/lib/libc/musl/arch/riscv64/pthread_arch.h @@ -11,4 +11,4 @@ static inline struct pthread *__pthread_self() #define DTP_OFFSET 0x800 -#define MC_PC gregs[0] +#define MC_PC __gregs[0] diff --git a/lib/libc/musl/arch/riscv64/syscall_arch.h b/lib/libc/musl/arch/riscv64/syscall_arch.h index 3e0804efe..7fd042cd4 100644 --- a/lib/libc/musl/arch/riscv64/syscall_arch.h +++ b/lib/libc/musl/arch/riscv64/syscall_arch.h @@ -74,3 +74,5 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo /* We don't have a clock_gettime function. #define VDSO_CGT_SYM "__vdso_clock_gettime" #define VDSO_CGT_VER "LINUX_2.6" */ + +#define IPC_64 0 diff --git a/lib/libc/musl/arch/s390x/bits/ipc.h b/lib/libc/musl/arch/s390x/bits/ipc.h deleted file mode 100644 index 4710c12b1..000000000 --- a/lib/libc/musl/arch/s390x/bits/ipc.h +++ /dev/null @@ -1,14 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - unsigned short __pad1; - unsigned short __ipc_perm_seq; - unsigned long __pad2; - unsigned long __pad3; -}; - -#define IPC_64 0x100 diff --git a/lib/libc/musl/arch/s390x/bits/sem.h b/lib/libc/musl/arch/s390x/bits/sem.h deleted file mode 100644 index 644f68a04..000000000 --- a/lib/libc/musl/arch/s390x/bits/sem.h +++ /dev/null @@ -1,7 +0,0 @@ -struct semid_ds { - struct ipc_perm sem_perm; - time_t sem_otime; - time_t sem_ctime; - unsigned short __pad[3], sem_nsems; - unsigned long __unused[2]; -}; diff --git a/lib/libc/musl/arch/s390x/bits/syscall.h.in b/lib/libc/musl/arch/s390x/bits/syscall.h.in index 72fd2cce1..c4c70474e 100644 --- a/lib/libc/musl/arch/s390x/bits/syscall.h.in +++ b/lib/libc/musl/arch/s390x/bits/syscall.h.in @@ -344,4 +344,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/musl/arch/s390x/kstat.h b/lib/libc/musl/arch/s390x/kstat.h new file mode 100644 index 000000000..001c10be3 --- /dev/null +++ b/lib/libc/musl/arch/s390x/kstat.h @@ -0,0 +1,19 @@ +struct kstat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + blksize_t st_blksize; + blkcnt_t st_blocks; + unsigned long __unused[3]; +}; diff --git a/lib/libc/musl/arch/x86_64/bits/ipc.h b/lib/libc/musl/arch/x86_64/bits/ipc.h deleted file mode 100644 index 3d894e300..000000000 --- a/lib/libc/musl/arch/x86_64/bits/ipc.h +++ /dev/null @@ -1,13 +0,0 @@ -struct ipc_perm { - key_t __ipc_perm_key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - mode_t mode; - int __ipc_perm_seq; - long __pad1; - long __pad2; -}; - -#define IPC_64 0 diff --git a/lib/libc/musl/arch/x86_64/bits/sem.h b/lib/libc/musl/arch/x86_64/bits/sem.h new file mode 100644 index 000000000..e61571c12 --- /dev/null +++ b/lib/libc/musl/arch/x86_64/bits/sem.h @@ -0,0 +1,11 @@ +struct semid_ds { + struct ipc_perm sem_perm; + time_t sem_otime; + long __unused1; + time_t sem_ctime; + long __unused2; + unsigned short sem_nsems; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; + long __unused3; + long __unused4; +}; diff --git a/lib/libc/musl/arch/x86_64/bits/shm.h b/lib/libc/musl/arch/x86_64/bits/shm.h deleted file mode 100644 index 6652d6592..000000000 --- a/lib/libc/musl/arch/x86_64/bits/shm.h +++ /dev/null @@ -1,25 +0,0 @@ -#define SHMLBA 4096 - -struct shmid_ds { - struct ipc_perm shm_perm; - size_t shm_segsz; - time_t shm_atime; - time_t shm_dtime; - time_t shm_ctime; - pid_t shm_cpid; - pid_t shm_lpid; - unsigned long shm_nattch; - unsigned long __pad1; - unsigned long __pad2; -}; - -struct shminfo { - unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; -}; - -struct shm_info { - int __used_ids; - unsigned long shm_tot, shm_rss, shm_swp; - unsigned long __swap_attempts, __swap_successes; -}; - diff --git a/lib/libc/musl/arch/x86_64/bits/syscall.h.in b/lib/libc/musl/arch/x86_64/bits/syscall.h.in index 49572ef2d..2d4634f68 100644 --- a/lib/libc/musl/arch/x86_64/bits/syscall.h.in +++ b/lib/libc/musl/arch/x86_64/bits/syscall.h.in @@ -337,4 +337,10 @@ #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 #define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 diff --git a/lib/libc/musl/arch/x86_64/kstat.h b/lib/libc/musl/arch/x86_64/kstat.h new file mode 100644 index 000000000..5976c04e1 --- /dev/null +++ b/lib/libc/musl/arch/x86_64/kstat.h @@ -0,0 +1,22 @@ +struct kstat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + long __unused[3]; +}; diff --git a/lib/libc/musl/arch/x86_64/syscall_arch.h b/lib/libc/musl/arch/x86_64/syscall_arch.h index 54e05ff65..92d5c1792 100644 --- a/lib/libc/musl/arch/x86_64/syscall_arch.h +++ b/lib/libc/musl/arch/x86_64/syscall_arch.h @@ -66,3 +66,5 @@ static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long #define VDSO_CGT_VER "LINUX_2.6" #define VDSO_GETCPU_SYM "__vdso_getcpu" #define VDSO_GETCPU_VER "LINUX_2.6" + +#define IPC_64 0 diff --git a/lib/libc/musl/include/fcntl.h b/lib/libc/musl/include/fcntl.h index af2934057..b664cdc44 100644 --- a/lib/libc/musl/include/fcntl.h +++ b/lib/libc/musl/include/fcntl.h @@ -100,6 +100,11 @@ int posix_fallocate(int, off_t, off_t); #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) #define AT_NO_AUTOMOUNT 0x800 #define AT_EMPTY_PATH 0x1000 +#define AT_STATX_SYNC_TYPE 0x6000 +#define AT_STATX_SYNC_AS_STAT 0x0000 +#define AT_STATX_FORCE_SYNC 0x2000 +#define AT_STATX_DONT_SYNC 0x4000 +#define AT_RECURSIVE 0x8000 #define FAPPEND O_APPEND #define FFSYNC O_SYNC diff --git a/lib/libc/musl/include/glob.h b/lib/libc/musl/include/glob.h index 76f6c1c68..4a562a206 100644 --- a/lib/libc/musl/include/glob.h +++ b/lib/libc/musl/include/glob.h @@ -31,6 +31,9 @@ void globfree(glob_t *); #define GLOB_NOESCAPE 0x40 #define GLOB_PERIOD 0x80 +#define GLOB_TILDE 0x1000 +#define GLOB_TILDE_CHECK 0x4000 + #define GLOB_NOSPACE 1 #define GLOB_ABORTED 2 #define GLOB_NOMATCH 3 diff --git a/lib/libc/musl/include/netinet/if_ether.h b/lib/libc/musl/include/netinet/if_ether.h index ecd6c73cb..8af47dbef 100644 --- a/lib/libc/musl/include/netinet/if_ether.h +++ b/lib/libc/musl/include/netinet/if_ether.h @@ -76,6 +76,7 @@ #define ETH_P_QINQ2 0x9200 #define ETH_P_QINQ3 0x9300 #define ETH_P_EDSA 0xDADA +#define ETH_P_DSA_8021Q 0xDADB #define ETH_P_IFE 0xED3E #define ETH_P_AF_IUCV 0xFBFB diff --git a/lib/libc/musl/include/sched.h b/lib/libc/musl/include/sched.h index 05d40b1e7..7e470d3a1 100644 --- a/lib/libc/musl/include/sched.h +++ b/lib/libc/musl/include/sched.h @@ -18,10 +18,12 @@ extern "C" { struct sched_param { int sched_priority; - int sched_ss_low_priority; - struct timespec sched_ss_repl_period; - struct timespec sched_ss_init_budget; - int sched_ss_max_repl; + int __reserved1; + struct { + time_t __reserved1; + long __reserved2; + } __reserved2[2]; + int __reserved3; }; int sched_get_priority_max(int); @@ -47,6 +49,7 @@ int sched_yield(void); #define CLONE_FS 0x00000200 #define CLONE_FILES 0x00000400 #define CLONE_SIGHAND 0x00000800 +#define CLONE_PIDFD 0x00001000 #define CLONE_PTRACE 0x00002000 #define CLONE_VFORK 0x00004000 #define CLONE_PARENT 0x00008000 diff --git a/lib/libc/musl/include/spawn.h b/lib/libc/musl/include/spawn.h index c9bd1939e..8eb73e003 100644 --- a/lib/libc/musl/include/spawn.h +++ b/lib/libc/musl/include/spawn.h @@ -71,6 +71,11 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict, int int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, int); int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int); +#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) +int posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *__restrict, const char *__restrict); +int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *, int); +#endif + #ifdef __cplusplus } #endif diff --git a/lib/libc/musl/include/stdlib.h b/lib/libc/musl/include/stdlib.h index 42ca83363..194c20339 100644 --- a/lib/libc/musl/include/stdlib.h +++ b/lib/libc/musl/include/stdlib.h @@ -152,6 +152,7 @@ int ptsname_r(int, char *, size_t); char *ecvt(double, int, int *, int *); char *fcvt(double, int, int *, int *); char *gcvt(double, int, char *); +char *secure_getenv(const char *); struct __locale_struct; float strtof_l(const char *__restrict, char **__restrict, struct __locale_struct *); double strtod_l(const char *__restrict, char **__restrict, struct __locale_struct *); diff --git a/lib/libc/musl/include/sys/ipc.h b/lib/libc/musl/include/sys/ipc.h index c5a39819c..9e366b7be 100644 --- a/lib/libc/musl/include/sys/ipc.h +++ b/lib/libc/musl/include/sys/ipc.h @@ -22,6 +22,7 @@ extern "C" { #endif #include +#include #define IPC_CREAT 01000 #define IPC_EXCL 02000 @@ -29,7 +30,6 @@ extern "C" { #define IPC_RMID 0 #define IPC_SET 1 -#define IPC_STAT 2 #define IPC_INFO 3 #define IPC_PRIVATE ((key_t) 0) diff --git a/lib/libc/musl/include/sys/msg.h b/lib/libc/musl/include/sys/msg.h index be6afc34f..db5c62a42 100644 --- a/lib/libc/musl/include/sys/msg.h +++ b/lib/libc/musl/include/sys/msg.h @@ -25,9 +25,9 @@ typedef unsigned long msglen_t; #define MSG_NOERROR 010000 #define MSG_EXCEPT 020000 -#define MSG_STAT 11 +#define MSG_STAT (11 | (IPC_STAT & 0x100)) #define MSG_INFO 12 -#define MSG_STAT_ANY 13 +#define MSG_STAT_ANY (13 | (IPC_STAT & 0x100)) struct msginfo { int msgpool, msgmap, msgmax, msgmnb, msgmni, msgssz, msgtql; diff --git a/lib/libc/musl/include/sys/sem.h b/lib/libc/musl/include/sys/sem.h index 61cdb83d3..410c87744 100644 --- a/lib/libc/musl/include/sys/sem.h +++ b/lib/libc/musl/include/sys/sem.h @@ -31,9 +31,9 @@ extern "C" { #define _SEM_SEMUN_UNDEFINED 1 -#define SEM_STAT 18 +#define SEM_STAT (18 | (IPC_STAT & 0x100)) #define SEM_INFO 19 -#define SEM_STAT_ANY 20 +#define SEM_STAT_ANY (20 | (IPC_STAT & 0x100)) struct seminfo { int semmap; diff --git a/lib/libc/musl/include/sys/shm.h b/lib/libc/musl/include/sys/shm.h index 8ef4e8f2f..fd708cab1 100644 --- a/lib/libc/musl/include/sys/shm.h +++ b/lib/libc/musl/include/sys/shm.h @@ -33,9 +33,9 @@ extern "C" { #define SHM_LOCK 11 #define SHM_UNLOCK 12 -#define SHM_STAT 13 +#define SHM_STAT (13 | (IPC_STAT & 0x100)) #define SHM_INFO 14 -#define SHM_STAT_ANY 15 +#define SHM_STAT_ANY (15 | (IPC_STAT & 0x100)) #define SHM_DEST 01000 #define SHM_LOCKED 02000 #define SHM_HUGETLB 04000 diff --git a/lib/libc/musl/include/unistd.h b/lib/libc/musl/include/unistd.h index 9485da7a6..7bcbff943 100644 --- a/lib/libc/musl/include/unistd.h +++ b/lib/libc/musl/include/unistd.h @@ -176,6 +176,7 @@ long syscall(long, ...); int execvpe(const char *, char *const [], char *const []); int issetugid(void); int getentropy(void *, size_t); +extern int optreset; #endif #ifdef _GNU_SOURCE @@ -188,6 +189,7 @@ char *get_current_dir_name(void); int syncfs(int); int euidaccess(const char *, int); int eaccess(const char *, int); +ssize_t copy_file_range(int, off_t *, int, off_t *, size_t, unsigned); #endif #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) diff --git a/lib/libc/musl/src/env/secure_getenv.c b/lib/libc/musl/src/env/secure_getenv.c new file mode 100644 index 000000000..72322f811 --- /dev/null +++ b/lib/libc/musl/src/env/secure_getenv.c @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include +#include "libc.h" + +char *secure_getenv(const char *name) +{ + return libc.secure ? NULL : getenv(name); +} diff --git a/lib/libc/musl/src/include/time.h b/lib/libc/musl/src/include/time.h index 24c87973a..cbabde476 100644 --- a/lib/libc/musl/src/include/time.h +++ b/lib/libc/musl/src/include/time.h @@ -4,6 +4,7 @@ #include "../../include/time.h" hidden int __clock_gettime(clockid_t, struct timespec *); +hidden int __clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); hidden char *__asctime_r(const struct tm *, char *); hidden struct tm *__gmtime_r(const time_t *restrict, struct tm *restrict); diff --git a/lib/libc/musl/src/include/unistd.h b/lib/libc/musl/src/include/unistd.h index 6deb1bcc1..1b4605c7c 100644 --- a/lib/libc/musl/src/include/unistd.h +++ b/lib/libc/musl/src/include/unistd.h @@ -9,5 +9,6 @@ hidden int __dup3(int, int, int); hidden int __mkostemps(char *, int, int); hidden int __execvpe(const char *, char *const *, char *const *); hidden int __aio_close(int); +hidden off_t __lseek(int, off_t, int); #endif diff --git a/lib/libc/musl/src/internal/dynlink.h b/lib/libc/musl/src/internal/dynlink.h index 165bbedbb..ffd06b047 100644 --- a/lib/libc/musl/src/internal/dynlink.h +++ b/lib/libc/musl/src/internal/dynlink.h @@ -28,6 +28,7 @@ typedef Elf64_Sym Sym; enum { REL_NONE = 0, REL_SYMBOLIC = -100, + REL_USYMBOLIC, REL_GOT, REL_PLT, REL_RELATIVE, diff --git a/lib/libc/musl/src/internal/pthread_impl.h b/lib/libc/musl/src/internal/pthread_impl.h index 9b0014212..5742dfc55 100644 --- a/lib/libc/musl/src/internal/pthread_impl.h +++ b/lib/libc/musl/src/internal/pthread_impl.h @@ -125,7 +125,6 @@ struct __timer { 0x80000000 }) void *__tls_get_addr(tls_mod_off_t *); -hidden void *__tls_get_new(tls_mod_off_t *); hidden int __init_tp(void *); hidden void *__copy_tls(unsigned char *); hidden void __reset_tls(); diff --git a/lib/libc/musl/src/internal/syscall.h b/lib/libc/musl/src/internal/syscall.h index 69f019cd8..9f2784dba 100644 --- a/lib/libc/musl/src/internal/syscall.h +++ b/lib/libc/musl/src/internal/syscall.h @@ -43,8 +43,8 @@ hidden long __syscall_ret(unsigned long), #define __syscall(...) __SYSCALL_DISP(__syscall,__VA_ARGS__) #define syscall(...) __syscall_ret(__syscall(__VA_ARGS__)) -#define socketcall __socketcall -#define socketcall_cp __socketcall_cp +#define socketcall(nm,a,b,c,d,e,f) __syscall_ret(__socketcall(nm,a,b,c,d,e,f)) +#define socketcall_cp(nm,a,b,c,d,e,f) __syscall_ret(__socketcall_cp(nm,a,b,c,d,e,f)) #define __syscall_cp0(n) (__syscall_cp)(n,0,0,0,0,0,0) #define __syscall_cp1(n,a) (__syscall_cp)(n,__scc(a),0,0,0,0,0) @@ -58,12 +58,12 @@ hidden long __syscall_ret(unsigned long), #define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__)) #ifndef SYSCALL_USE_SOCKETCALL -#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_##nm, a, b, c, d, e, f) -#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_##nm, a, b, c, d, e, f) +#define __socketcall(nm,a,b,c,d,e,f) __syscall(SYS_##nm, a, b, c, d, e, f) +#define __socketcall_cp(nm,a,b,c,d,e,f) __syscall_cp(SYS_##nm, a, b, c, d, e, f) #else -#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_socketcall, __SC_##nm, \ +#define __socketcall(nm,a,b,c,d,e,f) __syscall(SYS_socketcall, __SC_##nm, \ ((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f })) -#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_socketcall, __SC_##nm, \ +#define __socketcall_cp(nm,a,b,c,d,e,f) __syscall_cp(SYS_socketcall, __SC_##nm, \ ((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f })) #endif @@ -193,6 +193,89 @@ hidden long __syscall_ret(unsigned long), #define SYS_sendfile SYS_sendfile64 #endif + +/* Ensure that the plain syscall names are defined even for "time64-only" + * archs. These facilitate callers passing null time arguments, and make + * tests for establishing which to use/fallback-to more consistent when + * they do need to be called with time arguments. */ + +#ifndef SYS_clock_gettime +#define SYS_clock_gettime SYS_clock_gettime64 +#endif + +#ifndef SYS_clock_settime +#define SYS_clock_settime SYS_clock_settime64 +#endif + +#ifndef SYS_clock_adjtime +#define SYS_clock_adjtime SYS_clock_adjtime64 +#endif + +#ifndef SYS_clock_getres +#define SYS_clock_getres SYS_clock_getres_time64 +#endif + +#ifndef SYS_clock_nanosleep +#define SYS_clock_nanosleep SYS_clock_nanosleep_time64 +#endif + +#ifndef SYS_timer_gettime +#define SYS_timer_gettime SYS_timer_gettime64 +#endif + +#ifndef SYS_timer_settime +#define SYS_timer_settime SYS_timer_settime64 +#endif + +#ifndef SYS_timerfd_gettime +#define SYS_timerfd_gettime SYS_timerfd_gettime64 +#endif + +#ifndef SYS_timerfd_settime +#define SYS_timerfd_settime SYS_timerfd_settime64 +#endif + +#ifndef SYS_utimensat +#define SYS_utimensat SYS_utimensat_time64 +#endif + +#ifndef SYS_pselect6 +#define SYS_pselect6 SYS_pselect6_time64 +#endif + +#ifndef SYS_ppoll +#define SYS_ppoll SYS_ppoll_time64 +#endif + +#ifndef SYS_recvmmsg +#define SYS_recvmmsg SYS_recvmmsg_time64 +#endif + +#ifndef SYS_mq_timedsend +#define SYS_mq_timedsend SYS_mq_timedsend_time64 +#endif + +#ifndef SYS_mq_timedreceive +#define SYS_mq_timedreceive SYS_mq_timedreceive_time64 +#endif + +/* SYS_semtimedop omitted because SYS_ipc may provide it */ + +#ifndef SYS_rt_sigtimedwait +#define SYS_rt_sigtimedwait SYS_rt_sigtimedwait_time64 +#endif + +#ifndef SYS_futex +#define SYS_futex SYS_futex_time64 +#endif + +#ifndef SYS_sched_rr_get_interval +#define SYS_sched_rr_get_interval SYS_sched_rr_get_interval_time64 +#endif + + + + /* socketcall calls */ #define __SC_socket 1 @@ -216,6 +299,20 @@ hidden long __syscall_ret(unsigned long), #define __SC_recvmmsg 19 #define __SC_sendmmsg 20 +#ifndef SO_RCVTIMEO_OLD +#define SO_RCVTIMEO_OLD 20 +#endif +#ifndef SO_SNDTIMEO_OLD +#define SO_SNDTIMEO_OLD 21 +#endif + +#ifndef SIOCGSTAMP_OLD +#define SIOCGSTAMP_OLD 0x8906 +#endif +#ifndef SIOCGSTAMPNS_OLD +#define SIOCGSTAMPNS_OLD 0x8907 +#endif + #ifdef SYS_open #define __sys_open2(x,pn,fl) __syscall2(SYS_open, pn, (fl)|O_LARGEFILE) #define __sys_open3(x,pn,fl,mo) __syscall3(SYS_open, pn, (fl)|O_LARGEFILE, mo) diff --git a/lib/libc/musl/src/internal/version.h b/lib/libc/musl/src/internal/version.h index 70c5ae1e3..56294c973 100644 --- a/lib/libc/musl/src/internal/version.h +++ b/lib/libc/musl/src/internal/version.h @@ -1 +1 @@ -#define VERSION "1.1.23" +#define VERSION "1.1.24" diff --git a/lib/libc/musl/src/ipc/ipc.h b/lib/libc/musl/src/ipc/ipc.h index 30ab939ad..746a905c9 100644 --- a/lib/libc/musl/src/ipc/ipc.h +++ b/lib/libc/musl/src/ipc/ipc.h @@ -1,3 +1,5 @@ +#include "syscall.h" + #define IPCOP_semop 1 #define IPCOP_semget 2 #define IPCOP_semctl 3 @@ -10,3 +12,13 @@ #define IPCOP_shmdt 22 #define IPCOP_shmget 23 #define IPCOP_shmctl 24 + +#ifndef IPC_64 +#define IPC_64 0x100 +#endif + +#define IPC_TIME64 (IPC_STAT & 0x100) + +#define IPC_CMD(cmd) (((cmd) & ~IPC_TIME64) | IPC_64) + +#define IPC_HILO(b,t) ((b)->t = (b)->__##t##_lo | 0LL+(b)->__##t##_hi<<32) diff --git a/lib/libc/musl/src/ipc/msgctl.c b/lib/libc/musl/src/ipc/msgctl.c index 868197f61..b043041a5 100644 --- a/lib/libc/musl/src/ipc/msgctl.c +++ b/lib/libc/musl/src/ipc/msgctl.c @@ -18,17 +18,24 @@ int msgctl(int q, int cmd, struct msqid_ds *buf) } #endif #ifndef SYS_ipc - int r = __syscall(SYS_msgctl, q, cmd | IPC_64, buf); + int r = __syscall(SYS_msgctl, q, IPC_CMD(cmd), buf); #else - int r = __syscall(SYS_ipc, IPCOP_msgctl, q, cmd | IPC_64, 0, buf, 0); + int r = __syscall(SYS_ipc, IPCOP_msgctl, q, IPC_CMD(cmd), 0, buf, 0); #endif #ifdef SYSCALL_IPC_BROKEN_MODE - if (r >= 0) switch (cmd) { + if (r >= 0) switch (cmd | IPC_TIME64) { case IPC_STAT: case MSG_STAT: case MSG_STAT_ANY: buf->msg_perm.mode >>= 16; } +#endif +#if IPC_TIME64 + if (r >= 0 && (cmd&IPC_TIME64)) { + IPC_HILO(buf, msg_stime); + IPC_HILO(buf, msg_rtime); + IPC_HILO(buf, msg_ctime); + } #endif return __syscall_ret(r); } diff --git a/lib/libc/musl/src/ipc/semctl.c b/lib/libc/musl/src/ipc/semctl.c index ce1fb164f..ed9827477 100644 --- a/lib/libc/musl/src/ipc/semctl.c +++ b/lib/libc/musl/src/ipc/semctl.c @@ -18,9 +18,12 @@ int semctl(int id, int num, int cmd, ...) { union semun arg = {0}; va_list ap; - switch (cmd) { - case SETVAL: case GETALL: case SETALL: case IPC_STAT: case IPC_SET: - case IPC_INFO: case SEM_INFO: case SEM_STAT: + switch (cmd & ~IPC_TIME64) { + case SETVAL: case GETALL: case SETALL: case IPC_SET: + case IPC_INFO: case SEM_INFO: + case IPC_STAT & ~IPC_TIME64: + case SEM_STAT & ~IPC_TIME64: + case SEM_STAT_ANY & ~IPC_TIME64: va_start(ap, cmd); arg = va_arg(ap, union semun); va_end(ap); @@ -34,17 +37,23 @@ int semctl(int id, int num, int cmd, ...) } #endif #ifndef SYS_ipc - int r = __syscall(SYS_semctl, id, num, cmd | IPC_64, arg.buf); + int r = __syscall(SYS_semctl, id, num, IPC_CMD(cmd), arg.buf); #else - int r = __syscall(SYS_ipc, IPCOP_semctl, id, num, cmd | IPC_64, &arg.buf); + int r = __syscall(SYS_ipc, IPCOP_semctl, id, num, IPC_CMD(cmd), &arg.buf); #endif #ifdef SYSCALL_IPC_BROKEN_MODE - if (r >= 0) switch (cmd) { + if (r >= 0) switch (cmd | IPC_TIME64) { case IPC_STAT: case SEM_STAT: case SEM_STAT_ANY: arg.buf->sem_perm.mode >>= 16; } +#endif +#if IPC_TIME64 + if (r >= 0 && (cmd&IPC_TIME64)) { + IPC_HILO(arg.buf, sem_otime); + IPC_HILO(arg.buf, sem_ctime); + } #endif return __syscall_ret(r); } diff --git a/lib/libc/musl/src/ipc/semtimedop.c b/lib/libc/musl/src/ipc/semtimedop.c index 51e708053..1632e7b03 100644 --- a/lib/libc/musl/src/ipc/semtimedop.c +++ b/lib/libc/musl/src/ipc/semtimedop.c @@ -1,13 +1,35 @@ #define _GNU_SOURCE #include +#include #include "syscall.h" #include "ipc.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +#if !defined(SYS_semtimedop) && !defined(SYS_ipc) +#define NO_TIME32 1 +#else +#define NO_TIME32 0 +#endif + int semtimedop(int id, struct sembuf *buf, size_t n, const struct timespec *ts) { -#ifndef SYS_ipc +#ifdef SYS_semtimedop_time64 + time_t s = ts ? ts->tv_sec : 0; + long ns = ts ? ts->tv_nsec : 0; + int r = -ENOSYS; + if (NO_TIME32 || !IS32BIT(s)) + r = __syscall(SYS_semtimedop_time64, id, buf, n, + ts ? ((long long[]){s, ns}) : 0); + if (NO_TIME32 || r!=-ENOSYS) return __syscall_ret(r); + ts = ts ? (void *)(long[]){CLAMP(s), ns} : 0; +#endif +#if defined(SYS_ipc) + return syscall(SYS_ipc, IPCOP_semtimedop, id, n, 0, buf, ts); +#elif defined(SYS_semtimedop) return syscall(SYS_semtimedop, id, buf, n, ts); #else - return syscall(SYS_ipc, IPCOP_semtimedop, id, n, 0, buf, ts); + return __syscall_ret(-ENOSYS); #endif } diff --git a/lib/libc/musl/src/ipc/shmctl.c b/lib/libc/musl/src/ipc/shmctl.c index c2b2bb0d4..de3ce9d4d 100644 --- a/lib/libc/musl/src/ipc/shmctl.c +++ b/lib/libc/musl/src/ipc/shmctl.c @@ -18,17 +18,24 @@ int shmctl(int id, int cmd, struct shmid_ds *buf) } #endif #ifndef SYS_ipc - int r = __syscall(SYS_shmctl, id, cmd | IPC_64, buf); + int r = __syscall(SYS_shmctl, id, IPC_CMD(cmd), buf); #else - int r = __syscall(SYS_ipc, IPCOP_shmctl, id, cmd | IPC_64, 0, buf, 0); + int r = __syscall(SYS_ipc, IPCOP_shmctl, id, IPC_CMD(cmd), 0, buf, 0); #endif #ifdef SYSCALL_IPC_BROKEN_MODE - if (r >= 0) switch (cmd) { + if (r >= 0) switch (cmd | IPC_TIME64) { case IPC_STAT: case SHM_STAT: case SHM_STAT_ANY: buf->shm_perm.mode >>= 16; } +#endif +#if IPC_TIME64 + if (r >= 0 && (cmd&IPC_TIME64)) { + IPC_HILO(buf, shm_atime); + IPC_HILO(buf, shm_dtime); + IPC_HILO(buf, shm_ctime); + } #endif return __syscall_ret(r); } diff --git a/lib/libc/musl/src/ldso/aarch64/tlsdesc.s b/lib/libc/musl/src/ldso/aarch64/tlsdesc.s index 04d97e730..c6c685b3d 100644 --- a/lib/libc/musl/src/ldso/aarch64/tlsdesc.s +++ b/lib/libc/musl/src/ldso/aarch64/tlsdesc.s @@ -9,15 +9,11 @@ __tlsdesc_static: ldr x0,[x0,#8] ret -.hidden __tls_get_new - // size_t __tlsdesc_dynamic(size_t *a) // { // struct {size_t modidx,off;} *p = (void*)a[1]; // size_t *dtv = *(size_t**)(tp - 8); -// if (p->modidx <= dtv[0]) -// return dtv[p->modidx] + p->off - tp; -// return __tls_get_new(p) - tp; +// return dtv[p->modidx] + p->off - tp; // } .global __tlsdesc_dynamic .hidden __tlsdesc_dynamic diff --git a/lib/libc/musl/src/ldso/arm/tlsdesc.S b/lib/libc/musl/src/ldso/arm/tlsdesc.S index 455eac1d5..3ae133c96 100644 --- a/lib/libc/musl/src/ldso/arm/tlsdesc.S +++ b/lib/libc/musl/src/ldso/arm/tlsdesc.S @@ -8,8 +8,6 @@ __tlsdesc_static: ldr r0,[r0] bx lr -.hidden __tls_get_new - .global __tlsdesc_dynamic .hidden __tlsdesc_dynamic .type __tlsdesc_dynamic,%function @@ -29,8 +27,12 @@ __tlsdesc_dynamic: 2: #if __ARM_ARCH >= 5 blx r0 // r0 = tp +#else +#if __thumb__ + add lr,pc,#1 #else mov lr,pc +#endif bx r0 #endif #endif diff --git a/lib/libc/musl/src/ldso/i386/tlsdesc.s b/lib/libc/musl/src/ldso/i386/tlsdesc.s index a5c0100c3..32c817669 100644 --- a/lib/libc/musl/src/ldso/i386/tlsdesc.s +++ b/lib/libc/musl/src/ldso/i386/tlsdesc.s @@ -6,8 +6,6 @@ __tlsdesc_static: mov 4(%eax),%eax ret -.hidden __tls_get_new - .global __tlsdesc_dynamic .hidden __tlsdesc_dynamic .type __tlsdesc_dynamic,@function diff --git a/lib/libc/musl/src/ldso/x86_64/tlsdesc.s b/lib/libc/musl/src/ldso/x86_64/tlsdesc.s index 0151d15c9..e08f1d7df 100644 --- a/lib/libc/musl/src/ldso/x86_64/tlsdesc.s +++ b/lib/libc/musl/src/ldso/x86_64/tlsdesc.s @@ -6,8 +6,6 @@ __tlsdesc_static: mov 8(%rax),%rax ret -.hidden __tls_get_new - .global __tlsdesc_dynamic .hidden __tlsdesc_dynamic .type __tlsdesc_dynamic,@function diff --git a/lib/libc/musl/src/linux/adjtime.c b/lib/libc/musl/src/linux/adjtime.c index fa8af9f02..5a707f2f2 100644 --- a/lib/libc/musl/src/linux/adjtime.c +++ b/lib/libc/musl/src/linux/adjtime.c @@ -15,7 +15,7 @@ int adjtime(const struct timeval *in, struct timeval *out) tx.offset = in->tv_sec*1000000 + in->tv_usec; tx.modes = ADJ_OFFSET_SINGLESHOT; } - if (syscall(SYS_adjtimex, &tx) < 0) return -1; + if (adjtimex(&tx) < 0) return -1; if (out) { out->tv_sec = tx.offset / 1000000; if ((out->tv_usec = tx.offset % 1000000) < 0) { diff --git a/lib/libc/musl/src/linux/adjtimex.c b/lib/libc/musl/src/linux/adjtimex.c index 91de6824c..e9d727cf3 100644 --- a/lib/libc/musl/src/linux/adjtimex.c +++ b/lib/libc/musl/src/linux/adjtimex.c @@ -1,7 +1,7 @@ #include -#include "syscall.h" +#include int adjtimex(struct timex *tx) { - return syscall(SYS_adjtimex, tx); + return clock_adjtime(CLOCK_REALTIME, tx); } diff --git a/lib/libc/musl/src/linux/clock_adjtime.c b/lib/libc/musl/src/linux/clock_adjtime.c index 056ad6d34..2f531397c 100644 --- a/lib/libc/musl/src/linux/clock_adjtime.c +++ b/lib/libc/musl/src/linux/clock_adjtime.c @@ -1,7 +1,119 @@ #include +#include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + +struct ktimex64 { + unsigned modes; + int :32; + long long offset, freq, maxerror, esterror; + int status; + int :32; + long long constant, precision, tolerance; + long long time_sec, time_usec; + long long tick, ppsfreq, jitter; + int shift; + int :32; + long long stabil, jitcnt, calcnt, errcnt, stbcnt; + int tai; + int __padding[11]; +}; + +struct ktimex { + unsigned modes; + long offset, freq, maxerror, esterror; + int status; + long constant, precision, tolerance; + long time_sec, time_usec; + long tick, ppsfreq, jitter; + int shift; + long stabil, jitcnt, calcnt, errcnt, stbcnt; + int tai; + int __padding[11]; +}; + int clock_adjtime (clockid_t clock_id, struct timex *utx) { + int r = -ENOSYS; +#ifdef SYS_clock_adjtime64 + if (SYS_clock_adjtime == SYS_clock_adjtime64 || + (utx->modes & ADJ_SETOFFSET) && !IS32BIT(utx->time.tv_sec)) { + struct ktimex64 ktx = { + .modes = utx->modes, + .offset = utx->offset, + .freq = utx->freq, + .maxerror = utx->maxerror, + .esterror = utx->esterror, + .status = utx->status, + .constant = utx->constant, + .precision = utx->precision, + .tolerance = utx->tolerance, + .time_sec = utx->time.tv_sec, + .time_usec = utx->time.tv_usec, + .tick = utx->tick, + .ppsfreq = utx->ppsfreq, + .jitter = utx->jitter, + .shift = utx->shift, + .stabil = utx->stabil, + .jitcnt = utx->jitcnt, + .calcnt = utx->calcnt, + .errcnt = utx->errcnt, + .stbcnt = utx->stbcnt, + .tai = utx->tai, + }; + r = __syscall(SYS_clock_adjtime, clock_id, &ktx); + if (r>=0) { + utx->modes = ktx.modes; + utx->offset = ktx.offset; + utx->freq = ktx.freq; + utx->maxerror = ktx.maxerror; + utx->esterror = ktx.esterror; + utx->status = ktx.status; + utx->constant = ktx.constant; + utx->precision = ktx.precision; + utx->tolerance = ktx.tolerance; + utx->time.tv_sec = ktx.time_sec; + utx->time.tv_usec = ktx.time_usec; + utx->tick = ktx.tick; + utx->ppsfreq = ktx.ppsfreq; + utx->jitter = ktx.jitter; + utx->shift = ktx.shift; + utx->stabil = ktx.stabil; + utx->jitcnt = ktx.jitcnt; + utx->calcnt = ktx.calcnt; + utx->errcnt = ktx.errcnt; + utx->stbcnt = ktx.stbcnt; + utx->tai = ktx.tai; + } + } + if (SYS_clock_adjtime == SYS_clock_adjtime64 || r!=-ENOSYS) + return __syscall_ret(r); + if ((utx->modes & ADJ_SETOFFSET) && !IS32BIT(utx->time.tv_sec)) + return __syscall_ret(-ENOTSUP); +#endif + if (sizeof(time_t) > sizeof(long)) { + union { + struct timex utx; + struct ktimex ktx; + } u = { *utx }; + u.ktx.time_sec = utx->time.tv_sec; + u.ktx.time_usec = utx->time.tv_usec; +#ifdef SYS_adjtimex + if (clock_id==CLOCK_REALTIME) r = __syscall(SYS_adjtimex, &u); + else +#endif + r = __syscall(SYS_clock_adjtime, clock_id, &u); + if (r>=0) { + *utx = u.utx; + utx->time.tv_sec = u.ktx.time_sec; + utx->time.tv_usec = u.ktx.time_usec; + } + return __syscall_ret(r); + } +#ifdef SYS_adjtimex + if (clock_id==CLOCK_REALTIME) return syscall(SYS_adjtimex, utx); +#endif return syscall(SYS_clock_adjtime, clock_id, utx); } diff --git a/lib/libc/musl/src/linux/copy_file_range.c b/lib/libc/musl/src/linux/copy_file_range.c new file mode 100644 index 000000000..dd4b13333 --- /dev/null +++ b/lib/libc/musl/src/linux/copy_file_range.c @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include +#include "syscall.h" + +ssize_t copy_file_range(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned flags) +{ + return syscall(SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +} diff --git a/lib/libc/musl/src/linux/ppoll.c b/lib/libc/musl/src/linux/ppoll.c index 9e262477b..e614600ab 100644 --- a/lib/libc/musl/src/linux/ppoll.c +++ b/lib/libc/musl/src/linux/ppoll.c @@ -1,10 +1,26 @@ #define _GNU_SOURCE #include #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int ppoll(struct pollfd *fds, nfds_t n, const struct timespec *to, const sigset_t *mask) { + time_t s = to ? to->tv_sec : 0; + long ns = to ? to->tv_nsec : 0; +#ifdef SYS_ppoll_time64 + int r = -ENOSYS; + if (SYS_ppoll == SYS_ppoll_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_ppoll_time64, fds, n, + to ? ((long long[]){s, ns}) : 0, + mask, _NSIG/8); + if (SYS_ppoll == SYS_ppoll_time64 || r != -ENOSYS) + return __syscall_ret(r); + s = CLAMP(s); +#endif return syscall_cp(SYS_ppoll, fds, n, - to ? (struct timespec []){*to} : 0, mask, _NSIG/8); + to ? ((long[]){s, ns}) : 0, mask, _NSIG/8); } diff --git a/lib/libc/musl/src/linux/settimeofday.c b/lib/libc/musl/src/linux/settimeofday.c index 15c18c637..860fb5de9 100644 --- a/lib/libc/musl/src/linux/settimeofday.c +++ b/lib/libc/musl/src/linux/settimeofday.c @@ -1,8 +1,13 @@ #define _BSD_SOURCE #include +#include +#include #include "syscall.h" int settimeofday(const struct timeval *tv, const struct timezone *tz) { - return syscall(SYS_settimeofday, tv, 0); + if (!tv) return 0; + if (tv->tv_usec >= 1000000ULL) return __syscall_ret(-EINVAL); + return clock_settime(CLOCK_REALTIME, &((struct timespec){ + .tv_sec = tv->tv_sec, .tv_nsec = tv->tv_usec * 1000})); } diff --git a/lib/libc/musl/src/linux/timerfd.c b/lib/libc/musl/src/linux/timerfd.c index 62cc27736..5bdfaf165 100644 --- a/lib/libc/musl/src/linux/timerfd.c +++ b/lib/libc/musl/src/linux/timerfd.c @@ -1,6 +1,9 @@ #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + int timerfd_create(int clockid, int flags) { return syscall(SYS_timerfd_create, clockid, flags); @@ -8,10 +11,49 @@ int timerfd_create(int clockid, int flags) int timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old) { +#ifdef SYS_timerfd_settime64 + time_t is = new->it_interval.tv_sec, vs = new->it_value.tv_sec; + long ins = new->it_interval.tv_nsec, vns = new->it_value.tv_nsec; + int r = -ENOSYS; + if (SYS_timerfd_settime == SYS_timerfd_settime64 + || !IS32BIT(is) || !IS32BIT(vs) || (sizeof(time_t)>4 && old)) + r = __syscall(SYS_timerfd_settime64, fd, flags, + ((long long[]){is, ins, vs, vns}), old); + if (SYS_timerfd_settime == SYS_timerfd_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(is) || !IS32BIT(vs)) + return __syscall_ret(-ENOTSUP); + long old32[4]; + r = __syscall(SYS_timerfd_settime, fd, flags, + ((long[]){is, ins, vs, vns}), old32); + if (!r && old) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_nsec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_nsec = old32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timerfd_settime, fd, flags, new, old); } int timerfd_gettime(int fd, struct itimerspec *cur) { +#ifdef SYS_timerfd_gettime64 + int r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_timerfd_gettime64, fd, cur); + if (SYS_timerfd_gettime == SYS_timerfd_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long cur32[4]; + r = __syscall(SYS_timerfd_gettime, fd, cur32); + if (!r) { + cur->it_interval.tv_sec = cur32[0]; + cur->it_interval.tv_nsec = cur32[1]; + cur->it_value.tv_sec = cur32[2]; + cur->it_value.tv_nsec = cur32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timerfd_gettime, fd, cur); } diff --git a/lib/libc/musl/src/locale/catclose.c b/lib/libc/musl/src/locale/catclose.c index 02cd3e5c9..54e24dd21 100644 --- a/lib/libc/musl/src/locale/catclose.c +++ b/lib/libc/musl/src/locale/catclose.c @@ -1,6 +1,14 @@ +#define _BSD_SOURCE #include +#include +#include +#include + +#define V(p) be32toh(*(uint32_t *)(p)) int catclose (nl_catd catd) { + char *map = (char *)catd; + munmap(map, V(map+8)+20); return 0; } diff --git a/lib/libc/musl/src/locale/catgets.c b/lib/libc/musl/src/locale/catgets.c index bbee8986f..71c31c1d6 100644 --- a/lib/libc/musl/src/locale/catgets.c +++ b/lib/libc/musl/src/locale/catgets.c @@ -1,6 +1,38 @@ +#define _BSD_SOURCE #include +#include +#include +#include +#include + +#define V(p) be32toh(*(uint32_t *)(p)) + +static int cmp(const void *a, const void *b) +{ + uint32_t x = V(a), y = V(b); + return xy ? 1 : 0; +} char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) { - return (char *)s; + const char *map = (const char *)catd; + uint32_t nsets = V(map+4); + const char *sets = map+20; + const char *msgs = map+20+V(map+12); + const char *strings = map+20+V(map+16); + uint32_t set_id_be = htobe32(set_id); + uint32_t msg_id_be = htobe32(msg_id); + const char *set = bsearch(&set_id_be, sets, nsets, 12, cmp); + if (!set) { + errno = ENOMSG; + return (char *)s; + } + uint32_t nmsgs = V(set+4); + msgs += 12*V(set+8); + const char *msg = bsearch(&msg_id_be, msgs, nmsgs, 12, cmp); + if (!msg) { + errno = ENOMSG; + return (char *)s; + } + return (char *)(strings + V(msg+8)); } diff --git a/lib/libc/musl/src/locale/catopen.c b/lib/libc/musl/src/locale/catopen.c index 3fbc77178..97f2446d3 100644 --- a/lib/libc/musl/src/locale/catopen.c +++ b/lib/libc/musl/src/locale/catopen.c @@ -1,8 +1,79 @@ +#define _BSD_SOURCE #include +#include +#include +#include #include +#include +#include +#include +#include "libc.h" -nl_catd catopen (const char *name, int oflag) +#define V(p) be32toh(*(uint32_t *)(p)) + +static nl_catd do_catopen(const char *name) { - errno = EOPNOTSUPP; + size_t size; + const unsigned char *map = __map_file(name, &size); + /* Size recorded in the file must match file size; otherwise + * the information needed to unmap the file will be lost. */ + if (!map || V(map) != 0xff88ff89 || 20+V(map+8) != size) { + if(map) munmap((void *)map, size); + errno = ENOENT; + return (nl_catd)-1; + } + return (nl_catd)map; +} + +nl_catd catopen(const char *name, int oflag) +{ + nl_catd catd; + + if (strchr(name, '/')) return do_catopen(name); + + char buf[PATH_MAX]; + size_t i; + const char *path, *lang, *p, *z; + if (libc.secure || !(path = getenv("NLSPATH"))) { + errno = ENOENT; + return (nl_catd)-1; + } + lang = oflag ? nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES)) : getenv("LANG"); + if (!lang) lang = ""; + for (p=path; *p; p=z) { + i = 0; + z = __strchrnul(p, ':'); + for (; p= sizeof buf - i) { + break; + } + memcpy(buf+i, v, l); + i += l; + } + if (!*z && (p #include +#include #include "libm.h" /* @@ -26,7 +27,18 @@ as a double. */ #if LONG_MAX < 1U<<53 && defined(FE_INEXACT) -long lrint(double x) +#include +#include +#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 +#define EPS DBL_EPSILON +#elif FLT_EVAL_METHOD==2 +#define EPS LDBL_EPSILON +#endif +#ifdef __GNUC__ +/* avoid stack frame in lrint */ +__attribute__((noinline)) +#endif +static long lrint_slow(double x) { #pragma STDC FENV_ACCESS ON int e; @@ -38,6 +50,20 @@ long lrint(double x) /* conversion */ return x; } + +long lrint(double x) +{ + uint32_t abstop = asuint64(x)>>32 & 0x7fffffff; + uint64_t sign = asuint64(x) & (1ULL << 63); + + if (abstop < 0x41dfffff) { + /* |x| < 0x7ffffc00, no overflow */ + double_t toint = asdouble(asuint64(1/EPS) | sign); + double_t y = x + toint - toint; + return (long)y; + } + return lrint_slow(x); +} #else long lrint(double x) { diff --git a/lib/libc/musl/src/math/sqrt.c b/lib/libc/musl/src/math/sqrt.c index b27756738..f1f6d76c7 100644 --- a/lib/libc/musl/src/math/sqrt.c +++ b/lib/libc/musl/src/math/sqrt.c @@ -179,7 +179,6 @@ double sqrt(double x) ix1 = q1>>1; if (q&1) ix1 |= sign; - ix0 += m << 20; - INSERT_WORDS(z, ix0, ix1); + INSERT_WORDS(z, ix0 + ((uint32_t)m << 20), ix1); return z; } diff --git a/lib/libc/musl/src/math/sqrtf.c b/lib/libc/musl/src/math/sqrtf.c index 28cb4ad37..d6ace38aa 100644 --- a/lib/libc/musl/src/math/sqrtf.c +++ b/lib/libc/musl/src/math/sqrtf.c @@ -78,7 +78,6 @@ float sqrtf(float x) } } ix = (q>>1) + 0x3f000000; - ix += m << 23; - SET_FLOAT_WORD(z, ix); + SET_FLOAT_WORD(z, ix + ((uint32_t)m << 23)); return z; } diff --git a/lib/libc/musl/src/misc/getopt.c b/lib/libc/musl/src/misc/getopt.c index 864d52cdc..c3f669955 100644 --- a/lib/libc/musl/src/misc/getopt.c +++ b/lib/libc/musl/src/misc/getopt.c @@ -1,3 +1,4 @@ +#define _BSD_SOURCE #include #include #include diff --git a/lib/libc/musl/src/misc/ioctl.c b/lib/libc/musl/src/misc/ioctl.c index 5a41f0e84..6f31d4bc9 100644 --- a/lib/libc/musl/src/misc/ioctl.c +++ b/lib/libc/musl/src/misc/ioctl.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "syscall.h" int ioctl(int fd, int req, ...) @@ -9,5 +12,25 @@ int ioctl(int fd, int req, ...) va_start(ap, req); arg = va_arg(ap, void *); va_end(ap); - return syscall(SYS_ioctl, fd, req, arg); + int r = __syscall(SYS_ioctl, fd, req, arg); + if (r==-ENOTTY) switch (req) { + case SIOCGSTAMP: + case SIOCGSTAMPNS: + if (SIOCGSTAMP==SIOCGSTAMP_OLD) break; + if (req==SIOCGSTAMP) req=SIOCGSTAMP_OLD; + if (req==SIOCGSTAMPNS) req=SIOCGSTAMPNS_OLD; + long t32[2]; + r = __syscall(SYS_ioctl, fd, req, t32); + if (r<0) break; + if (req==SIOCGSTAMP_OLD) { + struct timeval *tv = arg; + tv->tv_sec = t32[0]; + tv->tv_usec = t32[1]; + } else { + struct timespec *ts = arg; + ts->tv_sec = t32[0]; + ts->tv_nsec = t32[1]; + } + } + return __syscall_ret(r); } diff --git a/lib/libc/musl/src/mq/mq_timedreceive.c b/lib/libc/musl/src/mq/mq_timedreceive.c index 2cef6a86b..f41b6642f 100644 --- a/lib/libc/musl/src/mq/mq_timedreceive.c +++ b/lib/libc/musl/src/mq/mq_timedreceive.c @@ -1,7 +1,24 @@ #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + ssize_t mq_timedreceive(mqd_t mqd, char *restrict msg, size_t len, unsigned *restrict prio, const struct timespec *restrict at) { +#ifdef SYS_mq_timedreceive_time64 + time_t s = at ? at->tv_sec : 0; + long ns = at ? at->tv_nsec : 0; + long r = -ENOSYS; + if (SYS_mq_timedreceive == SYS_mq_timedreceive_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_mq_timedreceive_time64, mqd, msg, len, prio, + at ? ((long long []){at->tv_sec, at->tv_nsec}) : 0); + if (SYS_mq_timedreceive == SYS_mq_timedreceive_time64 || r != -ENOSYS) + return __syscall_ret(r); + return syscall_cp(SYS_mq_timedreceive, mqd, msg, len, prio, + at ? ((long[]){CLAMP(s), ns}) : 0); +#else return syscall_cp(SYS_mq_timedreceive, mqd, msg, len, prio, at); +#endif } diff --git a/lib/libc/musl/src/mq/mq_timedsend.c b/lib/libc/musl/src/mq/mq_timedsend.c index 1c00aa0b2..56cfcbb83 100644 --- a/lib/libc/musl/src/mq/mq_timedsend.c +++ b/lib/libc/musl/src/mq/mq_timedsend.c @@ -1,7 +1,24 @@ #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int mq_timedsend(mqd_t mqd, const char *msg, size_t len, unsigned prio, const struct timespec *at) { +#ifdef SYS_mq_timedsend_time64 + time_t s = at ? at->tv_sec : 0; + long ns = at ? at->tv_nsec : 0; + long r = -ENOSYS; + if (SYS_mq_timedsend == SYS_mq_timedsend_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_mq_timedsend_time64, mqd, msg, len, prio, + at ? ((long long []){at->tv_sec, at->tv_nsec}) : 0); + if (SYS_mq_timedsend == SYS_mq_timedsend_time64 || r != -ENOSYS) + return __syscall_ret(r); + return syscall_cp(SYS_mq_timedsend, mqd, msg, len, prio, + at ? ((long[]){CLAMP(s), ns}) : 0); +#else return syscall_cp(SYS_mq_timedsend, mqd, msg, len, prio, at); +#endif } diff --git a/lib/libc/musl/src/multibyte/mbsrtowcs.c b/lib/libc/musl/src/multibyte/mbsrtowcs.c index 0ee8b69cb..9b2f2dfbb 100644 --- a/lib/libc/musl/src/multibyte/mbsrtowcs.c +++ b/lib/libc/musl/src/multibyte/mbsrtowcs.c @@ -38,12 +38,15 @@ size_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbs } if (!ws) for (;;) { +#ifdef __GNUC__ + typedef uint32_t __attribute__((__may_alias__)) w32; if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { - while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) { + while (!(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) { s += 4; wn -= 4; } } +#endif if (*s-1u < 0x7f) { s++; wn--; @@ -69,8 +72,10 @@ resume0: *src = (const void *)s; return wn0; } +#ifdef __GNUC__ + typedef uint32_t __attribute__((__may_alias__)) w32; if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { - while (wn>=5 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) { + while (wn>=5 && !(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) { *ws++ = *s++; *ws++ = *s++; *ws++ = *s++; @@ -78,6 +83,7 @@ resume0: wn -= 4; } } +#endif if (*s-1u < 0x7f) { *ws++ = *s++; wn--; diff --git a/lib/libc/musl/src/network/getsockopt.c b/lib/libc/musl/src/network/getsockopt.c index 28079d8c0..e871d624b 100644 --- a/lib/libc/musl/src/network/getsockopt.c +++ b/lib/libc/musl/src/network/getsockopt.c @@ -1,7 +1,32 @@ #include +#include +#include #include "syscall.h" int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t *restrict optlen) { - return socketcall(getsockopt, fd, level, optname, optval, optlen, 0); + long tv32[2]; + struct timeval *tv; + + int r = __socketcall(getsockopt, fd, level, optname, optval, optlen, 0); + + if (r==-ENOPROTOOPT) switch (level) { + case SOL_SOCKET: + switch (optname) { + case SO_RCVTIMEO: + case SO_SNDTIMEO: + if (SO_RCVTIMEO == SO_RCVTIMEO_OLD) break; + if (*optlen < sizeof *tv) return __syscall_ret(-EINVAL); + if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD; + if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD; + r = __socketcall(getsockopt, fd, level, optname, + tv32, (socklen_t[]){sizeof tv32}, 0); + if (r<0) break; + tv = optval; + tv->tv_sec = tv32[0]; + tv->tv_usec = tv32[1]; + *optlen = sizeof *tv; + } + } + return __syscall_ret(r); } diff --git a/lib/libc/musl/src/network/recvmmsg.c b/lib/libc/musl/src/network/recvmmsg.c index 58b1b2f63..d5dc6b51c 100644 --- a/lib/libc/musl/src/network/recvmmsg.c +++ b/lib/libc/musl/src/network/recvmmsg.c @@ -1,8 +1,13 @@ #define _GNU_SOURCE #include #include +#include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout) { #if LONG_MAX > INT_MAX @@ -11,5 +16,18 @@ int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int fla for (i = vlen; i; i--, mh++) mh->msg_hdr.__pad1 = mh->msg_hdr.__pad2 = 0; #endif +#ifdef SYS_recvmmsg_time64 + time_t s = timeout ? timeout->tv_sec : 0; + long ns = timeout ? timeout->tv_nsec : 0; + int r = -ENOSYS; + if (SYS_recvmmsg == SYS_recvmmsg_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_recvmmsg_time64, fd, msgvec, vlen, flags, + timeout ? ((long long[]){s, ns}) : 0); + if (SYS_recvmmsg == SYS_recvmmsg_time64 || r!=-ENOSYS) + return __syscall_ret(r); + return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags, + timeout ? ((long[]){CLAMP(s), ns}) : 0); +#else return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags, timeout); +#endif } diff --git a/lib/libc/musl/src/network/setsockopt.c b/lib/libc/musl/src/network/setsockopt.c index c960c9ca7..2c188a96e 100644 --- a/lib/libc/musl/src/network/setsockopt.c +++ b/lib/libc/musl/src/network/setsockopt.c @@ -1,7 +1,37 @@ #include +#include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) { - return socketcall(setsockopt, fd, level, optname, optval, optlen, 0); + const struct timeval *tv; + time_t s; + suseconds_t us; + + int r = __socketcall(setsockopt, fd, level, optname, optval, optlen, 0); + + if (r==-ENOPROTOOPT) switch (level) { + case SOL_SOCKET: + switch (optname) { + case SO_RCVTIMEO: + case SO_SNDTIMEO: + if (SO_RCVTIMEO == SO_RCVTIMEO_OLD) break; + if (optlen < sizeof *tv) return __syscall_ret(-EINVAL); + tv = optval; + s = tv->tv_sec; + us = tv->tv_usec; + if (!IS32BIT(s)) return __syscall_ret(-ENOTSUP); + + if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD; + if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD; + + r = __socketcall(setsockopt, fd, level, optname, + ((long[]){s, CLAMP(us)}), 2*sizeof(long), 0); + } + } + return __syscall_ret(r); } diff --git a/lib/libc/musl/src/process/fdop.h b/lib/libc/musl/src/process/fdop.h index 00b875143..5adf14438 100644 --- a/lib/libc/musl/src/process/fdop.h +++ b/lib/libc/musl/src/process/fdop.h @@ -1,6 +1,8 @@ #define FDOP_CLOSE 1 #define FDOP_DUP2 2 #define FDOP_OPEN 3 +#define FDOP_CHDIR 4 +#define FDOP_FCHDIR 5 struct fdop { struct fdop *next, *prev; diff --git a/lib/libc/musl/src/process/posix_spawn.c b/lib/libc/musl/src/process/posix_spawn.c index 306faa055..29652197c 100644 --- a/lib/libc/musl/src/process/posix_spawn.c +++ b/lib/libc/musl/src/process/posix_spawn.c @@ -125,6 +125,14 @@ static int child(void *args_vp) __syscall(SYS_close, fd); } break; + case FDOP_CHDIR: + ret = __syscall(SYS_chdir, op->path); + if (ret<0) goto fail; + break; + case FDOP_FCHDIR: + ret = __syscall(SYS_fchdir, op->fd); + if (ret<0) goto fail; + break; } } } diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_addchdir.c b/lib/libc/musl/src/process/posix_spawn_file_actions_addchdir.c new file mode 100644 index 000000000..7f2590ae4 --- /dev/null +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_addchdir.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include "fdop.h" + +int posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restrict fa, const char *restrict path) +{ + struct fdop *op = malloc(sizeof *op + strlen(path) + 1); + if (!op) return ENOMEM; + op->cmd = FDOP_CHDIR; + op->fd = -1; + strcpy(op->path, path); + if ((op->next = fa->__actions)) op->next->prev = op; + op->prev = 0; + fa->__actions = op; + return 0; +} diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c b/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c new file mode 100644 index 000000000..436c683d2 --- /dev/null +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include "fdop.h" + +int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *fa, int fd) +{ + struct fdop *op = malloc(sizeof *op); + if (!op) return ENOMEM; + op->cmd = FDOP_FCHDIR; + op->fd = fd; + if ((op->next = fa->__actions)) op->next->prev = op; + op->prev = 0; + fa->__actions = op; + return 0; +} diff --git a/lib/libc/musl/src/regex/glob.c b/lib/libc/musl/src/regex/glob.c index aa1c6a448..9de080ed9 100644 --- a/lib/libc/musl/src/regex/glob.c +++ b/lib/libc/musl/src/regex/glob.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include struct match { @@ -90,16 +92,23 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (* if (!*pat) { /* If we consumed any components above, or if GLOB_MARK is * requested and we don't yet know if the match is a dir, - * we must call stat to confirm the file exists and/or - * determine its type. */ + * we must confirm the file exists and/or determine its type. + * + * If marking dirs, symlink type is inconclusive; we need the + * type for the symlink target, and therefore must try stat + * first unless type is known not to be a symlink. Otherwise, + * or if that fails, use lstat for determining existence to + * avoid false negatives in the case of broken symlinks. */ struct stat st; - if ((flags & GLOB_MARK) && type==DT_LNK) type = 0; - if (!type && stat(buf, &st)) { + if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { + if (S_ISDIR(st.st_mode)) type = DT_DIR; + else type = DT_REG; + } + if (!type && lstat(buf, &st)) { if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) return GLOB_ABORTED; return 0; } - if (!type && S_ISDIR(st.st_mode)) type = DT_DIR; if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR)) return GLOB_NOSPACE; return 0; @@ -182,6 +191,39 @@ static int sort(const void *a, const void *b) return strcmp(*(const char **)a, *(const char **)b); } +static int expand_tilde(char **pat, char *buf, size_t *pos) +{ + char *p = *pat + 1; + size_t i = 0; + + char delim, *name_end = __strchrnul(p, '/'); + if ((delim = *name_end)) *name_end++ = 0; + *pat = name_end; + + char *home = *p ? NULL : getenv("HOME"); + if (!home) { + struct passwd pw, *res; + switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) + : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) { + case ENOMEM: + return GLOB_NOSPACE; + case 0: + if (!res) + default: + return GLOB_NOMATCH; + } + home = pw.pw_dir; + } + while (i < PATH_MAX - 2 && *home) + buf[i++] = *home++; + if (*home) + return GLOB_NOMATCH; + if ((buf[i] = delim)) + buf[++i] = 0; + *pos = i; + return 0; +} + int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) { struct match head = { .next = NULL }, *tail = &head; @@ -202,7 +244,12 @@ int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, i char *p = strdup(pat); if (!p) return GLOB_NOSPACE; buf[0] = 0; - error = do_glob(buf, 0, 0, p, flags, errfunc, &tail); + size_t pos = 0; + char *s = p; + if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') + error = expand_tilde(&s, buf, &pos); + if (!error) + error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); free(p); } diff --git a/lib/libc/musl/src/sched/sched_rr_get_interval.c b/lib/libc/musl/src/sched/sched_rr_get_interval.c index 4b01028f5..33a3d1aee 100644 --- a/lib/libc/musl/src/sched/sched_rr_get_interval.c +++ b/lib/libc/musl/src/sched/sched_rr_get_interval.c @@ -3,5 +3,19 @@ int sched_rr_get_interval(pid_t pid, struct timespec *ts) { +#ifdef SYS_sched_rr_get_interval_time64 + /* On a 32-bit arch, use the old syscall if it exists. */ + if (SYS_sched_rr_get_interval != SYS_sched_rr_get_interval_time64) { + long ts32[2]; + int r = __syscall(SYS_sched_rr_get_interval, pid, ts32); + if (!r) { + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + } + return __syscall_ret(r); + } +#endif + /* If reaching this point, it's a 64-bit arch or time64-only + * 32-bit arch and we can get result directly into timespec. */ return syscall(SYS_sched_rr_get_interval, pid, ts); } diff --git a/lib/libc/musl/src/select/pselect.c b/lib/libc/musl/src/select/pselect.c index 762af37f1..54cfb291b 100644 --- a/lib/libc/musl/src/select/pselect.c +++ b/lib/libc/musl/src/select/pselect.c @@ -1,12 +1,26 @@ #include #include #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int pselect(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, const struct timespec *restrict ts, const sigset_t *restrict mask) { syscall_arg_t data[2] = { (uintptr_t)mask, _NSIG/8 }; - struct timespec ts_tmp; - if (ts) ts_tmp = *ts; - return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, ts ? &ts_tmp : 0, data); + time_t s = ts ? ts->tv_sec : 0; + long ns = ts ? ts->tv_nsec : 0; +#ifdef SYS_pselect6_time64 + int r = -ENOSYS; + if (SYS_pselect6 == SYS_pselect6_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_pselect6_time64, n, rfds, wfds, efds, + ts ? ((long long[]){s, ns}) : 0, data); + if (SYS_pselect6 == SYS_pselect6_time64 || r!=-ENOSYS) + return __syscall_ret(r); + s = CLAMP(s); +#endif + return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, + ts ? ((long[]){s, ns}) : 0, data); } diff --git a/lib/libc/musl/src/select/select.c b/lib/libc/musl/src/select/select.c index 02fd75c38..8a7868840 100644 --- a/lib/libc/musl/src/select/select.c +++ b/lib/libc/musl/src/select/select.c @@ -4,22 +4,41 @@ #include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int select(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, struct timeval *restrict tv) { -#ifdef SYS_select - return syscall_cp(SYS_select, n, rfds, wfds, efds, tv); -#else - syscall_arg_t data[2] = { 0, _NSIG/8 }; - struct timespec ts; - if (tv) { - if (tv->tv_sec < 0 || tv->tv_usec < 0) - return __syscall_ret(-EINVAL); - time_t extra_secs = tv->tv_usec / 1000000; - ts.tv_nsec = tv->tv_usec % 1000000 * 1000; - const time_t max_time = (1ULL<<8*sizeof(time_t)-1)-1; - ts.tv_sec = extra_secs > max_time - tv->tv_sec ? - max_time : tv->tv_sec + extra_secs; + time_t s = tv ? tv->tv_sec : 0; + suseconds_t us = tv ? tv->tv_usec : 0; + long ns; + const time_t max_time = (1ULL<<8*sizeof(time_t)-1)-1; + + if (s<0 || us<0) return __syscall_ret(-EINVAL); + if (us/1000000 > max_time - s) { + s = max_time; + us = 999999; + ns = 999999999; + } else { + s += us/1000000; + us %= 1000000; + ns = us*1000; } - return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, tv ? &ts : 0, data); + +#ifdef SYS_pselect6_time64 + int r = -ENOSYS; + if (SYS_pselect6 == SYS_pselect6_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_pselect6_time64, n, rfds, wfds, efds, + tv ? ((long long[]){s, ns}) : 0, + ((syscall_arg_t[]){ 0, _NSIG/8 })); + if (SYS_pselect6 == SYS_pselect6_time64 || r!=-ENOSYS) + return __syscall_ret(r); +#endif +#ifdef SYS_select + return syscall_cp(SYS_select, n, rfds, wfds, efds, + tv ? ((long[]){s, us}) : 0); +#else + return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, + tv ? ((long[]){s, ns}) : 0, ((syscall_arg_t[]){ 0, _NSIG/8 })); #endif } diff --git a/lib/libc/musl/src/signal/getitimer.c b/lib/libc/musl/src/signal/getitimer.c index 8a8046a76..36d1eb9dc 100644 --- a/lib/libc/musl/src/signal/getitimer.c +++ b/lib/libc/musl/src/signal/getitimer.c @@ -3,5 +3,16 @@ int getitimer(int which, struct itimerval *old) { + if (sizeof(time_t) > sizeof(long)) { + long old32[4]; + int r = __syscall(SYS_getitimer, which, old32); + if (!r) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_usec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_usec = old32[3]; + } + return __syscall_ret(r); + } return syscall(SYS_getitimer, which, old); } diff --git a/lib/libc/musl/src/signal/setitimer.c b/lib/libc/musl/src/signal/setitimer.c index 21b1f45da..0dfbeb4db 100644 --- a/lib/libc/musl/src/signal/setitimer.c +++ b/lib/libc/musl/src/signal/setitimer.c @@ -1,7 +1,26 @@ #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + int setitimer(int which, const struct itimerval *restrict new, struct itimerval *restrict old) { + if (sizeof(time_t) > sizeof(long)) { + time_t is = new->it_interval.tv_sec, vs = new->it_value.tv_sec; + long ius = new->it_interval.tv_usec, vus = new->it_value.tv_usec; + if (!IS32BIT(is) || !IS32BIT(vs)) + return __syscall_ret(-ENOTSUP); + long old32[4]; + int r = __syscall(SYS_setitimer, which, + ((long[]){is, ius, vs, vus}), old32); + if (!r && old) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_usec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_usec = old32[3]; + } + return __syscall_ret(r); + } return syscall(SYS_setitimer, which, new, old); } diff --git a/lib/libc/musl/src/signal/sigaction.c b/lib/libc/musl/src/signal/sigaction.c index 05445089f..c109bea0c 100644 --- a/lib/libc/musl/src/signal/sigaction.c +++ b/lib/libc/musl/src/signal/sigaction.c @@ -7,7 +7,7 @@ #include "lock.h" #include "ksigaction.h" -volatile int dummy_lock[1] = { 0 }; +static volatile int dummy_lock[1] = { 0 }; extern hidden volatile int __abort_lock[1]; diff --git a/lib/libc/musl/src/signal/sigtimedwait.c b/lib/libc/musl/src/signal/sigtimedwait.c index 7bcfe720d..1287174eb 100644 --- a/lib/libc/musl/src/signal/sigtimedwait.c +++ b/lib/libc/musl/src/signal/sigtimedwait.c @@ -2,11 +2,31 @@ #include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +static int do_sigtimedwait(const sigset_t *restrict mask, siginfo_t *restrict si, const struct timespec *restrict ts) +{ +#ifdef SYS_rt_sigtimedwait_time64 + time_t s = ts ? ts->tv_sec : 0; + long ns = ts ? ts->tv_nsec : 0; + int r = -ENOSYS; + if (SYS_rt_sigtimedwait == SYS_rt_sigtimedwait_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_rt_sigtimedwait_time64, mask, si, + ts ? ((long long[]){s, ns}) : 0, _NSIG/8); + if (SYS_rt_sigtimedwait == SYS_rt_sigtimedwait_time64 || r!=-ENOSYS) + return r; + return __syscall_cp(SYS_rt_sigtimedwait, mask, si, + ts ? ((long[]){CLAMP(s), ns}) : 0, _NSIG/8);; +#else + return __syscall_cp(SYS_rt_sigtimedwait, mask, si, ts, _NSIG/8); +#endif +} + int sigtimedwait(const sigset_t *restrict mask, siginfo_t *restrict si, const struct timespec *restrict timeout) { int ret; - do ret = syscall_cp(SYS_rt_sigtimedwait, mask, - si, timeout, _NSIG/8); - while (ret<0 && errno==EINTR); - return ret; + do ret = do_sigtimedwait(mask, si, timeout); + while (ret==-EINTR); + return __syscall_ret(ret); } diff --git a/lib/libc/musl/src/signal/x32/getitimer.c b/lib/libc/musl/src/signal/x32/getitimer.c new file mode 100644 index 000000000..8a8046a76 --- /dev/null +++ b/lib/libc/musl/src/signal/x32/getitimer.c @@ -0,0 +1,7 @@ +#include +#include "syscall.h" + +int getitimer(int which, struct itimerval *old) +{ + return syscall(SYS_getitimer, which, old); +} diff --git a/lib/libc/musl/src/signal/x32/setitimer.c b/lib/libc/musl/src/signal/x32/setitimer.c new file mode 100644 index 000000000..21b1f45da --- /dev/null +++ b/lib/libc/musl/src/signal/x32/setitimer.c @@ -0,0 +1,7 @@ +#include +#include "syscall.h" + +int setitimer(int which, const struct itimerval *restrict new, struct itimerval *restrict old) +{ + return syscall(SYS_setitimer, which, new, old); +} diff --git a/lib/libc/musl/src/stat/fstat.c b/lib/libc/musl/src/stat/fstat.c index 4f13f4f0e..07f9a5de1 100644 --- a/lib/libc/musl/src/stat/fstat.c +++ b/lib/libc/musl/src/stat/fstat.c @@ -1,3 +1,4 @@ +#define _BSD_SOURCE #include #include #include @@ -5,17 +6,8 @@ int fstat(int fd, struct stat *st) { - int ret = __syscall(SYS_fstat, fd, st); - if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0) - return __syscall_ret(ret); - - char buf[15+3*sizeof(int)]; - __procfdname(buf, fd); -#ifdef SYS_stat - return syscall(SYS_stat, buf, st); -#else - return syscall(SYS_fstatat, AT_FDCWD, buf, st, 0); -#endif + if (fd<0) return __syscall_ret(-EBADF); + return fstatat(fd, "", st, AT_EMPTY_PATH); } weak_alias(fstat, fstat64); diff --git a/lib/libc/musl/src/stat/fstatat.c b/lib/libc/musl/src/stat/fstatat.c index 582db4429..d915fa106 100644 --- a/lib/libc/musl/src/stat/fstatat.c +++ b/lib/libc/musl/src/stat/fstatat.c @@ -1,9 +1,129 @@ +#define _BSD_SOURCE #include +#include +#include +#include +#include +#include #include "syscall.h" +#include "kstat.h" -int fstatat(int fd, const char *restrict path, struct stat *restrict buf, int flag) +struct statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t pad1; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t pad; + } stx_atime, stx_btime, stx_ctime, stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t spare[14]; +}; + +static int fstatat_statx(int fd, const char *restrict path, struct stat *restrict st, int flag) { - return syscall(SYS_fstatat, fd, path, buf, flag); + struct statx stx; + + int ret = __syscall(SYS_statx, fd, path, flag, 0x7ff, &stx); + if (ret) return ret; + + *st = (struct stat){ + .st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor), + .st_ino = stx.stx_ino, + .st_mode = stx.stx_mode, + .st_nlink = stx.stx_nlink, + .st_uid = stx.stx_uid, + .st_gid = stx.stx_gid, + .st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor), + .st_size = stx.stx_size, + .st_blksize = stx.stx_blksize, + .st_blocks = stx.stx_blocks, + .st_atim.tv_sec = stx.stx_atime.tv_sec, + .st_atim.tv_nsec = stx.stx_atime.tv_nsec, + .st_mtim.tv_sec = stx.stx_mtime.tv_sec, + .st_mtim.tv_nsec = stx.stx_mtime.tv_nsec, + .st_ctim.tv_sec = stx.stx_ctime.tv_sec, + .st_ctim.tv_nsec = stx.stx_ctime.tv_nsec, + }; + return 0; +} + +static int fstatat_kstat(int fd, const char *restrict path, struct stat *restrict st, int flag) +{ + int ret; + struct kstat kst; + + if (flag==AT_EMPTY_PATH && fd>=0 && !*path) { + ret = __syscall(SYS_fstat, fd, &kst); + if (ret==-EBADF && __syscall(SYS_fcntl, fd, F_GETFD)>=0) { + ret = __syscall(SYS_fstatat, fd, path, &kst, flag); + if (ret==-EINVAL) { + char buf[15+3*sizeof(int)]; + __procfdname(buf, fd); +#ifdef SYS_stat + ret = __syscall(SYS_stat, buf, &kst); +#else + ret = __syscall(SYS_fstatat, AT_FDCWD, buf, &kst, 0); +#endif + } + } + } +#ifdef SYS_lstat + else if ((fd == AT_FDCWD || *path=='/') && flag==AT_SYMLINK_NOFOLLOW) + ret = __syscall(SYS_lstat, path, &kst); +#endif +#ifdef SYS_stat + else if ((fd == AT_FDCWD || *path=='/') && !flag) + ret = __syscall(SYS_stat, path, &kst); +#endif + else ret = __syscall(SYS_fstatat, fd, path, &kst, flag); + + if (ret) return ret; + + *st = (struct stat){ + .st_dev = kst.st_dev, + .st_ino = kst.st_ino, + .st_mode = kst.st_mode, + .st_nlink = kst.st_nlink, + .st_uid = kst.st_uid, + .st_gid = kst.st_gid, + .st_rdev = kst.st_rdev, + .st_size = kst.st_size, + .st_blksize = kst.st_blksize, + .st_blocks = kst.st_blocks, + .st_atim.tv_sec = kst.st_atime_sec, + .st_atim.tv_nsec = kst.st_atime_nsec, + .st_mtim.tv_sec = kst.st_mtime_sec, + .st_mtim.tv_nsec = kst.st_mtime_nsec, + .st_ctim.tv_sec = kst.st_ctime_sec, + .st_ctim.tv_nsec = kst.st_ctime_nsec, + }; + + return 0; +} + +int fstatat(int fd, const char *restrict path, struct stat *restrict st, int flag) +{ + int ret; + if (sizeof((struct kstat){0}.st_atime_sec) < sizeof(time_t)) { + ret = fstatat_statx(fd, path, st, flag); + if (ret!=-ENOSYS) return __syscall_ret(ret); + } + ret = fstatat_kstat(fd, path, st, flag); + return __syscall_ret(ret); } weak_alias(fstatat, fstatat64); diff --git a/lib/libc/musl/src/stat/lstat.c b/lib/libc/musl/src/stat/lstat.c index 5b89f290d..9f95218a4 100644 --- a/lib/libc/musl/src/stat/lstat.c +++ b/lib/libc/musl/src/stat/lstat.c @@ -1,14 +1,9 @@ #include #include -#include "syscall.h" int lstat(const char *restrict path, struct stat *restrict buf) { -#ifdef SYS_lstat - return syscall(SYS_lstat, path, buf); -#else - return syscall(SYS_fstatat, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); -#endif + return fstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); } weak_alias(lstat, lstat64); diff --git a/lib/libc/musl/src/stat/stat.c b/lib/libc/musl/src/stat/stat.c index 0bec9d6fa..528870d2f 100644 --- a/lib/libc/musl/src/stat/stat.c +++ b/lib/libc/musl/src/stat/stat.c @@ -1,14 +1,9 @@ #include #include -#include "syscall.h" int stat(const char *restrict path, struct stat *restrict buf) { -#ifdef SYS_stat - return syscall(SYS_stat, path, buf); -#else - return syscall(SYS_fstatat, AT_FDCWD, path, buf, 0); -#endif + return fstatat(AT_FDCWD, path, buf, 0); } weak_alias(stat, stat64); diff --git a/lib/libc/musl/src/stat/utimensat.c b/lib/libc/musl/src/stat/utimensat.c index 159c8be3b..730723a9e 100644 --- a/lib/libc/musl/src/stat/utimensat.c +++ b/lib/libc/musl/src/stat/utimensat.c @@ -4,28 +4,51 @@ #include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define NS_SPECIAL(ns) ((ns)==UTIME_NOW || (ns)==UTIME_OMIT) + int utimensat(int fd, const char *path, const struct timespec times[2], int flags) { - int r = __syscall(SYS_utimensat, fd, path, times, flags); + int r; + if (times && times[0].tv_nsec==UTIME_NOW && times[1].tv_nsec==UTIME_NOW) + times = 0; +#ifdef SYS_utimensat_time64 + r = -ENOSYS; + time_t s0=0, s1=0; + long ns0=0, ns1=0; + if (times) { + ns0 = times[0].tv_nsec; + ns1 = times[1].tv_nsec; + if (!NS_SPECIAL(ns0)) s0 = times[0].tv_sec; + if (!NS_SPECIAL(ns1)) s1 = times[1].tv_sec; + } + if (SYS_utimensat == SYS_utimensat_time64 || !IS32BIT(s0) || !IS32BIT(s1)) + r = __syscall(SYS_utimensat_time64, fd, path, times ? + ((long long[]){s0, ns0, s1, ns1}) : 0, flags); + if (SYS_utimensat == SYS_utimensat_time64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(s0) || !IS32BIT(s1)) + return __syscall_ret(-ENOTSUP); + r = __syscall(SYS_utimensat, fd, path, + times ? ((long[]){s0, ns0, s1, ns1}) : 0, flags); +#else + r = __syscall(SYS_utimensat, fd, path, times, flags); +#endif + #ifdef SYS_futimesat if (r != -ENOSYS || flags) return __syscall_ret(r); - struct timeval *tv = 0, tmp[2]; + long *tv=0, tmp[4]; if (times) { int i; tv = tmp; for (i=0; i<2; i++) { if (times[i].tv_nsec >= 1000000000ULL) { - if (times[i].tv_nsec == UTIME_NOW && - times[1-i].tv_nsec == UTIME_NOW) { - tv = 0; - break; - } - if (times[i].tv_nsec == UTIME_OMIT) + if (NS_SPECIAL(times[i].tv_nsec)) return __syscall_ret(-ENOSYS); return __syscall_ret(-EINVAL); } - tmp[i].tv_sec = times[i].tv_sec; - tmp[i].tv_usec = times[i].tv_nsec / 1000; + tmp[2*i+0] = times[i].tv_sec; + tmp[2*i+1] = times[i].tv_nsec / 1000; } } diff --git a/lib/libc/musl/src/stdio/__stdio_seek.c b/lib/libc/musl/src/stdio/__stdio_seek.c index 13e06a663..326ab9bce 100644 --- a/lib/libc/musl/src/stdio/__stdio_seek.c +++ b/lib/libc/musl/src/stdio/__stdio_seek.c @@ -1,13 +1,7 @@ #include "stdio_impl.h" +#include off_t __stdio_seek(FILE *f, off_t off, int whence) { - off_t ret; -#ifdef SYS__llseek - if (syscall(SYS__llseek, f->fd, off>>32, off, &ret, whence)<0) - ret = -1; -#else - ret = syscall(SYS_lseek, f->fd, off, whence); -#endif - return ret; + return __lseek(f->fd, off, whence); } diff --git a/lib/libc/musl/src/stdio/vfwprintf.c b/lib/libc/musl/src/stdio/vfwprintf.c index 0adf0b7a4..85b036c3d 100644 --- a/lib/libc/musl/src/stdio/vfwprintf.c +++ b/lib/libc/musl/src/stdio/vfwprintf.c @@ -53,6 +53,8 @@ static const unsigned char states[]['z'-'A'+1] = { }, { /* 1: l-prefixed */ S('d') = LONG, S('i') = LONG, S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, S('c') = INT, S('s') = PTR, S('n') = PTR, S('l') = LLPRE, }, { /* 2: ll-prefixed */ diff --git a/lib/libc/musl/src/thread/__timedwait.c b/lib/libc/musl/src/thread/__timedwait.c index ae19bd630..666093be9 100644 --- a/lib/libc/musl/src/thread/__timedwait.c +++ b/lib/libc/musl/src/thread/__timedwait.c @@ -5,6 +5,27 @@ #include "syscall.h" #include "pthread_impl.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +static int __futex4_cp(volatile void *addr, int op, int val, const struct timespec *to) +{ + int r; +#ifdef SYS_futex_time64 + time_t s = to ? to->tv_sec : 0; + long ns = to ? to->tv_nsec : 0; + r = -ENOSYS; + if (SYS_futex == SYS_futex_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_futex_time64, addr, op, val, + to ? ((long long[]){s, ns}) : 0); + if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r; + to = to ? (void *)(long[]){CLAMP(s), ns} : 0; +#endif + r = __syscall_cp(SYS_futex, addr, op, val, to); + if (r != -ENOSYS) return r; + return __syscall_cp(SYS_futex, addr, op & ~FUTEX_PRIVATE, val, to); +} + static volatile int dummy = 0; weak_alias(dummy, __eintr_valid_flag); @@ -28,8 +49,7 @@ int __timedwait_cp(volatile int *addr, int val, top = &to; } - r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top); - if (r == ENOSYS) r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top); + r = -__futex4_cp(addr, FUTEX_WAIT|priv, val, top); if (r != EINTR && r != ETIMEDOUT && r != ECANCELED) r = 0; /* Mitigate bug in old kernels wrongly reporting EINTR for non- * interrupting (SA_RESTART) signal handlers. This is only practical diff --git a/lib/libc/musl/src/thread/arm/atomics.s b/lib/libc/musl/src/thread/arm/atomics.s index 101ad391e..da50508d8 100644 --- a/lib/libc/musl/src/thread/arm/atomics.s +++ b/lib/libc/musl/src/thread/arm/atomics.s @@ -15,10 +15,10 @@ __a_barrier_oldkuser: mov r1,r0 mov r2,sp ldr ip,=0xffff0fc0 - mov lr,pc - mov pc,ip + bl 1f pop {r0,r1,r2,r3,ip,lr} bx lr +1: bx ip .global __a_barrier_v6 .hidden __a_barrier_v6 diff --git a/lib/libc/musl/src/thread/arm/clone.s b/lib/libc/musl/src/thread/arm/clone.s index e16b13260..bb0965daf 100644 --- a/lib/libc/musl/src/thread/arm/clone.s +++ b/lib/libc/musl/src/thread/arm/clone.s @@ -20,13 +20,9 @@ __clone: bx lr 1: mov r0,r6 - tst r5,#1 - bne 1f - mov lr,pc - mov pc,r5 + bl 3f 2: mov r7,#1 svc 0 - -1: mov lr,pc - bx r5 b 2b + +3: bx r5 diff --git a/lib/libc/musl/src/thread/arm/syscall_cp.s b/lib/libc/musl/src/thread/arm/syscall_cp.s index a5730c08b..e607dd426 100644 --- a/lib/libc/musl/src/thread/arm/syscall_cp.s +++ b/lib/libc/musl/src/thread/arm/syscall_cp.s @@ -11,19 +11,19 @@ .type __syscall_cp_asm,%function __syscall_cp_asm: mov ip,sp - stmfd sp!,{r4,r5,r6,r7,lr} + stmfd sp!,{r4,r5,r6,r7} __cp_begin: ldr r0,[r0] cmp r0,#0 - blne __cp_cancel + bne __cp_cancel mov r7,r1 mov r0,r2 mov r1,r3 ldmfd ip,{r2,r3,r4,r5,r6} svc 0 __cp_end: - ldmfd sp!,{r4,r5,r6,r7,lr} + ldmfd sp!,{r4,r5,r6,r7} bx lr __cp_cancel: - ldmfd sp!,{r4,r5,r6,r7,lr} + ldmfd sp!,{r4,r5,r6,r7} b __cancel diff --git a/lib/libc/musl/src/thread/pthread_create.c b/lib/libc/musl/src/thread/pthread_create.c index ebf61dedc..5f4910925 100644 --- a/lib/libc/musl/src/thread/pthread_create.c +++ b/lib/libc/musl/src/thread/pthread_create.c @@ -172,23 +172,20 @@ void __do_cleanup_pop(struct __ptcb *cb) struct start_args { void *(*start_func)(void *); void *start_arg; - pthread_attr_t *attr; - volatile int *perr; + volatile int control; unsigned long sig_mask[_NSIG/8/sizeof(long)]; }; static int start(void *p) { struct start_args *args = p; - if (args->attr) { - pthread_t self = __pthread_self(); - int ret = -__syscall(SYS_sched_setscheduler, self->tid, - args->attr->_a_policy, &args->attr->_a_prio); - if (a_swap(args->perr, ret)==-2) - __wake(args->perr, 1, 1); - if (ret) { - self->detach_state = DT_DETACHED; - __pthread_exit(0); + int state = args->control; + if (state) { + if (a_cas(&args->control, 1, 2)==1) + __wait(&args->control, 0, 2, 1); + if (args->control) { + __syscall(SYS_set_tid_address, &args->control); + for (;;) __syscall(SYS_exit, 0); } } __syscall(SYS_rt_sigprocmask, SIG_SETMASK, &args->sig_mask, 0, _NSIG/8); @@ -233,7 +230,6 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED; pthread_attr_t attr = { 0 }; sigset_t set; - volatile int err = -1; if (!libc.can_do_threads) return ENOSYS; self = __pthread_self(); @@ -325,13 +321,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att struct start_args *args = (void *)stack; args->start_func = entry; args->start_arg = arg; - if (attr._a_sched) { - args->attr = &attr; - args->perr = &err; - } else { - args->attr = 0; - args->perr = 0; - } + args->control = attr._a_sched ? 1 : 0; /* Application signals (but not the synccall signal) must be * blocked before the thread list lock can be taken, to ensure @@ -349,29 +339,36 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att libc.threads_minus_1++; ret = __clone((c11 ? start_c11 : start), stack, flags, args, &new->tid, TP_ADJ(new), &__thread_list_lock); - /* If clone succeeded, new thread must be linked on the thread - * list before unlocking it, even if scheduling may still fail. */ + /* All clone failures translate to EAGAIN. If explicit scheduling + * was requested, attempt it before unlocking the thread list so + * that the failed thread is never exposed and so that we can + * clean up all transient resource usage before returning. */ + if (ret < 0) { + ret = -EAGAIN; + } else if (attr._a_sched) { + ret = __syscall(SYS_sched_setscheduler, + new->tid, attr._a_policy, &attr._a_prio); + if (a_swap(&args->control, ret ? 3 : 0)==2) + __wake(&args->control, 1, 1); + if (ret) + __wait(&args->control, 0, 3, 0); + } + if (ret >= 0) { new->next = self->next; new->prev = self; new->next->prev = new; new->prev->next = new; + } else { + libc.threads_minus_1--; } __tl_unlock(); __restore_sigs(&set); __release_ptc(); if (ret < 0) { - libc.threads_minus_1--; if (map) __munmap(map, size); - return EAGAIN; - } - - if (attr._a_sched) { - if (a_cas(&err, -1, -2)==-1) - __wait(&err, 0, -2, 1); - ret = err; - if (ret) return ret; + return -ret; } *res = new; diff --git a/lib/libc/musl/src/thread/pthread_join.c b/lib/libc/musl/src/thread/pthread_join.c index b8813e026..17dae85d7 100644 --- a/lib/libc/musl/src/thread/pthread_join.c +++ b/lib/libc/musl/src/thread/pthread_join.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include "pthread_impl.h" #include diff --git a/lib/libc/musl/src/thread/pthread_mutex_timedlock.c b/lib/libc/musl/src/thread/pthread_mutex_timedlock.c index 6b8936277..9279fc543 100644 --- a/lib/libc/musl/src/thread/pthread_mutex_timedlock.c +++ b/lib/libc/musl/src/thread/pthread_mutex_timedlock.c @@ -1,5 +1,23 @@ #include "pthread_impl.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +static int __futex4(volatile void *addr, int op, int val, const struct timespec *to) +{ +#ifdef SYS_futex_time64 + time_t s = to ? to->tv_sec : 0; + long ns = to ? to->tv_nsec : 0; + int r = -ENOSYS; + if (SYS_futex == SYS_futex_time64 || !IS32BIT(s)) + r = __syscall(SYS_futex_time64, addr, op, val, + to ? ((long long[]){s, ns}) : 0); + if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r; + to = to ? (void *)(long[]){CLAMP(s), ns} : 0; +#endif + return __syscall(SYS_futex, addr, op, val, to); +} + static int pthread_mutex_timedlock_pi(pthread_mutex_t *restrict m, const struct timespec *restrict at) { int type = m->_m_type; @@ -9,7 +27,7 @@ static int pthread_mutex_timedlock_pi(pthread_mutex_t *restrict m, const struct if (!priv) self->robust_list.pending = &m->_m_next; - do e = -__syscall(SYS_futex, &m->_m_lock, FUTEX_LOCK_PI|priv, 0, at); + do e = -__futex4(&m->_m_lock, FUTEX_LOCK_PI|priv, 0, at); while (e==EINTR); if (e) self->robust_list.pending = 0; diff --git a/lib/libc/musl/src/thread/thrd_sleep.c b/lib/libc/musl/src/thread/thrd_sleep.c index e8dfe400c..97de53455 100644 --- a/lib/libc/musl/src/thread/thrd_sleep.c +++ b/lib/libc/musl/src/thread/thrd_sleep.c @@ -1,10 +1,11 @@ #include +#include #include #include "syscall.h" int thrd_sleep(const struct timespec *req, struct timespec *rem) { - int ret = __syscall(SYS_nanosleep, req, rem); + int ret = -__clock_nanosleep(CLOCK_REALTIME, 0, req, rem); switch (ret) { case 0: return 0; case -EINTR: return -1; /* value specified by C11 */ diff --git a/lib/libc/musl/src/thread/x32/syscall_cp.s b/lib/libc/musl/src/thread/x32/syscall_cp.s index 9805af0ae..4f101716d 100644 --- a/lib/libc/musl/src/thread/x32/syscall_cp.s +++ b/lib/libc/musl/src/thread/x32/syscall_cp.s @@ -6,10 +6,10 @@ .global __cp_cancel .hidden __cp_cancel .hidden __cancel -.global __syscall_cp_internal -.hidden __syscall_cp_internal -.type __syscall_cp_internal,@function -__syscall_cp_internal: +.global __syscall_cp_asm +.hidden __syscall_cp_asm +.type __syscall_cp_asm,@function +__syscall_cp_asm: __cp_begin: mov (%rdi),%eax diff --git a/lib/libc/musl/src/thread/x32/syscall_cp_fixup.c b/lib/libc/musl/src/thread/x32/syscall_cp_fixup.c deleted file mode 100644 index 4956610f0..000000000 --- a/lib/libc/musl/src/thread/x32/syscall_cp_fixup.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -hidden long __syscall_cp_internal(volatile void*, long long, long long, - long long, long long, long long, - long long, long long); - -struct __timespec { long long tv_sec; long tv_nsec; }; -struct __timespec_kernel { long long tv_sec; long long tv_nsec; }; -#define __tsc(X) ((struct __timespec*)(unsigned long)(X)) -#define __fixup(X) do { if(X) { \ - ts->tv_sec = __tsc(X)->tv_sec; \ - ts->tv_nsec = __tsc(X)->tv_nsec; \ - (X) = (unsigned long)ts; } } while(0) - -hidden long __syscall_cp_asm (volatile void * foo, long long n, long long a1, - long long a2, long long a3, long long a4, - long long a5, long long a6) -{ - struct __timespec_kernel ts[1]; - switch (n) { - case SYS_mq_timedsend: case SYS_mq_timedreceive: case SYS_pselect6: - __fixup(a5); - break; - case SYS_futex: - if((a2 & (~128 /* FUTEX_PRIVATE_FLAG */)) == 0 /* FUTEX_WAIT */) - __fixup(a4); - break; - case SYS_clock_nanosleep: - case SYS_rt_sigtimedwait: case SYS_ppoll: - __fixup(a3); - break; - case SYS_nanosleep: - __fixup(a1); - break; - } - return __syscall_cp_internal(foo, n, a1, a2, a3, a4, a5, a6); -} - diff --git a/lib/libc/musl/src/time/clock_getres.c b/lib/libc/musl/src/time/clock_getres.c index 36a0d695b..81c670376 100644 --- a/lib/libc/musl/src/time/clock_getres.c +++ b/lib/libc/musl/src/time/clock_getres.c @@ -3,5 +3,19 @@ int clock_getres(clockid_t clk, struct timespec *ts) { +#ifdef SYS_clock_getres_time64 + /* On a 32-bit arch, use the old syscall if it exists. */ + if (SYS_clock_getres != SYS_clock_getres_time64) { + long ts32[2]; + int r = __syscall(SYS_clock_getres, clk, ts32); + if (!r && ts) { + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + } + return __syscall_ret(r); + } +#endif + /* If reaching this point, it's a 64-bit arch or time64-only + * 32-bit arch and we can get result directly into timespec. */ return syscall(SYS_clock_getres, clk, ts); } diff --git a/lib/libc/musl/src/time/clock_gettime.c b/lib/libc/musl/src/time/clock_gettime.c index 8fd1b8f58..3e1d0975b 100644 --- a/lib/libc/musl/src/time/clock_gettime.c +++ b/lib/libc/musl/src/time/clock_gettime.c @@ -8,9 +8,41 @@ static void *volatile vdso_func; +#ifdef VDSO_CGT32_SYM +static void *volatile vdso_func_32; +static int cgt_time32_wrap(clockid_t clk, struct timespec *ts) +{ + long ts32[2]; + int (*f)(clockid_t, long[2]) = + (int (*)(clockid_t, long[2]))vdso_func_32; + int r = f(clk, ts32); + if (!r) { + /* Fallback to syscalls if time32 overflowed. Maybe + * we lucked out and somehow migrated to a kernel with + * time64 syscalls available. */ + if (ts32[0] < 0) { + a_cas_p(&vdso_func, (void *)cgt_time32_wrap, 0); + return -ENOSYS; + } + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + } + return r; +} +#endif + static int cgt_init(clockid_t clk, struct timespec *ts) { void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); +#ifdef VDSO_CGT32_SYM + if (!p) { + void *q = __vdsosym(VDSO_CGT32_VER, VDSO_CGT32_SYM); + if (q) { + a_cas_p(&vdso_func_32, 0, q); + p = cgt_time32_wrap; + } + } +#endif int (*f)(clockid_t, struct timespec *) = (int (*)(clockid_t, struct timespec *))p; a_cas_p(&vdso_func, (void *)cgt_init, p); @@ -40,6 +72,25 @@ int __clock_gettime(clockid_t clk, struct timespec *ts) } #endif +#ifdef SYS_clock_gettime64 + r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_clock_gettime64, clk, ts); + if (SYS_clock_gettime == SYS_clock_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long ts32[2]; + r = __syscall(SYS_clock_gettime, clk, ts32); + if (r==-ENOSYS && clk==CLOCK_REALTIME) { + r = __syscall(SYS_gettimeofday, ts32, 0); + ts32[1] *= 1000; + } + if (!r) { + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + return r; + } + return __syscall_ret(r); +#else r = __syscall(SYS_clock_gettime, clk, ts); if (r == -ENOSYS) { if (clk == CLOCK_REALTIME) { @@ -50,6 +101,7 @@ int __clock_gettime(clockid_t clk, struct timespec *ts) r = -EINVAL; } return __syscall_ret(r); +#endif } weak_alias(__clock_gettime, clock_gettime); diff --git a/lib/libc/musl/src/time/clock_nanosleep.c b/lib/libc/musl/src/time/clock_nanosleep.c index 32f0c07e3..e195499cc 100644 --- a/lib/libc/musl/src/time/clock_nanosleep.c +++ b/lib/libc/musl/src/time/clock_nanosleep.c @@ -2,8 +2,37 @@ #include #include "syscall.h" -int clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem) +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem) { - int r = -__syscall_cp(SYS_clock_nanosleep, clk, flags, req, rem); - return clk == CLOCK_THREAD_CPUTIME_ID ? EINVAL : r; + if (clk == CLOCK_THREAD_CPUTIME_ID) return EINVAL; +#ifdef SYS_clock_nanosleep_time64 + time_t s = req->tv_sec; + long ns = req->tv_nsec; + int r = -ENOSYS; + if (SYS_clock_nanosleep == SYS_clock_nanosleep_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_clock_nanosleep_time64, clk, flags, + ((long long[]){s, ns}), rem); + if (SYS_clock_nanosleep == SYS_clock_nanosleep_time64 || r!=-ENOSYS) + return -r; + long long extra = s - CLAMP(s); + long ts32[2] = { CLAMP(s), ns }; + if (clk == CLOCK_REALTIME && !flags) + r = __syscall_cp(SYS_nanosleep, &ts32, &ts32); + else + r = __syscall_cp(SYS_clock_nanosleep, clk, flags, &ts32, &ts32); + if (r==-EINTR && rem && !(flags & TIMER_ABSTIME)) { + rem->tv_sec = ts32[0] + extra; + rem->tv_nsec = ts32[1]; + } + return -r; +#else + if (clk == CLOCK_REALTIME && !flags) + return -__syscall_cp(SYS_nanosleep, req, rem); + return -__syscall_cp(SYS_clock_nanosleep, clk, flags, req, rem); +#endif } + +weak_alias(__clock_nanosleep, clock_nanosleep); diff --git a/lib/libc/musl/src/time/clock_settime.c b/lib/libc/musl/src/time/clock_settime.c index 66b8162d7..1004ed152 100644 --- a/lib/libc/musl/src/time/clock_settime.c +++ b/lib/libc/musl/src/time/clock_settime.c @@ -1,7 +1,24 @@ #include +#include #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + int clock_settime(clockid_t clk, const struct timespec *ts) { +#ifdef SYS_clock_settime64 + time_t s = ts->tv_sec; + long ns = ts->tv_nsec; + int r = -ENOSYS; + if (SYS_clock_settime == SYS_clock_settime64 || !IS32BIT(s)) + r = __syscall(SYS_clock_settime64, clk, + ((long long[]){s, ns})); + if (SYS_clock_settime == SYS_clock_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(s)) + return __syscall_ret(-ENOTSUP); + return syscall(SYS_clock_settime, clk, ((long[]){s, ns})); +#else return syscall(SYS_clock_settime, clk, ts); +#endif } diff --git a/lib/libc/musl/src/time/nanosleep.c b/lib/libc/musl/src/time/nanosleep.c index 1e6f39224..bc9f7895f 100644 --- a/lib/libc/musl/src/time/nanosleep.c +++ b/lib/libc/musl/src/time/nanosleep.c @@ -3,5 +3,5 @@ int nanosleep(const struct timespec *req, struct timespec *rem) { - return syscall_cp(SYS_nanosleep, req, rem); + return __syscall_ret(-__clock_nanosleep(CLOCK_REALTIME, 0, req, rem)); } diff --git a/lib/libc/musl/src/time/timer_create.c b/lib/libc/musl/src/time/timer_create.c index c5e40a195..455d49fc5 100644 --- a/lib/libc/musl/src/time/timer_create.c +++ b/lib/libc/musl/src/time/timer_create.c @@ -1,5 +1,6 @@ #include #include +#include #include "pthread_impl.h" struct ksigevent { @@ -48,7 +49,6 @@ static void *start(void *arg) { pthread_t self = __pthread_self(); struct start_args *args = arg; - int id = self->timer_id; jmp_buf jb; void (*notify)(union sigval) = args->sev->sigev_notify_function; @@ -65,7 +65,7 @@ static void *start(void *arg) } if (self->timer_id < 0) break; } - __syscall(SYS_timer_delete, id); + __syscall(SYS_timer_delete, self->timer_id & INT_MAX); return 0; } diff --git a/lib/libc/musl/src/time/timer_gettime.c b/lib/libc/musl/src/time/timer_gettime.c index ed6d8d65c..21c9d32c3 100644 --- a/lib/libc/musl/src/time/timer_gettime.c +++ b/lib/libc/musl/src/time/timer_gettime.c @@ -8,5 +8,21 @@ int timer_gettime(timer_t t, struct itimerspec *val) pthread_t td = (void *)((uintptr_t)t << 1); t = (void *)(uintptr_t)(td->timer_id & INT_MAX); } +#ifdef SYS_timer_gettime64 + int r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_timer_gettime64, t, val); + if (SYS_timer_gettime == SYS_timer_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long val32[4]; + r = __syscall(SYS_timer_gettime, t, val32); + if (!r) { + val->it_interval.tv_sec = val32[0]; + val->it_interval.tv_nsec = val32[1]; + val->it_value.tv_sec = val32[2]; + val->it_value.tv_nsec = val32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timer_gettime, t, val); } diff --git a/lib/libc/musl/src/time/timer_settime.c b/lib/libc/musl/src/time/timer_settime.c index 62631aa49..373f00ced 100644 --- a/lib/libc/musl/src/time/timer_settime.c +++ b/lib/libc/musl/src/time/timer_settime.c @@ -2,11 +2,36 @@ #include #include "pthread_impl.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + int timer_settime(timer_t t, int flags, const struct itimerspec *restrict val, struct itimerspec *restrict old) { if ((intptr_t)t < 0) { pthread_t td = (void *)((uintptr_t)t << 1); t = (void *)(uintptr_t)(td->timer_id & INT_MAX); } +#ifdef SYS_timer_settime64 + time_t is = val->it_interval.tv_sec, vs = val->it_value.tv_sec; + long ins = val->it_interval.tv_nsec, vns = val->it_value.tv_nsec; + int r = -ENOSYS; + if (SYS_timer_settime == SYS_timer_settime64 + || !IS32BIT(is) || !IS32BIT(vs) || (sizeof(time_t)>4 && old)) + r = __syscall(SYS_timer_settime64, t, flags, + ((long long[]){is, ins, vs, vns}), old); + if (SYS_timer_settime == SYS_timer_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(is) || !IS32BIT(vs)) + return __syscall_ret(-ENOTSUP); + long old32[4]; + r = __syscall(SYS_timer_settime, t, flags, + ((long[]){is, ins, vs, vns}), old32); + if (!r && old) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_nsec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_nsec = old32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timer_settime, t, flags, val, old); } diff --git a/lib/libc/musl/src/unistd/alarm.c b/lib/libc/musl/src/unistd/alarm.c index 2e3263ac5..a5e0c822a 100644 --- a/lib/libc/musl/src/unistd/alarm.c +++ b/lib/libc/musl/src/unistd/alarm.c @@ -4,7 +4,7 @@ unsigned alarm(unsigned seconds) { - struct itimerval it = { .it_value.tv_sec = seconds }; - __syscall(SYS_setitimer, ITIMER_REAL, &it, &it); - return it.it_value.tv_sec + !!it.it_value.tv_usec; + struct itimerval it = { .it_value.tv_sec = seconds }, old = { 0 }; + setitimer(ITIMER_REAL, &it, &old); + return old.it_value.tv_sec + !!old.it_value.tv_usec; } diff --git a/lib/libc/musl/src/unistd/lseek.c b/lib/libc/musl/src/unistd/lseek.c index bf8cd8528..b4984f3e9 100644 --- a/lib/libc/musl/src/unistd/lseek.c +++ b/lib/libc/musl/src/unistd/lseek.c @@ -1,7 +1,7 @@ #include #include "syscall.h" -off_t lseek(int fd, off_t offset, int whence) +off_t __lseek(int fd, off_t offset, int whence) { #ifdef SYS__llseek off_t result; @@ -11,4 +11,5 @@ off_t lseek(int fd, off_t offset, int whence) #endif } -weak_alias(lseek, lseek64); +weak_alias(__lseek, lseek); +weak_alias(__lseek, lseek64); diff --git a/lib/libc/musl/src/unistd/mipsn32/lseek.c b/lib/libc/musl/src/unistd/mipsn32/lseek.c new file mode 100644 index 000000000..60e74a51f --- /dev/null +++ b/lib/libc/musl/src/unistd/mipsn32/lseek.c @@ -0,0 +1,20 @@ +#include +#include "syscall.h" + +off_t __lseek(int fd, off_t offset, int whence) +{ + register long long r4 __asm__("$4") = fd; + register long long r5 __asm__("$5") = offset; + register long long r6 __asm__("$6") = whence; + register long long r7 __asm__("$7"); + register long long r2 __asm__("$2") = SYS_lseek; + __asm__ __volatile__ ( + "syscall" + : "+&r"(r2), "=r"(r7) + : "r"(r4), "r"(r5), "r"(r6) + : SYSCALL_CLOBBERLIST); + return r7 ? __syscall_ret(-r2) : r2; +} + +weak_alias(__lseek, lseek); +weak_alias(__lseek, lseek64); diff --git a/lib/libc/musl/src/unistd/x32/lseek.c b/lib/libc/musl/src/unistd/x32/lseek.c new file mode 100644 index 000000000..326364295 --- /dev/null +++ b/lib/libc/musl/src/unistd/x32/lseek.c @@ -0,0 +1,15 @@ +#include +#include "syscall.h" + +off_t __lseek(int fd, off_t offset, int whence) +{ + off_t ret; + __asm__ __volatile__ ("syscall" + : "=a"(ret) + : "a"(SYS_lseek), "D"(fd), "S"(offset), "d"(whence) + : "rcx", "r11", "memory"); + return ret < 0 ? __syscall_ret(ret) : ret; +} + +weak_alias(__lseek, lseek); +weak_alias(__lseek, lseek64); diff --git a/lib/libcxx/LICENSE.TXT b/lib/libcxx/LICENSE.TXT new file mode 100644 index 000000000..e159d2834 --- /dev/null +++ b/lib/libcxx/LICENSE.TXT @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libc++ library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 31ae02b29..64f13eff9 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -5,6 +5,10 @@ const testing = std.testing; const mem = std.mem; const Allocator = mem.Allocator; +/// List of items. +/// +/// This is a wrapper around an array of T values. Initialize with +/// `init`. pub fn ArrayList(comptime T: type) type { return AlignedArrayList(T, null); } @@ -31,28 +35,47 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn init(allocator: *Allocator) Self { return Self{ - .items = [_]T{}, + .items = &[_]T{}, .len = 0, .allocator = allocator, }; } + /// Initialize with capacity to hold at least num elements. + /// Deinitialize with `deinit` or use `toOwnedSlice`. + pub fn initCapacity(allocator: *Allocator, num: usize) !Self { + var self = Self.init(allocator); + try self.ensureCapacity(num); + return self; + } + + /// Release all allocated memory. pub fn deinit(self: Self) void { self.allocator.free(self.items); } + /// Return contents as a slice. Only valid while the list + /// doesn't change size. pub fn toSlice(self: Self) Slice { return self.items[0..self.len]; } + /// Return list as const slice. Only valid while the list + /// doesn't change size. pub fn toSliceConst(self: Self) SliceConst { return self.items[0..self.len]; } + /// Safely access index i of the list. pub fn at(self: Self, i: usize) T { return self.toSliceConst()[i]; } + /// Safely access ptr to index i of the list. + pub fn ptrAt(self: Self, i: usize) *T { + return &self.toSlice()[i]; + } + /// Sets the value at index `i`, or returns `error.OutOfBounds` if /// the index is not in range. pub fn setOrError(self: Self, i: usize, item: T) !void { @@ -66,10 +89,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { self.items[i] = item; } - pub fn count(self: Self) usize { - return self.len; - } - + /// Return the maximum number of items the list can hold + /// without allocating more memory. pub fn capacity(self: Self) usize { return self.items.len; } @@ -93,6 +114,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { return result; } + /// Insert `item` at index `n`. Moves `list[n .. list.len]` + /// to make room. pub fn insert(self: *Self, n: usize, item: T) !void { try self.ensureCapacity(self.len + 1); self.len += 1; @@ -101,6 +124,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { self.items[n] = item; } + /// Insert slice `items` at index `n`. Moves + /// `list[n .. list.len]` to make room. pub fn insertSlice(self: *Self, n: usize, items: SliceConst) !void { try self.ensureCapacity(self.len + items.len); self.len += items.len; @@ -109,16 +134,22 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { mem.copy(T, self.items[n .. n + items.len], items); } + /// Extend the list by 1 element. Allocates more memory as + /// necessary. pub fn append(self: *Self, item: T) !void { const new_item_ptr = try self.addOne(); new_item_ptr.* = item; } + /// Extend the list by 1 element, but asserting `self.capacity` + /// is sufficient to hold an additional item. pub fn appendAssumeCapacity(self: *Self, item: T) void { const new_item_ptr = self.addOneAssumeCapacity(); new_item_ptr.* = item; } + /// Remove the element at index `i` from the list and return + /// its value. Asserts the array has at least one item. pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.len - 1; if (newlen == i) return self.pop(); @@ -149,17 +180,22 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { return self.swapRemove(i); } + /// Append the slice of items to the list. Allocates more + /// memory as necessary. pub fn appendSlice(self: *Self, items: SliceConst) !void { try self.ensureCapacity(self.len + items.len); mem.copy(T, self.items[self.len..], items); self.len += items.len; } + /// Adjust the list's length to `new_len`. Doesn't initialize + /// added items if any. pub fn resize(self: *Self, new_len: usize) !void { try self.ensureCapacity(new_len); self.len = new_len; } + /// Reduce allocated capacity to `new_len`. pub fn shrink(self: *Self, new_len: usize) void { assert(new_len <= self.len); self.len = new_len; @@ -178,6 +214,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { self.items = try self.allocator.realloc(self.items, better_capacity); } + /// Increase length by 1, returning pointer to the new item. pub fn addOne(self: *Self) !*T { const new_length = self.len + 1; try self.ensureCapacity(new_length); @@ -185,45 +222,24 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { } pub fn addOneAssumeCapacity(self: *Self) *T { - assert(self.count() < self.capacity()); + assert(self.len < self.capacity()); const result = &self.items[self.len]; self.len += 1; return result; } + /// Remove and return the last element from the list. Asserts + /// the list has at least one item. pub fn pop(self: *Self) T { self.len -= 1; return self.items[self.len]; } + /// Like `pop` but returns `null` if empty. pub fn popOrNull(self: *Self) ?T { if (self.len == 0) return null; return self.pop(); } - - pub const Iterator = struct { - list: *const Self, - // how many items have we returned - count: usize, - - pub fn next(it: *Iterator) ?T { - if (it.count >= it.list.len) return null; - const val = it.list.at(it.count); - it.count += 1; - return val; - } - - pub fn reset(it: *Iterator) void { - it.count = 0; - } - }; - - pub fn iterator(self: *const Self) Iterator { - return Iterator{ - .list = self, - .count = 0, - }; - } }; } @@ -234,10 +250,19 @@ test "std.ArrayList.init" { var list = ArrayList(i32).init(allocator); defer list.deinit(); - testing.expect(list.count() == 0); + testing.expect(list.len == 0); testing.expect(list.capacity() == 0); } +test "std.ArrayList.initCapacity" { + var bytes: [1024]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + var list = try ArrayList(i8).initCapacity(allocator, 200); + defer list.deinit(); + testing.expect(list.len == 0); + testing.expect(list.capacity() >= 200); +} + test "std.ArrayList.basic" { var bytes: [1024]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; @@ -273,18 +298,14 @@ test "std.ArrayList.basic" { testing.expect(list.pop() == 10); testing.expect(list.len == 9); - list.appendSlice([_]i32{ - 1, - 2, - 3, - }) catch unreachable; + list.appendSlice(&[_]i32{ 1, 2, 3 }) catch unreachable; testing.expect(list.len == 12); testing.expect(list.pop() == 3); testing.expect(list.pop() == 2); testing.expect(list.pop() == 1); testing.expect(list.len == 9); - list.appendSlice([_]i32{}) catch unreachable; + list.appendSlice(&[_]i32{}) catch unreachable; testing.expect(list.len == 9); // can only set on indices < self.len @@ -311,18 +332,18 @@ test "std.ArrayList.orderedRemove" { try list.append(7); //remove from middle - testing.expectEqual(i32(4), list.orderedRemove(3)); - testing.expectEqual(i32(5), list.at(3)); - testing.expectEqual(usize(6), list.len); + testing.expectEqual(@as(i32, 4), list.orderedRemove(3)); + testing.expectEqual(@as(i32, 5), list.at(3)); + testing.expectEqual(@as(usize, 6), list.len); //remove from end - testing.expectEqual(i32(7), list.orderedRemove(5)); - testing.expectEqual(usize(5), list.len); + testing.expectEqual(@as(i32, 7), list.orderedRemove(5)); + testing.expectEqual(@as(usize, 5), list.len); //remove from front - testing.expectEqual(i32(1), list.orderedRemove(0)); - testing.expectEqual(i32(2), list.at(0)); - testing.expectEqual(usize(4), list.len); + testing.expectEqual(@as(i32, 1), list.orderedRemove(0)); + testing.expectEqual(@as(i32, 2), list.at(0)); + testing.expectEqual(@as(usize, 4), list.len); } test "std.ArrayList.swapRemove" { @@ -380,35 +401,6 @@ test "std.ArrayList.swapRemoveOrError" { testing.expectError(error.OutOfBounds, list.swapRemoveOrError(2)); } -test "std.ArrayList.iterator" { - var list = ArrayList(i32).init(debug.global_allocator); - defer list.deinit(); - - try list.append(1); - try list.append(2); - try list.append(3); - - var count: i32 = 0; - var it = list.iterator(); - while (it.next()) |next| { - testing.expect(next == count + 1); - count += 1; - } - - testing.expect(count == 3); - testing.expect(it.next() == null); - it.reset(); - count = 0; - while (it.next()) |next| { - testing.expect(next == count + 1); - count += 1; - if (count == 2) break; - } - - it.reset(); - testing.expect(it.next().? == 1); -} - test "std.ArrayList.insert" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); @@ -431,10 +423,7 @@ test "std.ArrayList.insertSlice" { try list.append(2); try list.append(3); try list.append(4); - try list.insertSlice(1, [_]i32{ - 9, - 8, - }); + try list.insertSlice(1, &[_]i32{ 9, 8 }); testing.expect(list.items[0] == 1); testing.expect(list.items[1] == 9); testing.expect(list.items[2] == 8); diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig index 2bc11ba3f..71f52bfd1 100644 --- a/lib/std/ascii.zig +++ b/lib/std/ascii.zig @@ -129,26 +129,26 @@ const combinedTable = init: { comptime var i = 0; inline while (i < 128) : (i += 1) { table[i] = - u8(alpha[i]) << @enumToInt(tIndex.Alpha) | - u8(hex[i]) << @enumToInt(tIndex.Hex) | - u8(space[i]) << @enumToInt(tIndex.Space) | - u8(digit[i]) << @enumToInt(tIndex.Digit) | - u8(lower[i]) << @enumToInt(tIndex.Lower) | - u8(upper[i]) << @enumToInt(tIndex.Upper) | - u8(punct[i]) << @enumToInt(tIndex.Punct) | - u8(graph[i]) << @enumToInt(tIndex.Graph); + @as(u8, alpha[i]) << @enumToInt(tIndex.Alpha) | + @as(u8, hex[i]) << @enumToInt(tIndex.Hex) | + @as(u8, space[i]) << @enumToInt(tIndex.Space) | + @as(u8, digit[i]) << @enumToInt(tIndex.Digit) | + @as(u8, lower[i]) << @enumToInt(tIndex.Lower) | + @as(u8, upper[i]) << @enumToInt(tIndex.Upper) | + @as(u8, punct[i]) << @enumToInt(tIndex.Punct) | + @as(u8, graph[i]) << @enumToInt(tIndex.Graph); } mem.set(u8, table[128..256], 0); break :init table; }; fn inTable(c: u8, t: tIndex) bool { - return (combinedTable[c] & (u8(1) << @enumToInt(t))) != 0; + return (combinedTable[c] & (@as(u8, 1) << @enumToInt(t))) != 0; } pub fn isAlNum(c: u8) bool { - return (combinedTable[c] & ((u8(1) << @enumToInt(tIndex.Alpha)) | - u8(1) << @enumToInt(tIndex.Digit))) != 0; + return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) | + @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0; } pub fn isAlpha(c: u8) bool { diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index dbc011bed..f5dbd04da 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -106,7 +106,7 @@ pub fn Queue(comptime T: type) type { pub fn dump(self: *Self) void { var stderr_file = std.io.getStdErr() catch return; const stderr = &stderr_file.outStream().stream; - const Error = @typeInfo(@typeOf(stderr)).Pointer.child.Error; + const Error = @typeInfo(@TypeOf(stderr)).Pointer.child.Error; self.dumpToStream(Error, stderr) catch return; } @@ -116,19 +116,19 @@ pub fn Queue(comptime T: type) type { fn dumpRecursive(s: *std.io.OutStream(Error), optional_node: ?*Node, indent: usize) Error!void { try s.writeByteNTimes(' ', indent); if (optional_node) |node| { - try s.print("0x{x}={}\n", @ptrToInt(node), node.data); + try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data }); try dumpRecursive(s, node.next, indent + 1); } else { - try s.print("(null)\n"); + try s.print("(null)\n", .{}); } } }; const held = self.mutex.acquire(); defer held.release(); - try stream.print("head: "); + try stream.print("head: ", .{}); try S.dumpRecursive(stream, self.head, 0); - try stream.print("tail: "); + try stream.print("tail: ", .{}); try S.dumpRecursive(stream, self.tail, 0); } }; @@ -152,8 +152,8 @@ const puts_per_thread = 500; const put_thread_count = 3; test "std.atomic.Queue" { - var plenty_of_memory = try std.heap.direct_allocator.alloc(u8, 300 * 1024); - defer std.heap.direct_allocator.free(plenty_of_memory); + var plenty_of_memory = try std.heap.page_allocator.alloc(u8, 300 * 1024); + defer std.heap.page_allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); var a = &fixed_buffer_allocator.allocator; @@ -199,7 +199,7 @@ test "std.atomic.Queue" { for (putters) |t| t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); for (getters) |t| t.wait(); @@ -207,16 +207,15 @@ test "std.atomic.Queue" { } if (context.put_sum != context.get_sum) { - std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); + std.debug.panic("failure\nput_sum:{} != get_sum:{}", .{ context.put_sum, context.get_sum }); } if (context.get_count != puts_per_thread * put_thread_count) { - std.debug.panic( - "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", + std.debug.panic("failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", .{ context.get_count, - u32(puts_per_thread), - u32(put_thread_count), - ); + @as(u32, puts_per_thread), + @as(u32, put_thread_count), + }); } } @@ -351,7 +350,7 @@ test "std.atomic.Queue dump" { \\tail: 0x{x}=1 \\ (null) \\ - , @ptrToInt(queue.head), @ptrToInt(queue.tail)); + , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) }); expect(mem.eql(u8, buffer[0..sos.pos], expected)); // Test a stream with two elements @@ -372,6 +371,6 @@ test "std.atomic.Queue dump" { \\tail: 0x{x}=2 \\ (null) \\ - , @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail)); + , .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) }); expect(mem.eql(u8, buffer[0..sos.pos], expected)); } diff --git a/lib/std/atomic/stack.zig b/lib/std/atomic/stack.zig index dd288adbf..0f67a257c 100644 --- a/lib/std/atomic/stack.zig +++ b/lib/std/atomic/stack.zig @@ -9,9 +9,9 @@ const expect = std.testing.expect; pub fn Stack(comptime T: type) type { return struct { root: ?*Node, - lock: @typeOf(lock_init), + lock: @TypeOf(lock_init), - const lock_init = if (builtin.single_threaded) {} else u8(0); + const lock_init = if (builtin.single_threaded) {} else @as(u8, 0); pub const Self = @This(); @@ -86,8 +86,8 @@ const puts_per_thread = 500; const put_thread_count = 3; test "std.atomic.stack" { - var plenty_of_memory = try std.heap.direct_allocator.alloc(u8, 300 * 1024); - defer std.heap.direct_allocator.free(plenty_of_memory); + var plenty_of_memory = try std.heap.page_allocator.alloc(u8, 300 * 1024); + defer std.heap.page_allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); var a = &fixed_buffer_allocator.allocator; @@ -128,22 +128,21 @@ test "std.atomic.stack" { for (putters) |t| t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); for (getters) |t| t.wait(); } if (context.put_sum != context.get_sum) { - std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); + std.debug.panic("failure\nput_sum:{} != get_sum:{}", .{ context.put_sum, context.get_sum }); } if (context.get_count != puts_per_thread * put_thread_count) { - std.debug.panic( - "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", + std.debug.panic("failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", .{ context.get_count, - u32(puts_per_thread), - u32(put_thread_count), - ); + @as(u32, puts_per_thread), + @as(u32, put_thread_count), + }); } } diff --git a/lib/std/bloom_filter.zig b/lib/std/bloom_filter.zig index c36c5e9df..f12f0d86a 100644 --- a/lib/std/bloom_filter.zig +++ b/lib/std/bloom_filter.zig @@ -28,7 +28,7 @@ pub fn BloomFilter( assert(n_items > 0); assert(math.isPowerOfTwo(n_items)); assert(K > 0); - const cellEmpty = if (Cell == bool) false else Cell(0); + const cellEmpty = if (Cell == bool) false else @as(Cell, 0); const cellMax = if (Cell == bool) true else math.maxInt(Cell); const n_bytes = (n_items * comptime std.meta.bitCount(Cell)) / 8; assert(n_bytes > 0); @@ -62,7 +62,7 @@ pub fn BloomFilter( } pub fn getCell(self: Self, cell: Index) Cell { - return Io.get(self.data, cell, 0); + return Io.get(&self.data, cell, 0); } pub fn incrementCell(self: *Self, cell: Index) void { @@ -70,7 +70,7 @@ pub fn BloomFilter( // skip the 'get' operation Io.set(&self.data, cell, 0, cellMax); } else { - const old = Io.get(self.data, cell, 0); + const old = Io.get(&self.data, cell, 0); if (old != cellMax) { Io.set(&self.data, cell, 0, old + 1); } @@ -120,7 +120,7 @@ pub fn BloomFilter( } else if (newsize > n_items) { var copied: usize = 0; while (copied < r.data.len) : (copied += self.data.len) { - std.mem.copy(u8, r.data[copied .. copied + self.data.len], self.data); + std.mem.copy(u8, r.data[copied .. copied + self.data.len], &self.data); } } return r; @@ -137,7 +137,7 @@ pub fn BloomFilter( var i: usize = 0; while (i < n_items) : (i += 1) { const cell = self.getCell(@intCast(Index, i)); - n += if (if (Cell == bool) cell else cell > 0) Index(1) else Index(0); + n += if (if (Cell == bool) cell else cell > 0) @as(Index, 1) else @as(Index, 0); } } return n; @@ -161,7 +161,7 @@ fn hashFunc(out: []u8, Ki: usize, in: []const u8) void { test "std.BloomFilter" { inline for ([_]type{ bool, u1, u2, u3, u4 }) |Cell| { - const emptyCell = if (Cell == bool) false else Cell(0); + const emptyCell = if (Cell == bool) false else @as(Cell, 0); const BF = BloomFilter(128 * 8, 8, Cell, builtin.endian, hashFunc); var bf = BF{}; var i: usize = undefined; @@ -170,8 +170,8 @@ test "std.BloomFilter" { while (i < BF.items) : (i += 1) { testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); } - testing.expectEqual(BF.Index(0), bf.popCount()); - testing.expectEqual(f64(0), bf.estimateItems()); + testing.expectEqual(@as(BF.Index, 0), bf.popCount()); + testing.expectEqual(@as(f64, 0), bf.estimateItems()); // fill in a few items bf.incrementCell(42); bf.incrementCell(255); @@ -196,8 +196,8 @@ test "std.BloomFilter" { while (i < BF.items) : (i += 1) { testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); } - testing.expectEqual(BF.Index(0), bf.popCount()); - testing.expectEqual(f64(0), bf.estimateItems()); + testing.expectEqual(@as(BF.Index, 0), bf.popCount()); + testing.expectEqual(@as(f64, 0), bf.estimateItems()); // Lets add a string bf.add("foo"); @@ -218,8 +218,8 @@ test "std.BloomFilter" { while (i < BF.items) : (i += 1) { testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); } - testing.expectEqual(BF.Index(0), bf.popCount()); - testing.expectEqual(f64(0), bf.estimateItems()); + testing.expectEqual(@as(BF.Index, 0), bf.popCount()); + testing.expectEqual(@as(f64, 0), bf.estimateItems()); comptime var teststrings = [_][]const u8{ "foo", @@ -246,12 +246,12 @@ test "std.BloomFilter" { inline for (teststrings) |str| { testing.expectEqual(true, larger_bf.contains(str)); } - testing.expectEqual(u12(bf.popCount()) * (4096 / 1024), larger_bf.popCount()); + testing.expectEqual(@as(u12, bf.popCount()) * (4096 / 1024), larger_bf.popCount()); const smaller_bf = bf.resize(64); inline for (teststrings) |str| { testing.expectEqual(true, smaller_bf.contains(str)); } - testing.expect(bf.popCount() <= u10(smaller_bf.popCount()) * (1024 / 64)); + testing.expect(bf.popCount() <= @as(u10, smaller_bf.popCount()) * (1024 / 64)); } } diff --git a/lib/std/buf_map.zig b/lib/std/buf_map.zig index d7aa31415..06f00608b 100644 --- a/lib/std/buf_map.zig +++ b/lib/std/buf_map.zig @@ -83,7 +83,7 @@ pub const BufMap = struct { }; test "BufMap" { - var bufmap = BufMap.init(std.heap.direct_allocator); + var bufmap = BufMap.init(std.heap.page_allocator); defer bufmap.deinit(); try bufmap.set("x", "1"); diff --git a/lib/std/buf_set.zig b/lib/std/buf_set.zig index 1a321e89c..7ac8136e7 100644 --- a/lib/std/buf_set.zig +++ b/lib/std/buf_set.zig @@ -65,7 +65,7 @@ pub const BufSet = struct { }; test "BufSet" { - var bufset = BufSet.init(std.heap.direct_allocator); + var bufset = BufSet.init(std.heap.page_allocator); defer bufset.deinit(); try bufset.put("x"); diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index bc6aa254d..6313d693b 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -17,6 +17,7 @@ pub const Buffer = struct { return self; } + /// Initialize memory to size bytes of undefined values. /// Must deinitialize with deinit. pub fn initSize(allocator: *Allocator, size: usize) !Buffer { var self = initNull(allocator); @@ -24,6 +25,14 @@ pub const Buffer = struct { return self; } + /// Initialize with capacity to hold at least num bytes. + /// Must deinitialize with deinit. + pub fn initCapacity(allocator: *Allocator, num: usize) !Buffer { + var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) }; + self.list.appendAssumeCapacity(0); + return self; + } + /// Must deinitialize with deinit. /// None of the other operations are valid until you do one of these: /// * ::replaceContents @@ -55,7 +64,7 @@ pub const Buffer = struct { return result; } - pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer { + pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer { const countSize = struct { fn countSize(size: *usize, bytes: []const u8) (error{}!void) { size.* += bytes.len; @@ -72,12 +81,12 @@ pub const Buffer = struct { self.list.deinit(); } - pub fn toSlice(self: *const Buffer) []u8 { - return self.list.toSlice()[0..self.len()]; + pub fn toSlice(self: Buffer) [:0]u8 { + return self.list.toSlice()[0..self.len() :0]; } - pub fn toSliceConst(self: *const Buffer) []const u8 { - return self.list.toSliceConst()[0..self.len()]; + pub fn toSliceConst(self: Buffer) [:0]const u8 { + return self.list.toSliceConst()[0..self.len() :0]; } pub fn shrink(self: *Buffer, new_len: usize) void { @@ -91,14 +100,21 @@ pub const Buffer = struct { self.list.items[self.len()] = 0; } - pub fn isNull(self: *const Buffer) bool { + pub fn isNull(self: Buffer) bool { return self.list.len == 0; } - pub fn len(self: *const Buffer) usize { + pub fn len(self: Buffer) usize { return self.list.len - 1; } + pub fn capacity(self: Buffer) usize { + return if (self.list.items.len > 0) + self.list.items.len - 1 + else + 0; + } + pub fn append(self: *Buffer, m: []const u8) !void { const old_len = self.len(); try self.resize(old_len + m.len); @@ -111,16 +127,16 @@ pub const Buffer = struct { self.list.toSlice()[old_len] = byte; } - pub fn eql(self: *const Buffer, m: []const u8) bool { + pub fn eql(self: Buffer, m: []const u8) bool { return mem.eql(u8, self.toSliceConst(), m); } - pub fn startsWith(self: *const Buffer, m: []const u8) bool { + pub fn startsWith(self: Buffer, m: []const u8) bool { if (self.len() < m.len) return false; return mem.eql(u8, self.list.items[0..m.len], m); } - pub fn endsWith(self: *const Buffer, m: []const u8) bool { + pub fn endsWith(self: Buffer, m: []const u8) bool { const l = self.len(); if (l < m.len) return false; const start = l - m.len; @@ -131,11 +147,6 @@ pub const Buffer = struct { try self.resize(m.len); mem.copy(u8, self.list.toSlice(), m); } - - /// For passing to C functions. - pub fn ptr(self: *const Buffer) [*]u8 { - return self.list.items.ptr; - } }; test "simple Buffer" { @@ -156,3 +167,21 @@ test "simple Buffer" { try buf2.resize(4); testing.expect(buf.startsWith(buf2.toSlice())); } + +test "Buffer.initSize" { + var buf = try Buffer.initSize(debug.global_allocator, 3); + testing.expect(buf.len() == 3); + try buf.append("hello"); + testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello")); +} + +test "Buffer.initCapacity" { + var buf = try Buffer.initCapacity(debug.global_allocator, 10); + testing.expect(buf.len() == 0); + testing.expect(buf.capacity() >= 10); + const old_cap = buf.capacity(); + try buf.append("hello"); + testing.expect(buf.len() == 5); + testing.expect(buf.capacity() == old_cap); + testing.expect(mem.eql(u8, buf.toSliceConst(), "hello")); +} diff --git a/lib/std/build.zig b/lib/std/build.zig index 3b5299f52..ad4be9e4c 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -17,6 +17,10 @@ const fmt_lib = std.fmt; const File = std.fs.File; pub const FmtStep = @import("build/fmt.zig").FmtStep; +pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; +pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep; +pub const RunStep = @import("build/run.zig").RunStep; +pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep; pub const Builder = struct { install_tls: TopLevelStep, @@ -45,6 +49,7 @@ pub const Builder = struct { dest_dir: ?[]const u8, lib_dir: []const u8, exe_dir: []const u8, + h_dir: []const u8, install_path: []const u8, search_prefixes: ArrayList([]const u8), installed_files: ArrayList(InstalledFile), @@ -53,8 +58,9 @@ pub const Builder = struct { release_mode: ?builtin.Mode, is_release: bool, override_lib_dir: ?[]const u8, - + vcpkg_root: VcpkgRoot, pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, + args: ?[][]const u8 = null, const PkgConfigError = error{ PkgConfigCrashed, @@ -145,6 +151,7 @@ pub const Builder = struct { .install_prefix = null, .lib_dir = undefined, .exe_dir = undefined, + .h_dir = undefined, .dest_dir = env_map.get("DESTDIR"), .installed_files = ArrayList(InstalledFile).init(allocator), .install_tls = TopLevelStep{ @@ -159,6 +166,8 @@ pub const Builder = struct { .is_release = false, .override_lib_dir = null, .install_path = undefined, + .vcpkg_root = VcpkgRoot{ .Unattempted = {} }, + .args = null, }; try self.top_level_steps.append(&self.install_tls); try self.top_level_steps.append(&self.uninstall_tls); @@ -185,7 +194,7 @@ pub const Builder = struct { pub fn resolveInstallPrefix(self: *Builder) void { if (self.dest_dir) |dest_dir| { const install_prefix = self.install_prefix orelse "/usr"; - self.install_path = fs.path.join(self.allocator, [_][]const u8{ dest_dir, install_prefix }) catch unreachable; + self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable; } else { const install_prefix = self.install_prefix orelse blk: { const p = self.cache_root; @@ -194,28 +203,73 @@ pub const Builder = struct { }; self.install_path = install_prefix; } - self.lib_dir = fs.path.join(self.allocator, [_][]const u8{ self.install_path, "lib" }) catch unreachable; - self.exe_dir = fs.path.join(self.allocator, [_][]const u8{ self.install_path, "bin" }) catch unreachable; + self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; + self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable; + self.h_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "include" }) catch unreachable; } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { + return LibExeObjStep.createExecutable( + self, + name, + if (root_src) |p| FileSource{ .path = p } else null, + false, + ); + } + + pub fn addExecutableFromWriteFileStep( + self: *Builder, + name: []const u8, + wfs: *WriteFileStep, + basename: []const u8, + ) *LibExeObjStep { + return LibExeObjStep.createExecutable(self, name, @as(FileSource, .{ + .write_file = .{ + .step = wfs, + .basename = basename, + }, + }), false); + } + + pub fn addExecutableSource( + self: *Builder, + name: []const u8, + root_src: ?FileSource, + ) *LibExeObjStep { return LibExeObjStep.createExecutable(self, name, root_src, false); } pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return LibExeObjStep.createObject(self, name, root_src); + const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; + return LibExeObjStep.createObject(self, name, root_src_param); + } + + pub fn addObjectFromWriteFileStep( + self: *Builder, + name: []const u8, + wfs: *WriteFileStep, + basename: []const u8, + ) *LibExeObjStep { + return LibExeObjStep.createObject(self, name, @as(FileSource, .{ + .write_file = .{ + .step = wfs, + .basename = basename, + }, + })); } pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { - return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); + const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; + return LibExeObjStep.createSharedLibrary(self, name, root_src_param, ver); } pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return LibExeObjStep.createStaticLibrary(self, name, root_src); + const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; + return LibExeObjStep.createStaticLibrary(self, name, root_src_param); } pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { - return LibExeObjStep.createTest(self, "test", root_src); + return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); } pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { @@ -231,7 +285,7 @@ pub const Builder = struct { /// To run an executable built with zig build, see `LibExeObjStep.run`. pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { assert(argv.len >= 1); - const run_step = RunStep.create(self, self.fmt("run {}", argv[0])); + const run_step = RunStep.create(self, self.fmt("run {}", .{argv[0]})); run_step.addArgs(argv); return run_step; } @@ -252,12 +306,18 @@ pub const Builder = struct { } pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; - write_file_step.* = WriteFileStep.init(self, file_path, data); + const write_file_step = self.addWriteFiles(); + write_file_step.add(file_path, data); return write_file_step; } - pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { + pub fn addWriteFiles(self: *Builder) *WriteFileStep { + const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; + write_file_step.* = WriteFileStep.init(self); + return write_file_step; + } + + pub fn addLog(self: *Builder, comptime format: []const u8, args: var) *LogStep { const data = self.fmt(format, args); const log_step = self.allocator.create(LogStep) catch unreachable; log_step.* = LogStep.init(self, data); @@ -274,6 +334,10 @@ pub const Builder = struct { return FmtStep.create(self, paths); } + pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { + return TranslateCStep.create(self, source); + } + pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { return Version{ .major = major, @@ -329,9 +393,9 @@ pub const Builder = struct { for (self.installed_files.toSliceConst()) |installed_file| { const full_path = self.getInstallPath(installed_file.dir, installed_file.path); if (self.verbose) { - warn("rm {}\n", full_path); + warn("rm {}\n", .{full_path}); } - fs.deleteTree(self.allocator, full_path) catch {}; + fs.deleteTree(full_path) catch {}; } // TODO remove empty directories @@ -339,7 +403,7 @@ pub const Builder = struct { fn makeOneStep(self: *Builder, s: *Step) anyerror!void { if (s.loop_flag) { - warn("Dependency loop detected:\n {}\n", s.name); + warn("Dependency loop detected:\n {}\n", .{s.name}); return error.DependencyLoopDetected; } s.loop_flag = true; @@ -347,7 +411,7 @@ pub const Builder = struct { for (s.dependencies.toSlice()) |dep| { self.makeOneStep(dep) catch |err| { if (err == error.DependencyLoopDetected) { - warn(" {}\n", s.name); + warn(" {}\n", .{s.name}); } return err; }; @@ -364,7 +428,7 @@ pub const Builder = struct { return &top_level_step.step; } } - warn("Cannot run step '{}' because it does not exist\n", name); + warn("Cannot run step '{}' because it does not exist\n", .{name}); return error.InvalidStepName; } @@ -377,12 +441,12 @@ pub const Builder = struct { const word = it.next() orelse break; if (mem.eql(u8, word, "-isystem")) { const include_path = it.next() orelse { - warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n"); + warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n", .{}); break; }; self.addNativeSystemIncludeDir(include_path); } else { - warn("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word); + warn("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", .{word}); break; } } @@ -396,7 +460,7 @@ pub const Builder = struct { const word = it.next() orelse break; if (mem.eql(u8, word, "-rpath")) { const rpath = it.next() orelse { - warn("Expected argument after -rpath in NIX_LDFLAGS\n"); + warn("Expected argument after -rpath in NIX_LDFLAGS\n", .{}); break; }; self.addNativeSystemRPath(rpath); @@ -404,7 +468,7 @@ pub const Builder = struct { const lib_path = word[2..]; self.addNativeSystemLibPath(lib_path); } else { - warn("Unrecognized C flag from NIX_LDFLAGS: {}\n", word); + warn("Unrecognized C flag from NIX_LDFLAGS: {}\n", .{word}); break; } } @@ -430,8 +494,8 @@ pub const Builder = struct { self.addNativeSystemIncludeDir("/usr/local/include"); self.addNativeSystemLibPath("/usr/local/lib"); - self.addNativeSystemIncludeDir(self.fmt("/usr/include/{}", triple)); - self.addNativeSystemLibPath(self.fmt("/usr/lib/{}", triple)); + self.addNativeSystemIncludeDir(self.fmt("/usr/include/{}", .{triple})); + self.addNativeSystemLibPath(self.fmt("/usr/lib/{}", .{triple})); self.addNativeSystemIncludeDir("/usr/include"); self.addNativeSystemLibPath("/usr/lib"); @@ -439,7 +503,7 @@ pub const Builder = struct { // example: on a 64-bit debian-based linux distro, with zlib installed from apt: // zlib.h is in /usr/include (added above) // libz.so.1 is in /lib/x86_64-linux-gnu (added here) - self.addNativeSystemLibPath(self.fmt("/lib/{}", triple)); + self.addNativeSystemLibPath(self.fmt("/lib/{}", .{triple})); }, } } @@ -452,7 +516,7 @@ pub const Builder = struct { .description = description, }; if ((self.available_options_map.put(name, available_option) catch unreachable) != null) { - panic("Option '{}' declared twice", name); + panic("Option '{}' declared twice", .{name}); } self.available_options_list.append(available_option) catch unreachable; @@ -467,33 +531,41 @@ pub const Builder = struct { } else if (mem.eql(u8, s, "false")) { return false; } else { - warn("Expected -D{} to be a boolean, but received '{}'\n", name, s); + warn("Expected -D{} to be a boolean, but received '{}'\n", .{ name, s }); self.markInvalidUserInput(); return null; } }, UserValue.List => { - warn("Expected -D{} to be a boolean, but received a list.\n", name); + warn("Expected -D{} to be a boolean, but received a list.\n", .{name}); self.markInvalidUserInput(); return null; }, }, - TypeId.Int => panic("TODO integer options to build script"), - TypeId.Float => panic("TODO float options to build script"), + TypeId.Int => panic("TODO integer options to build script", .{}), + TypeId.Float => panic("TODO float options to build script", .{}), TypeId.String => switch (entry.value.value) { UserValue.Flag => { - warn("Expected -D{} to be a string, but received a boolean.\n", name); + warn("Expected -D{} to be a string, but received a boolean.\n", .{name}); self.markInvalidUserInput(); return null; }, UserValue.List => { - warn("Expected -D{} to be a string, but received a list.\n", name); + warn("Expected -D{} to be a string, but received a list.\n", .{name}); self.markInvalidUserInput(); return null; }, UserValue.Scalar => |s| return s, }, - TypeId.List => panic("TODO list options to build script"), + TypeId.List => switch (entry.value.value) { + UserValue.Flag => { + warn("Expected -D{} to be a list, but received a boolean.\n", .{name}); + self.markInvalidUserInput(); + return null; + }, + UserValue.Scalar => |s| return &[_][]const u8{s}, + UserValue.List => |lst| return lst.toSliceConst(), + }, } } @@ -512,7 +584,7 @@ pub const Builder = struct { if (self.release_mode != null) { @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); } - const description = self.fmt("create a release build ({})", @tagName(mode)); + const description = self.fmt("create a release build ({})", .{@tagName(mode)}); self.is_release = self.option(bool, "release", description) orelse false; self.release_mode = if (self.is_release) mode else builtin.Mode.Debug; } @@ -535,7 +607,7 @@ pub const Builder = struct { else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { - warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); + warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)", .{}); self.markInvalidUserInput(); break :x builtin.Mode.Debug; }; @@ -598,7 +670,7 @@ pub const Builder = struct { }) catch unreachable; }, UserValue.Flag => { - warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name); + warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", .{ name, value, name }); return true; }, } @@ -619,11 +691,11 @@ pub const Builder = struct { // option already exists switch (gop.kv.value.value) { UserValue.Scalar => |s| { - warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s); + warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); return true; }, UserValue.List => { - warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name); + warn("Flag '-D{}' conflicts with multiple options of the same name.\n", .{name}); return true; }, UserValue.Flag => {}, @@ -664,7 +736,7 @@ pub const Builder = struct { while (true) { const entry = it.next() orelse break; if (!entry.value.used) { - warn("Invalid option: -D{}\n\n", entry.key); + warn("Invalid option: -D{}\n\n", .{entry.key}); self.markInvalidUserInput(); } } @@ -677,11 +749,11 @@ pub const Builder = struct { } fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| warn("cd {} && ", yes_cwd); + if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); for (argv) |arg| { - warn("{} ", arg); + warn("{} ", .{arg}); } - warn("\n"); + warn("\n", .{}); } fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { @@ -696,20 +768,20 @@ pub const Builder = struct { child.env_map = env_map; const term = child.spawnAndWait() catch |err| { - warn("Unable to spawn {}: {}\n", argv[0], @errorName(err)); + warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); return err; }; switch (term) { .Exited => |code| { if (code != 0) { - warn("The following command exited with error code {}:\n", code); + warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); return error.UncleanExit; } }, else => { - warn("The following command terminated unexpectedly:\n"); + warn("The following command terminated unexpectedly:\n", .{}); printCmd(cwd, argv); return error.UncleanExit; @@ -719,7 +791,7 @@ pub const Builder = struct { pub fn makePath(self: *Builder, path: []const u8) !void { fs.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { - warn("Unable to create path {}: {}\n", path, @errorName(err)); + warn("Unable to create path {}: {}\n", .{ path, @errorName(err) }); return err; }; } @@ -792,20 +864,20 @@ pub const Builder = struct { fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { if (self.verbose) { - warn("cp {} {} ", source_path, dest_path); + warn("cp {} {} ", .{ source_path, dest_path }); } const prev_status = try fs.updateFile(source_path, dest_path); if (self.verbose) switch (prev_status) { - .stale => warn("# installed\n"), - .fresh => warn("# up-to-date\n"), + .stale => warn("# installed\n", .{}), + .fresh => warn("# up-to-date\n", .{}), }; } fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { - return fs.path.resolve(self.allocator, [_][]const u8{ self.build_root, rel_path }) catch unreachable; + return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; } - pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { + pub fn fmt(self: *Builder, comptime format: []const u8, args: var) []u8 { return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; } @@ -817,7 +889,11 @@ pub const Builder = struct { if (fs.path.isAbsolute(name)) { return name; } - const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); + const full_path = try fs.path.join(self.allocator, &[_][]const u8{ + search_prefix, + "bin", + self.fmt("{}{}", .{ name, exe_extension }), + }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } } @@ -826,9 +902,12 @@ pub const Builder = struct { if (fs.path.isAbsolute(name)) { return name; } - var it = mem.tokenize(PATH, [_]u8{fs.path.delimiter}); + var it = mem.tokenize(PATH, &[_]u8{fs.path.delimiter}); while (it.next()) |path| { - const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); + const full_path = try fs.path.join(self.allocator, &[_][]const u8{ + path, + self.fmt("{}{}", .{ name, exe_extension }), + }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } } @@ -838,7 +917,10 @@ pub const Builder = struct { return name; } for (paths) |path| { - const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); + const full_path = try fs.path.join(self.allocator, &[_][]const u8{ + path, + self.fmt("{}{}", .{ name, exe_extension }), + }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } } @@ -853,7 +935,7 @@ pub const Builder = struct { ) ![]u8 { assert(argv.len != 0); - const max_output_size = 100 * 1024; + const max_output_size = 400 * 1024; const child = try std.ChildProcess.init(argv, self.allocator); defer child.deinit(); @@ -885,7 +967,7 @@ pub const Builder = struct { } } - pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { + pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 { assert(argv.len != 0); if (self.verbose) { @@ -895,17 +977,20 @@ pub const Builder = struct { var code: u8 = undefined; return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { error.FileNotFound => { - warn("Unable to spawn the following command: file not found\n"); + if (src_step) |s| warn("{}...", .{s.name}); + warn("Unable to spawn the following command: file not found\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ExitCodeFailure => { - warn("The following command exited with error code {}:\n", code); + if (src_step) |s| warn("{}...", .{s.name}); + warn("The following command exited with error code {}:\n", .{code}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ProcessTerminated => { - warn("The following command terminated unexpectedly:\n"); + if (src_step) |s| warn("{}...", .{s.name}); + warn("The following command terminated unexpectedly:\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, @@ -913,6 +998,10 @@ pub const Builder = struct { }; } + pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { + return self.execFromStep(argv, null); + } + pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { self.search_prefixes.append(search_prefix) catch unreachable; } @@ -922,15 +1011,16 @@ pub const Builder = struct { .Prefix => self.install_path, .Bin => self.exe_dir, .Lib => self.lib_dir, + .Header => self.h_dir, }; return fs.path.resolve( self.allocator, - [_][]const u8{ base_dir, dest_rel_path }, + &[_][]const u8{ base_dir, dest_rel_path }, ) catch unreachable; } fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg { - const stdout = try self.execAllowFail([_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); + const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); var list = ArrayList(PkgConfigPkg).init(self.allocator); var line_it = mem.tokenize(stdout, "\r\n"); while (line_it.next()) |line| { @@ -957,6 +1047,7 @@ pub const Builder = struct { error.ProcessTerminated => error.PkgConfigCrashed, error.ExitCodeFailure => error.PkgConfigFailed, error.FileNotFound => error.PkgConfigNotInstalled, + error.InvalidName => error.PkgConfigNotInstalled, error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, else => return err, }; @@ -967,432 +1058,18 @@ pub const Builder = struct { }; test "builder.findProgram compiles" { - const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache"); - _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null; + const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache"); + _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; } -pub const Version = struct { - major: u32, - minor: u32, - patch: u32, -}; +/// Deprecated. Use `builtin.Version`. +pub const Version = builtin.Version; -pub const CrossTarget = struct { - arch: builtin.Arch, - os: builtin.Os, - abi: builtin.Abi, -}; +/// Deprecated. Use `std.Target.Cross`. +pub const CrossTarget = std.Target.Cross; -pub const Target = union(enum) { - Native: void, - Cross: CrossTarget, - - pub fn zigTriple(self: Target, allocator: *Allocator) ![]u8 { - return std.fmt.allocPrint( - allocator, - "{}{}-{}-{}", - @tagName(self.getArch()), - Target.archSubArchName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - ); - } - - pub fn allocDescription(self: Target, allocator: *Allocator) ![]u8 { - // TODO is there anything else worthy of the description that is not - // already captured in the triple? - return self.zigTriple(allocator); - } - - pub fn zigTripleNoSubArch(self: Target, allocator: *Allocator) ![]u8 { - return std.fmt.allocPrint( - allocator, - "{}-{}-{}", - @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - ); - } - - pub fn linuxTriple(self: Target, allocator: *Allocator) ![]u8 { - return std.fmt.allocPrint( - allocator, - "{}-{}-{}", - @tagName(self.getArch()), - @tagName(self.getOs()), - @tagName(self.getAbi()), - ); - } - - pub fn parse(text: []const u8) !Target { - var it = mem.separate(text, "-"); - const arch_name = it.next() orelse return error.MissingArchitecture; - const os_name = it.next() orelse return error.MissingOperatingSystem; - const abi_name = it.next(); - - var cross = CrossTarget{ - .arch = try parseArchSub(arch_name), - .os = try parseOs(os_name), - .abi = undefined, - }; - cross.abi = if (abi_name) |n| try parseAbi(n) else defaultAbi(cross.arch, cross.os); - return Target{ .Cross = cross }; - } - - pub fn defaultAbi(arch: builtin.Arch, target_os: builtin.Os) builtin.Abi { - switch (arch) { - .wasm32, .wasm64 => return .musl, - else => {}, - } - switch (target_os) { - .freestanding, - .ananas, - .cloudabi, - .dragonfly, - .lv2, - .solaris, - .haiku, - .minix, - .rtems, - .nacl, - .cnk, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .zen, - .hermit, - => return .eabi, - .openbsd, - .macosx, - .freebsd, - .ios, - .tvos, - .watchos, - .fuchsia, - .kfreebsd, - .netbsd, - .hurd, - => return .gnu, - .windows, - .uefi, - => return .msvc, - .linux, - .wasi, - .emscripten, - => return .musl, - } - } - - pub const ParseArchSubError = error{ - UnknownArchitecture, - UnknownSubArchitecture, - }; - - pub fn parseArchSub(text: []const u8) ParseArchSubError!builtin.Arch { - const info = @typeInfo(builtin.Arch); - inline for (info.Union.fields) |field| { - if (mem.eql(u8, text, field.name)) { - if (field.field_type == void) { - return (builtin.Arch)(@field(builtin.Arch, field.name)); - } else { - const sub_info = @typeInfo(field.field_type); - inline for (sub_info.Enum.fields) |sub_field| { - const combined = field.name ++ sub_field.name; - if (mem.eql(u8, text, combined)) { - return @unionInit(builtin.Arch, field.name, @field(field.field_type, sub_field.name)); - } - } - return error.UnknownSubArchitecture; - } - } - } - return error.UnknownArchitecture; - } - - pub fn parseOs(text: []const u8) !builtin.Os { - const info = @typeInfo(builtin.Os); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(builtin.Os, field.name); - } - } - return error.UnknownOperatingSystem; - } - - pub fn parseAbi(text: []const u8) !builtin.Abi { - const info = @typeInfo(builtin.Abi); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(builtin.Abi, field.name); - } - } - return error.UnknownApplicationBinaryInterface; - } - - fn archSubArchName(arch: builtin.Arch) []const u8 { - return switch (arch) { - .arm => |sub| @tagName(sub), - .armeb => |sub| @tagName(sub), - .thumb => |sub| @tagName(sub), - .thumbeb => |sub| @tagName(sub), - .aarch64 => |sub| @tagName(sub), - .aarch64_be => |sub| @tagName(sub), - .kalimba => |sub| @tagName(sub), - else => "", - }; - } - - pub fn subArchName(self: Target) []const u8 { - switch (self) { - .Native => return archSubArchName(builtin.arch), - .Cross => |cross| return archSubArchName(cross.arch), - } - } - - pub fn oFileExt(self: Target) []const u8 { - return switch (self.getAbi()) { - builtin.Abi.msvc => ".obj", - else => ".o", - }; - } - - pub fn exeFileExt(self: Target) []const u8 { - return switch (self.getOs()) { - .windows => ".exe", - else => "", - }; - } - - pub fn staticLibSuffix(self: Target) []const u8 { - if (self.isWasm()) { - return ".wasm"; - } - switch (self.getAbi()) { - .msvc => return ".lib", - else => return ".a", - } - } - - pub fn dynamicLibSuffix(self: Target) []const u8 { - if (self.isDarwin()) { - return ".dylib"; - } - switch (self.getOs()) { - .windows => return ".dll", - else => return ".so", - } - } - - pub fn libPrefix(self: Target) []const u8 { - if (self.isWasm()) { - return ""; - } - switch (self.getAbi()) { - .msvc => return "", - else => return "lib", - } - } - - pub fn getOs(self: Target) builtin.Os { - return switch (self) { - .Native => builtin.os, - .Cross => |t| t.os, - }; - } - - pub fn getArch(self: Target) builtin.Arch { - switch (self) { - .Native => return builtin.arch, - .Cross => |t| return t.arch, - } - } - - pub fn getAbi(self: Target) builtin.Abi { - switch (self) { - .Native => return builtin.abi, - .Cross => |t| return t.abi, - } - } - - pub fn isMinGW(self: Target) bool { - return self.isWindows() and self.isGnu(); - } - - pub fn isGnu(self: Target) bool { - return switch (self.getAbi()) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, - }; - } - - pub fn isDarwin(self: Target) bool { - return switch (self.getOs()) { - .ios, .macosx, .watchos, .tvos => true, - else => false, - }; - } - - pub fn isWindows(self: Target) bool { - return switch (self.getOs()) { - .windows => true, - else => false, - }; - } - - pub fn isLinux(self: Target) bool { - return switch (self.getOs()) { - .linux => true, - else => false, - }; - } - - pub fn isUefi(self: Target) bool { - return switch (self.getOs()) { - .uefi => true, - else => false, - }; - } - - pub fn isWasm(self: Target) bool { - return switch (self.getArch()) { - .wasm32, .wasm64 => true, - else => false, - }; - } - - pub fn isFreeBSD(self: Target) bool { - return switch (self.getOs()) { - .freebsd => true, - else => false, - }; - } - - pub fn isNetBSD(self: Target) bool { - return switch (self.getOs()) { - .netbsd => true, - else => false, - }; - } - - pub fn wantSharedLibSymLinks(self: Target) bool { - return !self.isWindows(); - } - - pub fn osRequiresLibC(self: Target) bool { - return self.isDarwin() or self.isFreeBSD() or self.isNetBSD(); - } - - pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.getArch()) { - .avr, - .msp430, - => return 16, - - .arc, - .arm, - .armeb, - .hexagon, - .le32, - .mips, - .mipsel, - .powerpc, - .r600, - .riscv32, - .sparc, - .sparcel, - .tce, - .tcele, - .thumb, - .thumbeb, - .i386, - .xcore, - .nvptx, - .amdil, - .hsail, - .spir, - .kalimba, - .shave, - .lanai, - .wasm32, - .renderscript32, - .aarch64_32, - => return 32, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc64, - .powerpc64le, - .riscv64, - .x86_64, - .nvptx64, - .le64, - .amdil64, - .hsail64, - .spir64, - .wasm64, - .renderscript64, - .amdgcn, - .bpfel, - .bpfeb, - .sparcv9, - .s390x, - => return 64, - } - } - - pub const Executor = union(enum) { - native, - qemu: []const u8, - wine: []const u8, - unavailable, - }; - - pub fn getExternalExecutor(self: Target) Executor { - if (@TagType(Target)(self) == .Native) return .native; - - // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. - if (self.getOs() == builtin.os) { - return switch (self.getArch()) { - .aarch64 => Executor{ .qemu = "qemu-aarch64" }, - .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, - .arm => Executor{ .qemu = "qemu-arm" }, - .armeb => Executor{ .qemu = "qemu-armeb" }, - .i386 => Executor{ .qemu = "qemu-i386" }, - .mips => Executor{ .qemu = "qemu-mips" }, - .mipsel => Executor{ .qemu = "qemu-mipsel" }, - .mips64 => Executor{ .qemu = "qemu-mips64" }, - .mips64el => Executor{ .qemu = "qemu-mips64el" }, - .powerpc => Executor{ .qemu = "qemu-ppc" }, - .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, - .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, - .riscv32 => Executor{ .qemu = "qemu-riscv32" }, - .riscv64 => Executor{ .qemu = "qemu-riscv64" }, - .s390x => Executor{ .qemu = "qemu-s390x" }, - .sparc => Executor{ .qemu = "qemu-sparc" }, - .x86_64 => Executor{ .qemu = "qemu-x86_64" }, - else => return .unavailable, - }; - } - - if (self.isWindows()) { - switch (self.getArchPtrBitWidth()) { - 32 => return Executor{ .wine = "wine" }, - 64 => return Executor{ .wine = "wine64" }, - else => return .unavailable, - } - } - - return .unavailable; - } -}; +/// Deprecated. Use `std.Target`. +pub const Target = std.Target; const Pkg = struct { name: []const u8, @@ -1400,7 +1077,7 @@ const Pkg = struct { }; const CSourceFile = struct { - source_path: []const u8, + source: FileSource, args: []const []const u8, }; @@ -1413,6 +1090,33 @@ fn isLibCLibrary(name: []const u8) bool { return false; } +pub const FileSource = union(enum) { + /// Relative to build root + path: []const u8, + write_file: struct { + step: *WriteFileStep, + basename: []const u8, + }, + translate_c: *TranslateCStep, + + pub fn addStepDependencies(self: FileSource, step: *Step) void { + switch (self) { + .path => {}, + .write_file => |wf| step.dependOn(&wf.step.step), + .translate_c => |tc| step.dependOn(&tc.step), + } + } + + /// Should only be called during make() + pub fn getPath(self: FileSource, builder: *Builder) []const u8 { + return switch (self) { + .path => |p| builder.pathFromRoot(p), + .write_file => |wf| wf.step.getOutputPath(wf.basename), + .translate_c => |tc| tc.getOutputPath(), + }; + } +}; + pub const LibExeObjStep = struct { step: Step, builder: *Builder, @@ -1436,6 +1140,7 @@ pub const LibExeObjStep = struct { disable_gen_h: bool, bundle_compiler_rt: bool, disable_stack_probing: bool, + disable_sanitize_c: bool, c_std: Builder.CStd, override_lib_dir: ?[]const u8, main_pkg_path: ?[]const u8, @@ -1444,7 +1149,7 @@ pub const LibExeObjStep = struct { filter: ?[]const u8, single_threaded: bool, - root_src: ?[]const u8, + root_src: ?FileSource, out_h_filename: []const u8, out_lib_filename: []const u8, out_pdb_filename: []const u8, @@ -1460,6 +1165,7 @@ pub const LibExeObjStep = struct { output_dir: ?[]const u8, need_system_paths: bool, is_linking_libc: bool = false, + vcpkg_bin_path: ?[]const u8 = null, installed_path: ?[]const u8, install_step: ?*InstallArtifactStep, @@ -1469,12 +1175,17 @@ pub const LibExeObjStep = struct { valgrind_support: ?bool = null, + link_eh_frame_hdr: bool = false, + /// Uses system Wine installation to run cross compiled Windows build artifacts. enable_wine: bool = false, /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. enable_qemu: bool = false, + /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. + enable_wasmtime: bool = false, + /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, /// this will be the directory $glibc-build-dir/install/glibcs /// Given the example of the aarch64 target, this is the directory @@ -1483,16 +1194,22 @@ pub const LibExeObjStep = struct { dynamic_linker: ?[]const u8 = null, + /// Position Independent Code + force_pic: ?bool = null, + + subsystem: ?builtin.SubSystem = null, + const LinkObject = union(enum) { StaticPath: []const u8, OtherStep: *LibExeObjStep, SystemLib: []const u8, - AssemblyFile: []const u8, + AssemblyFile: FileSource, CSourceFile: *CSourceFile, }; const IncludeDir = union(enum) { RawPath: []const u8, + RawPathSystem: []const u8, OtherStep: *LibExeObjStep, }; @@ -1503,39 +1220,46 @@ pub const LibExeObjStep = struct { Test, }; - pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { + pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, ver: Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver); return self; } - pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { + pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0)); return self; } - pub fn createObject(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { + pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } - pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, is_dynamic: bool) *LibExeObjStep { + pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0)); return self; } - pub fn createTest(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { + pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); return self; } - fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep { + fn initExtraArgs( + builder: *Builder, + name: []const u8, + root_src: ?FileSource, + kind: Kind, + is_dynamic: bool, + ver: Version, + ) LibExeObjStep { if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { - panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", name); + panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } var self = LibExeObjStep{ .strip = false, @@ -1552,9 +1276,9 @@ pub const LibExeObjStep = struct { .step = Step.init(name, builder.allocator, make), .version = ver, .out_filename = undefined, - .out_h_filename = builder.fmt("{}.h", name), + .out_h_filename = builder.fmt("{}.h", .{name}), .out_lib_filename = undefined, - .out_pdb_filename = builder.fmt("{}.pdb", name), + .out_pdb_filename = builder.fmt("{}.pdb", .{name}), .major_only_filename = undefined, .name_only_filename = undefined, .packages = ArrayList(Pkg).init(builder.allocator), @@ -1575,6 +1299,7 @@ pub const LibExeObjStep = struct { .disable_gen_h = false, .bundle_compiler_rt = false, .disable_stack_probing = false, + .disable_sanitize_c = false, .output_dir = null, .need_system_paths = false, .single_threaded = false, @@ -1582,42 +1307,55 @@ pub const LibExeObjStep = struct { .install_step = null, }; self.computeOutFileNames(); + if (root_src) |rs| rs.addStepDependencies(&self.step); return self; } fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { .Obj => { - self.out_filename = self.builder.fmt("{}{}", self.name, self.target.oFileExt()); + self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() }); }, .Exe => { - self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt()); + self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.exeFileExt() }); }, .Test => { - self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt()); + self.out_filename = self.builder.fmt("test{}", .{self.target.exeFileExt()}); }, .Lib => { if (!self.is_dynamic) { - self.out_filename = self.builder.fmt( - "{}{}{}", + self.out_filename = self.builder.fmt("{}{}{}", .{ self.target.libPrefix(), self.name, self.target.staticLibSuffix(), - ); + }); self.out_lib_filename = self.out_filename; } else { if (self.target.isDarwin()) { - self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); - self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); - self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); + self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{ + self.name, + self.version.major, + self.version.minor, + self.version.patch, + }); + self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{ + self.name, + self.version.major, + }); + self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name}); self.out_lib_filename = self.out_filename; } else if (self.target.isWindows()) { - self.out_filename = self.builder.fmt("{}.dll", self.name); - self.out_lib_filename = self.builder.fmt("{}.lib", self.name); + self.out_filename = self.builder.fmt("{}.dll", .{self.name}); + self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); } else { - self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.name, self.version.major, self.version.minor, self.version.patch); - self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); - self.name_only_filename = self.builder.fmt("lib{}.so", self.name); + self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{ + self.name, + self.version.major, + self.version.minor, + self.version.patch, + }); + self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, self.version.major }); + self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name}); self.out_lib_filename = self.out_filename; } } @@ -1670,8 +1408,13 @@ pub const LibExeObjStep = struct { // It doesn't have to be native. We catch that if you actually try to run it. // Consider that this is declarative; the run step may not be run unless a user // option is supplied. - const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", exe.step.name)); + const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", .{exe.step.name})); run_step.addArtifactArg(exe); + + if (exe.vcpkg_bin_path) |path| { + run_step.addPathDir(path); + } + return run_step; } @@ -1781,7 +1524,7 @@ pub const LibExeObjStep = struct { }; var code: u8 = undefined; - const stdout = if (self.builder.execAllowFail([_][]const u8{ + const stdout = if (self.builder.execAllowFail(&[_][]const u8{ "pkg-config", pkg_name, "--cflags", @@ -1817,7 +1560,7 @@ pub const LibExeObjStep = struct { } else if (mem.eql(u8, tok, "-pthread")) { self.linkLibC(); } else if (self.builder.verbose) { - warn("Ignoring pkg-config flag '{}'\n", tok); + warn("Ignoring pkg-config flag '{}'\n", .{tok}); } } } @@ -1855,15 +1598,22 @@ pub const LibExeObjStep = struct { } pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void { + self.addCSourceFileSource(.{ + .args = args, + .source = .{ .path = file }, + }); + } + + pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; - const args_copy = self.builder.allocator.alloc([]u8, args.len) catch unreachable; - for (args) |arg, i| { + + const args_copy = self.builder.allocator.alloc([]u8, source.args.len) catch unreachable; + for (source.args) |arg, i| { args_copy[i] = self.builder.dupe(arg); } - c_source_file.* = CSourceFile{ - .source_path = self.builder.dupe(file), - .args = args_copy, - }; + + c_source_file.* = source; + c_source_file.args = args_copy; self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable; } @@ -1901,7 +1651,7 @@ pub const LibExeObjStep = struct { pub fn getOutputPath(self: *LibExeObjStep) []const u8 { return fs.path.join( self.builder.allocator, - [_][]const u8{ self.output_dir.?, self.out_filename }, + &[_][]const u8{ self.output_dir.?, self.out_filename }, ) catch unreachable; } @@ -1911,7 +1661,7 @@ pub const LibExeObjStep = struct { assert(self.kind == Kind.Lib); return fs.path.join( self.builder.allocator, - [_][]const u8{ self.output_dir.?, self.out_lib_filename }, + &[_][]const u8{ self.output_dir.?, self.out_lib_filename }, ) catch unreachable; } @@ -1922,7 +1672,7 @@ pub const LibExeObjStep = struct { assert(!self.disable_gen_h); return fs.path.join( self.builder.allocator, - [_][]const u8{ self.output_dir.?, self.out_h_filename }, + &[_][]const u8{ self.output_dir.?, self.out_h_filename }, ) catch unreachable; } @@ -1932,7 +1682,7 @@ pub const LibExeObjStep = struct { assert(self.target.isWindows() or self.target.isUefi()); return fs.path.join( self.builder.allocator, - [_][]const u8{ self.output_dir.?, self.out_pdb_filename }, + &[_][]const u8{ self.output_dir.?, self.out_pdb_filename }, ) catch unreachable; } @@ -1940,6 +1690,20 @@ pub const LibExeObjStep = struct { self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable; } + pub fn addAssemblyFileFromWriteFileStep(self: *LibExeObjStep, wfs: *WriteFileStep, basename: []const u8) void { + self.addAssemblyFileSource(.{ + .write_file = .{ + .step = wfs, + .basename = self.builder.dupe(basename), + }, + }); + } + + pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { + self.link_objects.append(LinkObject{ .AssemblyFile = source }) catch unreachable; + source.addStepDependencies(&self.step); + } + pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable; } @@ -1951,7 +1715,11 @@ pub const LibExeObjStep = struct { pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; - out.print("pub const {} = {};\n", name, value) catch unreachable; + out.print("pub const {} = {};\n", .{ name, value }) catch unreachable; + } + + pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { + self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable; } pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { @@ -1973,6 +1741,43 @@ pub const LibExeObjStep = struct { }) catch unreachable; } + /// If Vcpkg was found on the system, it will be added to include and lib + /// paths for the specified target. + pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: VcpkgLinkage) !void { + // Ideally in the Unattempted case we would call the function recursively + // after findVcpkgRoot and have only one switch statement, but the compiler + // cannot resolve the error set. + switch (self.builder.vcpkg_root) { + .Unattempted => { + self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| + VcpkgRoot{ .Found = root } + else + .NotFound; + }, + .NotFound => return error.VcpkgNotFound, + .Found => {}, + } + + switch (self.builder.vcpkg_root) { + .Unattempted => unreachable, + .NotFound => return error.VcpkgNotFound, + .Found => |root| { + const allocator = self.builder.allocator; + const triplet = try Target.vcpkgTriplet(allocator, self.target, linkage); + defer self.builder.allocator.free(triplet); + + const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" }); + errdefer allocator.free(include_path); + try self.include_dirs.append(IncludeDir{ .RawPath = include_path }); + + const lib_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "lib" }); + try self.lib_paths.append(lib_path); + + self.vcpkg_bin_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "bin" }); + }, + } + } + pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { assert(self.kind == Kind.Test); self.exec_cmd_args = args; @@ -2009,7 +1814,7 @@ pub const LibExeObjStep = struct { const builder = self.builder; if (self.root_src == null and self.link_objects.len == 0) { - warn("{}: linker needs 1 or more objects to link\n", self.step.name); + warn("{}: linker needs 1 or more objects to link\n", .{self.step.name}); return error.NeedAnObject; } @@ -2026,25 +1831,23 @@ pub const LibExeObjStep = struct { }; zig_args.append(cmd) catch unreachable; - if (self.root_src) |root_src| { - zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; - } + if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); for (self.link_objects.toSlice()) |link_object| { switch (link_object) { - LinkObject.StaticPath => |static_path| { + .StaticPath => |static_path| { try zig_args.append("--object"); try zig_args.append(builder.pathFromRoot(static_path)); }, - LinkObject.OtherStep => |other| switch (other.kind) { - LibExeObjStep.Kind.Exe => unreachable, - LibExeObjStep.Kind.Test => unreachable, - LibExeObjStep.Kind.Obj => { + .OtherStep => |other| switch (other.kind) { + .Exe => unreachable, + .Test => unreachable, + .Obj => { try zig_args.append("--object"); try zig_args.append(other.getOutputPath()); }, - LibExeObjStep.Kind.Lib => { + .Lib => { if (!other.is_dynamic or self.target.isWindows()) { try zig_args.append("--object"); try zig_args.append(other.getOutputLibPath()); @@ -2060,20 +1863,20 @@ pub const LibExeObjStep = struct { } }, }, - LinkObject.SystemLib => |name| { + .SystemLib => |name| { try zig_args.append("--library"); try zig_args.append(name); }, - LinkObject.AssemblyFile => |asm_file| { + .AssemblyFile => |asm_file| { try zig_args.append("--c-source"); - try zig_args.append(builder.pathFromRoot(asm_file)); + try zig_args.append(asm_file.getPath(builder)); }, - LinkObject.CSourceFile => |c_source_file| { + .CSourceFile => |c_source_file| { try zig_args.append("--c-source"); for (c_source_file.args) |arg| { try zig_args.append(arg); } - try zig_args.append(self.builder.pathFromRoot(c_source_file.source_path)); + try zig_args.append(c_source_file.source.getPath(builder)); }, } } @@ -2081,7 +1884,7 @@ pub const LibExeObjStep = struct { if (self.build_options_contents.len() > 0) { const build_options_file = try fs.path.join( builder.allocator, - [_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) }, + &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, ); try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); try zig_args.append("--pkg-begin"); @@ -2109,7 +1912,10 @@ pub const LibExeObjStep = struct { if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; if (self.strip) { - zig_args.append("--strip") catch unreachable; + try zig_args.append("--strip"); + } + if (self.link_eh_frame_hdr) { + try zig_args.append("--eh-frame-hdr"); } if (self.single_threaded) { @@ -2136,13 +1942,13 @@ pub const LibExeObjStep = struct { if (self.kind == Kind.Lib and self.is_dynamic) { zig_args.append("--ver-major") catch unreachable; - zig_args.append(builder.fmt("{}", self.version.major)) catch unreachable; + zig_args.append(builder.fmt("{}", .{self.version.major})) catch unreachable; zig_args.append("--ver-minor") catch unreachable; - zig_args.append(builder.fmt("{}", self.version.minor)) catch unreachable; + zig_args.append(builder.fmt("{}", .{self.version.minor})) catch unreachable; zig_args.append("--ver-patch") catch unreachable; - zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; + zig_args.append(builder.fmt("{}", .{self.version.patch})) catch unreachable; } if (self.is_dynamic) { try zig_args.append("-dynamic"); @@ -2156,10 +1962,13 @@ pub const LibExeObjStep = struct { if (self.disable_stack_probing) { try zig_args.append("-fno-stack-check"); } + if (self.disable_sanitize_c) { + try zig_args.append("-fno-sanitize-c"); + } switch (self.target) { - Target.Native => {}, - Target.Cross => { + .Native => {}, + .Cross => { try zig_args.append("-target"); try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable); }, @@ -2167,7 +1976,7 @@ pub const LibExeObjStep = struct { if (self.target_glibc) |ver| { try zig_args.append("-target-glibc"); - try zig_args.append(builder.fmt("{}.{}.{}", ver.major, ver.minor, ver.patch)); + try zig_args.append(builder.fmt("{}.{}.{}", .{ ver.major, ver.minor, ver.patch })); } if (self.linker_script) |linker_script| { @@ -2205,7 +2014,7 @@ pub const LibExeObjStep = struct { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); if (glibc_dir_arg) |dir| { - const full_dir = try fs.path.join(builder.allocator, [_][]const u8{ + const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{ dir, try self.target.linuxTriple(builder.allocator), }); @@ -2222,6 +2031,11 @@ pub const LibExeObjStep = struct { try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); }, + .wasmtime => |bin_name| if (self.enable_wasmtime) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd-bin"); + }, } for (self.packages.toSliceConst()) |pkg| { zig_args.append("--pkg-begin") catch unreachable; @@ -2232,11 +2046,15 @@ pub const LibExeObjStep = struct { for (self.include_dirs.toSliceConst()) |include_dir| { switch (include_dir) { - IncludeDir.RawPath => |include_path| { + .RawPath => |include_path| { + try zig_args.append("-I"); + try zig_args.append(self.builder.pathFromRoot(include_path)); + }, + .RawPathSystem => |include_path| { try zig_args.append("-isystem"); try zig_args.append(self.builder.pathFromRoot(include_path)); }, - IncludeDir.OtherStep => |other| { + .OtherStep => |other| { const h_path = other.getOutputHPath(); try zig_args.append("-isystem"); try zig_args.append(fs.path.dirname(h_path).?); @@ -2309,17 +2127,39 @@ pub const LibExeObjStep = struct { try zig_args.append(builder.pathFromRoot(dir)); } + if (self.force_pic) |pic| { + if (pic) { + try zig_args.append("-fPIC"); + } else { + try zig_args.append("-fno-PIC"); + } + } + + if (self.subsystem) |subsystem| { + try zig_args.append("--subsystem"); + try zig_args.append(switch (subsystem) { + .Console => "console", + .Windows => "windows", + .Posix => "posix", + .Native => "native", + .EfiApplication => "efi_application", + .EfiBootServiceDriver => "efi_boot_service_driver", + .EfiRom => "efi_rom", + .EfiRuntimeDriver => "efi_runtime_driver", + }); + } + if (self.kind == Kind.Test) { try builder.spawnChild(zig_args.toSliceConst()); } else { try zig_args.append("--cache"); try zig_args.append("on"); - const output_path_nl = try builder.exec(zig_args.toSliceConst()); + const output_path_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step); const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); if (self.output_dir) |output_dir| { - const full_dest = try fs.path.join(builder.allocator, [_][]const u8{ + const full_dest = try fs.path.join(builder.allocator, &[_][]const u8{ output_dir, fs.path.basename(output_path), }); @@ -2335,126 +2175,13 @@ pub const LibExeObjStep = struct { } }; -pub const RunStep = struct { - step: Step, - builder: *Builder, - - /// See also addArg and addArgs to modifying this directly - argv: ArrayList(Arg), - - /// Set this to modify the current working directory - cwd: ?[]const u8, - - /// Override this field to modify the environment, or use setEnvironmentVariable - env_map: ?*BufMap, - - pub const Arg = union(enum) { - Artifact: *LibExeObjStep, - Bytes: []u8, - }; - - pub fn create(builder: *Builder, name: []const u8) *RunStep { - const self = builder.allocator.create(RunStep) catch unreachable; - self.* = RunStep{ - .builder = builder, - .step = Step.init(name, builder.allocator, make), - .argv = ArrayList(Arg).init(builder.allocator), - .cwd = null, - .env_map = null, - }; - return self; - } - - pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { - self.argv.append(Arg{ .Artifact = artifact }) catch unreachable; - self.step.dependOn(&artifact.step); - } - - pub fn addArg(self: *RunStep, arg: []const u8) void { - self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable; - } - - pub fn addArgs(self: *RunStep, args: []const []const u8) void { - for (args) |arg| { - self.addArg(arg); - } - } - - pub fn clearEnvironment(self: *RunStep) void { - const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; - new_env_map.* = BufMap.init(self.builder.allocator); - self.env_map = new_env_map; - } - - pub fn addPathDir(self: *RunStep, search_path: []const u8) void { - const PATH = if (std.os.windows.is_the_target) "Path" else "PATH"; - const env_map = self.getEnvMap(); - const prev_path = env_map.get(PATH) orelse { - env_map.set(PATH, search_path) catch unreachable; - return; - }; - const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", prev_path, search_path); - env_map.set(PATH, new_path) catch unreachable; - } - - pub fn getEnvMap(self: *RunStep) *BufMap { - return self.env_map orelse { - const env_map = self.builder.allocator.create(BufMap) catch unreachable; - env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; - self.env_map = env_map; - return env_map; - }; - } - - pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { - const env_map = self.getEnvMap(); - env_map.set(key, value) catch unreachable; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(RunStep, "step", step); - - const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; - - var argv = ArrayList([]const u8).init(self.builder.allocator); - for (self.argv.toSlice()) |arg| { - switch (arg) { - Arg.Bytes => |bytes| try argv.append(bytes), - Arg.Artifact => |artifact| { - if (artifact.target.isWindows()) { - // On Windows we don't have rpaths so we have to add .dll search paths to PATH - self.addPathForDynLibs(artifact); - } - const executable_path = artifact.installed_path orelse artifact.getOutputPath(); - try argv.append(executable_path); - }, - } - } - - return self.builder.spawnChildEnvMap(cwd, self.env_map orelse self.builder.env_map, argv.toSliceConst()); - } - - fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { - for (artifact.link_objects.toSliceConst()) |link_object| { - switch (link_object) { - LibExeObjStep.LinkObject.OtherStep => |other| { - if (other.target.isWindows() and other.isDynamicLibrary()) { - self.addPathDir(fs.path.dirname(other.getOutputPath()).?); - self.addPathForDynLibs(other); - } - }, - else => {}, - } - } - } -}; - const InstallArtifactStep = struct { step: Step, builder: *Builder, artifact: *LibExeObjStep, dest_dir: InstallDir, pdb_dir: ?InstallDir, + h_dir: ?InstallDir, const Self = @This(); @@ -2464,13 +2191,13 @@ const InstallArtifactStep = struct { const self = builder.allocator.create(Self) catch unreachable; self.* = Self{ .builder = builder, - .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), + .step = Step.init(builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make), .artifact = artifact, .dest_dir = switch (artifact.kind) { .Obj => unreachable, .Test => unreachable, - .Exe => InstallDir.Bin, - .Lib => InstallDir.Lib, + .Exe => .Bin, + .Lib => .Lib, }, .pdb_dir = if (artifact.producesPdbFile()) blk: { if (artifact.kind == .Exe) { @@ -2479,6 +2206,7 @@ const InstallArtifactStep = struct { break :blk InstallDir.Lib; } } else null, + .h_dir = if (artifact.kind == .Lib and !artifact.disable_gen_h) .Header else null, }; self.step.dependOn(&artifact.step); artifact.install_step = self; @@ -2487,10 +2215,16 @@ const InstallArtifactStep = struct { if (self.artifact.isDynamicLibrary()) { builder.pushInstalledFile(.Lib, artifact.major_only_filename); builder.pushInstalledFile(.Lib, artifact.name_only_filename); + if (self.artifact.target.isWindows()) { + builder.pushInstalledFile(.Lib, artifact.out_lib_filename); + } } if (self.pdb_dir) |pdb_dir| { builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); } + if (self.h_dir) |h_dir| { + builder.pushInstalledFile(h_dir, artifact.out_h_filename); + } return self; } @@ -2500,13 +2234,17 @@ const InstallArtifactStep = struct { const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); try builder.updateFile(self.artifact.getOutputPath(), full_dest_path); - if (self.artifact.isDynamicLibrary()) { + if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) { try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename); } if (self.pdb_dir) |pdb_dir| { const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); try builder.updateFile(self.artifact.getOutputPdbPath(), full_pdb_path); } + if (self.h_dir) |h_dir| { + const full_pdb_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); + try builder.updateFile(self.artifact.getOutputHPath(), full_pdb_path); + } self.artifact.installed_path = full_dest_path; } }; @@ -2527,7 +2265,7 @@ pub const InstallFileStep = struct { builder.pushInstalledFile(dir, dest_rel_path); return InstallFileStep{ .builder = builder, - .step = Step.init(builder.fmt("install {}", src_path), builder.allocator, make), + .step = Step.init(builder.fmt("install {}", .{src_path}), builder.allocator, make), .src_path = src_path, .dir = dir, .dest_rel_path = dest_rel_path, @@ -2561,7 +2299,7 @@ pub const InstallDirStep = struct { builder.pushInstalledFile(options.install_dir, options.install_subdir); return InstallDirStep{ .builder = builder, - .step = Step.init(builder.fmt("install {}/", options.source_dir), builder.allocator, make), + .step = Step.init(builder.fmt("install {}/", .{options.source_dir}), builder.allocator, make), .options = options, }; } @@ -2579,7 +2317,7 @@ pub const InstallDirStep = struct { }; const rel_path = entry.path[full_src_dir.len + 1 ..]; - const dest_path = try fs.path.join(self.builder.allocator, [_][]const u8{ dest_prefix, rel_path }); + const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path }); switch (entry.kind) { .Directory => try fs.makePath(self.builder.allocator, dest_path), .File => try self.builder.updateFile(entry.path, dest_path), @@ -2589,36 +2327,6 @@ pub const InstallDirStep = struct { } }; -pub const WriteFileStep = struct { - step: Step, - builder: *Builder, - file_path: []const u8, - data: []const u8, - - pub fn init(builder: *Builder, file_path: []const u8, data: []const u8) WriteFileStep { - return WriteFileStep{ - .builder = builder, - .step = Step.init(builder.fmt("writefile {}", file_path), builder.allocator, make), - .file_path = file_path, - .data = data, - }; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(WriteFileStep, "step", step); - const full_path = self.builder.pathFromRoot(self.file_path); - const full_path_dir = fs.path.dirname(full_path) orelse "."; - fs.makePath(self.builder.allocator, full_path_dir) catch |err| { - warn("unable to make path {}: {}\n", full_path_dir, @errorName(err)); - return err; - }; - io.writeFile(full_path, self.data) catch |err| { - warn("unable to write {}: {}\n", full_path, @errorName(err)); - return err; - }; - } -}; - pub const LogStep = struct { step: Step, builder: *Builder, @@ -2627,14 +2335,14 @@ pub const LogStep = struct { pub fn init(builder: *Builder, data: []const u8) LogStep { return LogStep{ .builder = builder, - .step = Step.init(builder.fmt("log {}", data), builder.allocator, make), + .step = Step.init(builder.fmt("log {}", .{data}), builder.allocator, make), .data = data, }; } fn make(step: *Step) anyerror!void { const self = @fieldParentPtr(LogStep, "step", step); - warn("{}", self.data); + warn("{}", .{self.data}); } }; @@ -2646,7 +2354,7 @@ pub const RemoveDirStep = struct { pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, - .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make), + .step = Step.init(builder.fmt("RemoveDir {}", .{dir_path}), builder.allocator, make), .dir_path = dir_path, }; } @@ -2655,8 +2363,8 @@ pub const RemoveDirStep = struct { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); - fs.deleteTree(self.builder.allocator, full_path) catch |err| { - warn("Unable to remove {}: {}\n", full_path, @errorName(err)); + fs.deleteTree(full_path) catch |err| { + warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) }); return err; }; } @@ -2702,27 +2410,64 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj // sym link for libfoo.so.1 to libfoo.so.1.2.3 const major_only_path = fs.path.join( allocator, - [_][]const u8{ out_dir, filename_major_only }, + &[_][]const u8{ out_dir, filename_major_only }, ) catch unreachable; fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", major_only_path, out_basename); + warn("Unable to symlink {} -> {}\n", .{ major_only_path, out_basename }); return err; }; // sym link for libfoo.so to libfoo.so.1 const name_only_path = fs.path.join( allocator, - [_][]const u8{ out_dir, filename_name_only }, + &[_][]const u8{ out_dir, filename_name_only }, ) catch unreachable; fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); + warn("Unable to symlink {} -> {}\n", .{ name_only_path, filename_major_only }); return err; }; } +/// Returned slice must be freed by the caller. +fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { + const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); + defer allocator.free(appdata_path); + + const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); + defer allocator.free(path_file); + + const file = fs.cwd().openFile(path_file, .{}) catch return null; + defer file.close(); + + const size = @intCast(usize, try file.getEndPos()); + const vcpkg_path = try allocator.alloc(u8, size); + const size_read = try file.read(vcpkg_path); + std.debug.assert(size == size_read); + + return vcpkg_path; +} + +const VcpkgRoot = union(VcpkgRootStatus) { + Unattempted: void, + NotFound: void, + Found: []const u8, +}; + +const VcpkgRootStatus = enum { + Unattempted, + NotFound, + Found, +}; + +pub const VcpkgLinkage = enum { + Static, + Dynamic, +}; + pub const InstallDir = enum { Prefix, Lib, Bin, + Header, }; pub const InstalledFile = struct { diff --git a/lib/std/build/check_file.zig b/lib/std/build/check_file.zig new file mode 100644 index 000000000..782cdb560 --- /dev/null +++ b/lib/std/build/check_file.zig @@ -0,0 +1,52 @@ +const std = @import("../std.zig"); +const build = std.build; +const Step = build.Step; +const Builder = build.Builder; +const fs = std.fs; +const mem = std.mem; +const warn = std.debug.warn; + +pub const CheckFileStep = struct { + step: Step, + builder: *Builder, + expected_matches: []const []const u8, + source: build.FileSource, + max_bytes: usize = 20 * 1024 * 1024, + + pub fn create( + builder: *Builder, + source: build.FileSource, + expected_matches: []const []const u8, + ) *CheckFileStep { + const self = builder.allocator.create(CheckFileStep) catch unreachable; + self.* = CheckFileStep{ + .builder = builder, + .step = Step.init("CheckFile", builder.allocator, make), + .source = source, + .expected_matches = expected_matches, + }; + self.source.addStepDependencies(&self.step); + return self; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(CheckFileStep, "step", step); + + const src_path = self.source.getPath(self.builder); + const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); + + for (self.expected_matches) |expected_match| { + if (mem.indexOf(u8, contents, expected_match) == null) { + warn( + \\ + \\========= Expected to find: =================== + \\{} + \\========= But file does not contain it: ======= + \\{} + \\ + , .{ expected_match, contents }); + return error.TestFailed; + } + } + } +}; diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig new file mode 100644 index 000000000..75809bde0 --- /dev/null +++ b/lib/std/build/run.zig @@ -0,0 +1,302 @@ +const std = @import("../std.zig"); +const builtin = std.builtin; +const build = std.build; +const Step = build.Step; +const Builder = build.Builder; +const LibExeObjStep = build.LibExeObjStep; +const fs = std.fs; +const mem = std.mem; +const process = std.process; +const ArrayList = std.ArrayList; +const BufMap = std.BufMap; +const Buffer = std.Buffer; +const warn = std.debug.warn; + +const max_stdout_size = 1 * 1024 * 1024; // 1 MiB + +pub const RunStep = struct { + step: Step, + builder: *Builder, + + /// See also addArg and addArgs to modifying this directly + argv: ArrayList(Arg), + + /// Set this to modify the current working directory + cwd: ?[]const u8, + + /// Override this field to modify the environment, or use setEnvironmentVariable + env_map: ?*BufMap, + + stdout_action: StdIoAction = .inherit, + stderr_action: StdIoAction = .inherit, + + expected_exit_code: u8 = 0, + + pub const StdIoAction = union(enum) { + inherit, + ignore, + expect_exact: []const u8, + expect_matches: []const []const u8, + }; + + pub const Arg = union(enum) { + Artifact: *LibExeObjStep, + Bytes: []u8, + }; + + pub fn create(builder: *Builder, name: []const u8) *RunStep { + const self = builder.allocator.create(RunStep) catch unreachable; + self.* = RunStep{ + .builder = builder, + .step = Step.init(name, builder.allocator, make), + .argv = ArrayList(Arg).init(builder.allocator), + .cwd = null, + .env_map = null, + }; + return self; + } + + pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { + self.argv.append(Arg{ .Artifact = artifact }) catch unreachable; + self.step.dependOn(&artifact.step); + } + + pub fn addArg(self: *RunStep, arg: []const u8) void { + self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable; + } + + pub fn addArgs(self: *RunStep, args: []const []const u8) void { + for (args) |arg| { + self.addArg(arg); + } + } + + pub fn clearEnvironment(self: *RunStep) void { + const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; + new_env_map.* = BufMap.init(self.builder.allocator); + self.env_map = new_env_map; + } + + pub fn addPathDir(self: *RunStep, search_path: []const u8) void { + const env_map = self.getEnvMap(); + + var key: []const u8 = undefined; + var prev_path: ?[]const u8 = undefined; + if (builtin.os == .windows) { + key = "Path"; + prev_path = env_map.get(key); + if (prev_path == null) { + key = "PATH"; + prev_path = env_map.get(key); + } + } else { + key = "PATH"; + prev_path = env_map.get(key); + } + + if (prev_path) |pp| { + const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path }); + env_map.set(key, new_path) catch unreachable; + } else { + env_map.set(key, search_path) catch unreachable; + } + } + + pub fn getEnvMap(self: *RunStep) *BufMap { + return self.env_map orelse { + const env_map = self.builder.allocator.create(BufMap) catch unreachable; + env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; + self.env_map = env_map; + return env_map; + }; + } + + pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { + const env_map = self.getEnvMap(); + env_map.set(key, value) catch unreachable; + } + + pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { + self.stderr_action = .{ .expect_exact = bytes }; + } + + pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { + self.stdout_action = .{ .expect_exact = bytes }; + } + + fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { + return switch (action) { + .ignore => .Ignore, + .inherit => .Inherit, + .expect_exact, .expect_matches => .Pipe, + }; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(RunStep, "step", step); + + const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; + + var argv_list = ArrayList([]const u8).init(self.builder.allocator); + for (self.argv.toSlice()) |arg| { + switch (arg) { + Arg.Bytes => |bytes| try argv_list.append(bytes), + Arg.Artifact => |artifact| { + if (artifact.target.isWindows()) { + // On Windows we don't have rpaths so we have to add .dll search paths to PATH + self.addPathForDynLibs(artifact); + } + const executable_path = artifact.installed_path orelse artifact.getOutputPath(); + try argv_list.append(executable_path); + }, + } + } + + const argv = argv_list.toSliceConst(); + + const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable; + defer child.deinit(); + + child.cwd = cwd; + child.env_map = self.env_map orelse self.builder.env_map; + + child.stdin_behavior = .Ignore; + child.stdout_behavior = stdIoActionToBehavior(self.stdout_action); + child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); + + child.spawn() catch |err| { + warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + return err; + }; + + var stdout = Buffer.initNull(self.builder.allocator); + var stderr = Buffer.initNull(self.builder.allocator); + + // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). + + switch (self.stdout_action) { + .expect_exact, .expect_matches => { + var stdout_file_in_stream = child.stdout.?.inStream(); + stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; + }, + .inherit, .ignore => {}, + } + + switch (self.stdout_action) { + .expect_exact, .expect_matches => { + var stderr_file_in_stream = child.stderr.?.inStream(); + stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; + }, + .inherit, .ignore => {}, + } + + const term = child.wait() catch |err| { + warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + return err; + }; + + switch (term) { + .Exited => |code| { + if (code != self.expected_exit_code) { + warn("The following command exited with error code {} (expected {}):\n", .{ + code, + self.expected_exit_code, + }); + printCmd(cwd, argv); + return error.UncleanExit; + } + }, + else => { + warn("The following command terminated unexpectedly:\n", .{}); + printCmd(cwd, argv); + return error.UncleanExit; + }, + } + + switch (self.stderr_action) { + .inherit, .ignore => {}, + .expect_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stderr.toSliceConst())) { + warn( + \\ + \\========= Expected this stderr: ========= + \\{} + \\========= But found: ==================== + \\{} + \\ + , .{ expected_bytes, stderr.toSliceConst() }); + printCmd(cwd, argv); + return error.TestFailed; + } + }, + .expect_matches => |matches| for (matches) |match| { + if (mem.indexOf(u8, stderr.toSliceConst(), match) == null) { + warn( + \\ + \\========= Expected to find in stderr: ========= + \\{} + \\========= But stderr does not contain it: ===== + \\{} + \\ + , .{ match, stderr.toSliceConst() }); + printCmd(cwd, argv); + return error.TestFailed; + } + }, + } + + switch (self.stdout_action) { + .inherit, .ignore => {}, + .expect_exact => |expected_bytes| { + if (!mem.eql(u8, expected_bytes, stdout.toSliceConst())) { + warn( + \\ + \\========= Expected this stdout: ========= + \\{} + \\========= But found: ==================== + \\{} + \\ + , .{ expected_bytes, stdout.toSliceConst() }); + printCmd(cwd, argv); + return error.TestFailed; + } + }, + .expect_matches => |matches| for (matches) |match| { + if (mem.indexOf(u8, stdout.toSliceConst(), match) == null) { + warn( + \\ + \\========= Expected to find in stdout: ========= + \\{} + \\========= But stdout does not contain it: ===== + \\{} + \\ + , .{ match, stdout.toSliceConst() }); + printCmd(cwd, argv); + return error.TestFailed; + } + }, + } + } + + fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { + if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); + for (argv) |arg| { + warn("{} ", .{arg}); + } + warn("\n", .{}); + } + + fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { + for (artifact.link_objects.toSliceConst()) |link_object| { + switch (link_object) { + .OtherStep => |other| { + if (other.target.isWindows() and other.isDynamicLibrary()) { + self.addPathDir(fs.path.dirname(other.getOutputPath()).?); + self.addPathForDynLibs(other); + } + }, + else => {}, + } + } + } +}; diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig new file mode 100644 index 000000000..3d1bdad67 --- /dev/null +++ b/lib/std/build/translate_c.zig @@ -0,0 +1,87 @@ +const std = @import("../std.zig"); +const build = std.build; +const Step = build.Step; +const Builder = build.Builder; +const WriteFileStep = build.WriteFileStep; +const LibExeObjStep = build.LibExeObjStep; +const CheckFileStep = build.CheckFileStep; +const fs = std.fs; +const mem = std.mem; + +pub const TranslateCStep = struct { + step: Step, + builder: *Builder, + source: build.FileSource, + output_dir: ?[]const u8, + out_basename: []const u8, + target: std.Target = .Native, + + pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { + const self = builder.allocator.create(TranslateCStep) catch unreachable; + self.* = TranslateCStep{ + .step = Step.init("translate-c", builder.allocator, make), + .builder = builder, + .source = source, + .output_dir = null, + .out_basename = undefined, + }; + source.addStepDependencies(&self.step); + return self; + } + + /// Unless setOutputDir was called, this function must be called only in + /// the make step, from a step that has declared a dependency on this one. + /// To run an executable built with zig build, use `run`, or create an install step and invoke it. + pub fn getOutputPath(self: *TranslateCStep) []const u8 { + return fs.path.join( + self.builder.allocator, + &[_][]const u8{ self.output_dir.?, self.out_basename }, + ) catch unreachable; + } + + pub fn setTarget(self: *TranslateCStep, target: std.Target) void { + self.target = target; + } + + /// Creates a step to build an executable from the translated source. + pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { + return self.builder.addExecutableSource("translated_c", @as(build.FileSource, .{ .translate_c = self })); + } + + pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { + return CheckFileStep.create(self.builder, .{ .translate_c = self }, expected_matches); + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(TranslateCStep, "step", step); + + var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); + try argv_list.append(self.builder.zig_exe); + try argv_list.append("translate-c"); + try argv_list.append("-lc"); + + try argv_list.append("--cache"); + try argv_list.append("on"); + + switch (self.target) { + .Native => {}, + .Cross => { + try argv_list.append("-target"); + try argv_list.append(try self.target.zigTriple(self.builder.allocator)); + }, + } + + try argv_list.append(self.source.getPath(self.builder)); + + const output_path_nl = try self.builder.execFromStep(argv_list.toSliceConst(), &self.step); + const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); + + self.out_basename = fs.path.basename(output_path); + if (self.output_dir) |output_dir| { + const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); + try self.builder.updateFile(output_path, full_dest); + } else { + self.output_dir = fs.path.dirname(output_path).?; + } + } +}; diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig new file mode 100644 index 000000000..13d131ac6 --- /dev/null +++ b/lib/std/build/write_file.zig @@ -0,0 +1,94 @@ +const std = @import("../std.zig"); +const build = @import("../build.zig"); +const Step = build.Step; +const Builder = build.Builder; +const fs = std.fs; +const warn = std.debug.warn; +const ArrayList = std.ArrayList; + +pub const WriteFileStep = struct { + step: Step, + builder: *Builder, + output_dir: []const u8, + files: ArrayList(File), + + pub const File = struct { + basename: []const u8, + bytes: []const u8, + }; + + pub fn init(builder: *Builder) WriteFileStep { + return WriteFileStep{ + .builder = builder, + .step = Step.init("writefile", builder.allocator, make), + .files = ArrayList(File).init(builder.allocator), + .output_dir = undefined, + }; + } + + pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { + self.files.append(.{ .basename = basename, .bytes = bytes }) catch unreachable; + } + + /// Unless setOutputDir was called, this function must be called only in + /// the make step, from a step that has declared a dependency on this one. + /// To run an executable built with zig build, use `run`, or create an install step and invoke it. + pub fn getOutputPath(self: *WriteFileStep, basename: []const u8) []const u8 { + return fs.path.join( + self.builder.allocator, + &[_][]const u8{ self.output_dir, basename }, + ) catch unreachable; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(WriteFileStep, "step", step); + + // The cache is used here not really as a way to speed things up - because writing + // the data to a file would probably be very fast - but as a way to find a canonical + // location to put build artifacts. + + // If, for example, a hard-coded path was used as the location to put WriteFileStep + // files, then two WriteFileSteps executing in parallel might clobber each other. + + // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b + // directly and construct the path, and no "cache hit" detection happens; the files + // are always written. + var hash = std.crypto.Blake2b384.init(); + + // Random bytes to make WriteFileStep unique. Refresh this with + // new random bytes when WriteFileStep implementation is modified + // in a non-backwards-compatible way. + hash.update("eagVR1dYXoE7ARDP"); + for (self.files.toSliceConst()) |file| { + hash.update(file.basename); + hash.update(file.bytes); + hash.update("|"); + } + var digest: [48]u8 = undefined; + hash.final(&digest); + var hash_basename: [64]u8 = undefined; + fs.base64_encoder.encode(&hash_basename, &digest); + self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ + self.builder.cache_root, + "o", + &hash_basename, + }); + // TODO replace with something like fs.makePathAndOpenDir + fs.makePath(self.builder.allocator, self.output_dir) catch |err| { + warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); + return err; + }; + var dir = try fs.cwd().openDirTraverse(self.output_dir); + defer dir.close(); + for (self.files.toSliceConst()) |file| { + dir.writeFile(file.basename, file.bytes) catch |err| { + warn("unable to write {} into {}: {}\n", .{ + file.basename, + self.output_dir, + @errorName(err), + }); + return err; + }; + } + } +}; diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig new file mode 100644 index 000000000..dbd19fbad --- /dev/null +++ b/lib/std/builtin.zig @@ -0,0 +1,475 @@ +pub usingnamespace @import("builtin"); + +/// Deprecated: use `std.Target.Os`. +pub const Os = std.Target.Os; + +/// Deprecated: use `std.Target.Arch`. +pub const Arch = std.Target.Arch; + +/// Deprecated: use `std.Target.Abi`. +pub const Abi = std.Target.Abi; + +/// Deprecated: use `std.Target.ObjectFormat`. +pub const ObjectFormat = std.Target.ObjectFormat; + +/// Deprecated: use `std.Target.SubSystem`. +pub const SubSystem = std.Target.SubSystem; + +/// `explicit_subsystem` is missing when the subsystem is automatically detected, +/// so Zig standard library has the subsystem detection logic here. This should generally be +/// used rather than `explicit_subsystem`. +/// On non-Windows targets, this is `null`. +pub const subsystem: ?SubSystem = blk: { + if (@hasDecl(@This(), "explicit_subsystem")) break :blk explicit_subsystem; + switch (os) { + .windows => { + if (is_test) { + break :blk SubSystem.Console; + } + if (@hasDecl(root, "main") or + @hasDecl(root, "WinMain") or + @hasDecl(root, "wWinMain") or + @hasDecl(root, "WinMainCRTStartup") or + @hasDecl(root, "wWinMainCRTStartup")) + { + break :blk SubSystem.Windows; + } else { + break :blk SubSystem.Console; + } + }, + else => break :blk null, + } +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const StackTrace = struct { + index: usize, + instruction_addresses: []usize, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const GlobalLinkage = enum { + Internal, + Strong, + Weak, + LinkOnce, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const AtomicOrder = enum { + Unordered, + Monotonic, + Acquire, + Release, + AcqRel, + SeqCst, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const AtomicRmwOp = enum { + Xchg, + Add, + Sub, + And, + Nand, + Or, + Xor, + Max, + Min, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const Mode = enum { + Debug, + ReleaseSafe, + ReleaseFast, + ReleaseSmall, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const CallingConvention = enum { + Unspecified, + C, + Cold, + Naked, + Async, + Interrupt, + Signal, + Stdcall, + Fastcall, + Vectorcall, + Thiscall, + APCS, + AAPCS, + AAPCSVFP, +}; + +pub const TypeId = @TagType(TypeInfo); + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const TypeInfo = union(enum) { + Type: void, + Void: void, + Bool: void, + NoReturn: void, + Int: Int, + Float: Float, + Pointer: Pointer, + Array: Array, + Struct: Struct, + ComptimeFloat: void, + ComptimeInt: void, + Undefined: void, + Null: void, + Optional: Optional, + ErrorUnion: ErrorUnion, + ErrorSet: ErrorSet, + Enum: Enum, + Union: Union, + Fn: Fn, + BoundFn: Fn, + Opaque: void, + Frame: void, + AnyFrame: AnyFrame, + Vector: Vector, + EnumLiteral: void, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Int = struct { + is_signed: bool, + bits: comptime_int, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Float = struct { + bits: comptime_int, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Pointer = struct { + size: Size, + is_const: bool, + is_volatile: bool, + alignment: comptime_int, + child: type, + is_allowzero: bool, + + /// The type of the sentinel is the element type of the pointer, which is + /// the value of the `child` field in this struct. However there is no way + /// to refer to that type here, so we use `var`. + sentinel: var, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Size = enum { + One, + Many, + Slice, + C, + }; + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Array = struct { + len: comptime_int, + child: type, + + /// The type of the sentinel is the element type of the array, which is + /// the value of the `child` field in this struct. However there is no way + /// to refer to that type here, so we use `var`. + sentinel: var, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const ContainerLayout = enum { + Auto, + Extern, + Packed, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const StructField = struct { + name: []const u8, + offset: ?comptime_int, + field_type: type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Struct = struct { + layout: ContainerLayout, + fields: []StructField, + decls: []Declaration, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Optional = struct { + child: type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const ErrorUnion = struct { + error_set: type, + payload: type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Error = struct { + name: []const u8, + value: comptime_int, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const ErrorSet = ?[]Error; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const EnumField = struct { + name: []const u8, + value: comptime_int, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Enum = struct { + layout: ContainerLayout, + tag_type: type, + fields: []EnumField, + decls: []Declaration, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const UnionField = struct { + name: []const u8, + enum_field: ?EnumField, + field_type: type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Union = struct { + layout: ContainerLayout, + tag_type: ?type, + fields: []UnionField, + decls: []Declaration, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const FnArg = struct { + is_generic: bool, + is_noalias: bool, + arg_type: ?type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Fn = struct { + calling_convention: CallingConvention, + is_generic: bool, + is_var_args: bool, + return_type: ?type, + args: []FnArg, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const AnyFrame = struct { + child: ?type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Vector = struct { + len: comptime_int, + child: type, + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Declaration = struct { + name: []const u8, + is_pub: bool, + data: Data, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Data = union(enum) { + Type: type, + Var: type, + Fn: FnDecl, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const FnDecl = struct { + fn_type: type, + inline_type: Inline, + is_var_args: bool, + is_extern: bool, + is_export: bool, + lib_name: ?[]const u8, + return_type: type, + arg_names: [][]const u8, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Inline = enum { + Auto, + Always, + Never, + }; + }; + }; + }; +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const FloatMode = enum { + Strict, + Optimized, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const Endian = enum { + Big, + Little, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const OutputMode = enum { + Exe, + Lib, + Obj, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const LinkMode = enum { + Static, + Dynamic, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const Version = struct { + major: u32, + minor: u32, + patch: u32, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const CallOptions = struct { + modifier: Modifier = .auto, + stack: ?[]align(std.Target.stack_align) u8 = null, + + pub const Modifier = enum { + /// Equivalent to function call syntax. + auto, + + /// Equivalent to async keyword used with function call syntax. + async_kw, + + /// Prevents tail call optimization. This guarantees that the return + /// address will point to the callsite, as opposed to the callsite's + /// callsite. If the call is otherwise required to be tail-called + /// or inlined, a compile error is emitted instead. + never_tail, + + /// Guarantees that the call will not be inlined. If the call is + /// otherwise required to be inlined, a compile error is emitted instead. + never_inline, + + /// Asserts that the function call will not suspend. This allows a + /// non-async function to call an async function. + no_async, + + /// Guarantees that the call will be generated with tail call optimization. + /// If this is not possible, a compile error is emitted instead. + always_tail, + + /// Guarantees that the call will inlined at the callsite. + /// If this is not possible, a compile error is emitted instead. + always_inline, + + /// Evaluates the call at compile-time. If the call cannot be completed at + /// compile-time, a compile error is emitted instead. + compile_time, + }; +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const ExportOptions = struct { + name: []const u8, + linkage: GlobalLinkage = .Strong, + section: ?[]const u8 = null, +}; + +/// This function type is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const TestFn = struct { + name: []const u8, + func: fn () anyerror!void, +}; + +/// This function type is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const PanicFn = fn ([]const u8, ?*StackTrace) noreturn; + +/// This function is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_panic; + +/// This function is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn { + @setCold(true); + if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) { + root.os.panic(msg, error_return_trace); + unreachable; + } + switch (os) { + .freestanding => { + while (true) { + @breakpoint(); + } + }, + .wasi => { + std.debug.warn("{}", .{msg}); + std.os.abort(); + }, + .uefi => { + // TODO look into using the debug info and logging helpful messages + std.os.abort(); + }, + else => { + const first_trace_addr = @returnAddress(); + std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", .{msg}); + }, + } +} + +const std = @import("std.zig"); +const root = @import("root"); diff --git a/lib/std/c.zig b/lib/std/c.zig index 9c7ec2f0e..45d0b4db0 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -8,8 +8,16 @@ pub usingnamespace switch (builtin.os) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), - .freebsd => @import("c/freebsd.zig"), + .freebsd, .kfreebsd => @import("c/freebsd.zig"), .netbsd => @import("c/netbsd.zig"), + .dragonfly => @import("c/dragonfly.zig"), + .openbsd => @import("c/openbsd.zig"), + .haiku => @import("c/haiku.zig"), + .hermit => @import("c/hermit.zig"), + .solaris => @import("c/solaris.zig"), + .fuchsia => @import("c/fuchsia.zig"), + .minix => @import("c/minix.zig"), + .emscripten => @import("c/emscripten.zig"), else => struct {}, }; @@ -48,24 +56,21 @@ pub fn versionCheck(glibc_version: builtin.Version) type { }; } -// TODO https://github.com/ziglang/zig/issues/265 on this whole file - -pub extern "c" fn fopen(filename: [*]const u8, modes: [*]const u8) ?*FILE; +pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE; pub extern "c" fn fclose(stream: *FILE) c_int; pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; -pub extern "c" fn printf(format: [*]const u8, ...) c_int; +pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: fd_t) c_int; pub extern "c" fn close(fd: fd_t) c_int; -pub extern "c" fn @"close$NOCANCEL"(fd: fd_t) c_int; pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t; -pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int; -pub extern "c" fn openat(fd: c_int, path: [*]const u8, oflag: c_uint, ...) c_int; +pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; +pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize; pub extern "c" fn readv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint) isize; @@ -73,39 +78,41 @@ pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize; pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: u64) isize; pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) isize; pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: u64) isize; -pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; +pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int; pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize; pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) isize; pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void; pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; -pub extern "c" fn unlink(path: [*]const u8) c_int; +pub extern "c" fn unlink(path: [*:0]const u8) c_int; +pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int; pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; pub extern "c" fn pipe(fds: *[2]fd_t) c_int; pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; -pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; -pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; -pub extern "c" fn chdir(path: [*]const u8) c_int; -pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; +pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; +pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; +pub extern "c" fn chdir(path: [*:0]const u8) c_int; +pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; pub extern "c" fn dup(fd: fd_t) c_int; pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; -pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; -pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; +pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; +pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: [*]const u8) c_int; -pub extern "c" fn getenv(name: [*]const u8) ?[*]u8; +pub extern "c" fn rmdir(path: [*:0]const u8) c_int; +pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; +pub extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; -pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; +pub extern "c" fn sysctlbyname(name: [*:0]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int; @@ -114,7 +121,28 @@ pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int; pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int; pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; -pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; +pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: *c_void, optlen: *socklen_t) c_int; +pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: *c_void, optlen: socklen_t) c_int; +pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; +pub extern "c" fn sendto( + sockfd: fd_t, + buf: *const c_void, + len: usize, + flags: u32, + dest_addr: *const sockaddr, + addrlen: socklen_t, +) isize; + +pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; +pub extern fn recvfrom( + sockfd: fd_t, + noalias buf: *c_void, + len: usize, + flags: u32, + noalias src_addr: ?*sockaddr, + noalias addrlen: ?*socklen_t, +) isize; + pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; @@ -128,7 +156,11 @@ pub extern "c" fn realloc(?*c_void, usize) ?*c_void; pub extern "c" fn free(*c_void) void; pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; -pub extern "c" fn utimensat(dirfd: fd_t, pathname: [*]const u8, times: *[2]timespec, flags: u32) c_int; +// Deprecated +pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int; +pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int; + +pub extern "c" fn utimensat(dirfd: fd_t, pathname: [*:0]const u8, times: *[2]timespec, flags: u32) c_int; pub extern "c" fn futimens(fd: fd_t, times: *const [2]timespec) c_int; pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; @@ -147,3 +179,55 @@ pub extern "c" fn kevent( nevents: c_int, timeout: ?*const timespec, ) c_int; + +pub extern "c" fn getaddrinfo( + noalias node: [*:0]const u8, + noalias service: [*:0]const u8, + noalias hints: *const addrinfo, + noalias res: **addrinfo, +) c_int; + +pub extern "c" fn freeaddrinfo(res: *addrinfo) void; + +pub extern "c" fn getnameinfo( + noalias addr: *const sockaddr, + addrlen: socklen_t, + noalias host: [*]u8, + hostlen: socklen_t, + noalias serv: [*]u8, + servlen: socklen_t, + flags: u32, +) c_int; + +pub extern "c" fn gai_strerror(errcode: c_int) [*:0]const u8; + +pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int; + +pub extern "c" fn dn_expand( + msg: [*:0]const u8, + eomorig: [*:0]const u8, + comp_dn: [*:0]const u8, + exp_dn: [*:0]u8, + length: c_int, +) c_int; + +pub extern "c" fn sched_yield() c_int; + +pub const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}; +pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int; + +pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; +pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int; +pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; +pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) c_int; +pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; + +pub const pthread_t = *@OpaqueType(); +pub const FILE = @OpaqueType(); + +pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void; +pub extern "c" fn dlclose(handle: *c_void) c_int; +pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*:0]const u8) ?*c_void; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index f2e8120a0..bcb5a38ba 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -36,8 +36,8 @@ const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; /// export a weak symbol here, to be overridden by the real one. pub extern "c" var _mh_execute_header: mach_hdr = undefined; comptime { - if (std.os.darwin.is_the_target) { - @export("_mh_execute_header", _mh_execute_header, .Weak); + if (std.Target.current.isDarwin()) { + @export(_mh_execute_header, .{ .name = "_mh_execute_header", .linkage = .Weak }); } } @@ -46,13 +46,85 @@ pub const mach_header = macho.mach_header; pub const _errno = __error; +pub extern "c" fn @"close$NOCANCEL"(fd: fd_t) c_int; pub extern "c" fn mach_host_self() mach_port_t; pub extern "c" fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t; pub extern "c" fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t; pub extern "c" fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t; pub fn sigaddset(set: *sigset_t, signo: u5) void { - set.* |= u32(1) << (signo - 1); + set.* |= @as(u32, 1) << (signo - 1); } pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +/// get address to use bind() +pub const AI_PASSIVE = 0x00000001; + +/// fill ai_canonname +pub const AI_CANONNAME = 0x00000002; + +/// prevent host name resolution +pub const AI_NUMERICHOST = 0x00000004; + +/// prevent service name resolution +pub const AI_NUMERICSERV = 0x00001000; + +/// address family for hostname not supported +pub const EAI_ADDRFAMILY = 1; + +/// temporary failure in name resolution +pub const EAI_AGAIN = 2; + +/// invalid value for ai_flags +pub const EAI_BADFLAGS = 3; + +/// non-recoverable failure in name resolution +pub const EAI_FAIL = 4; + +/// ai_family not supported +pub const EAI_FAMILY = 5; + +/// memory allocation failure +pub const EAI_MEMORY = 6; + +/// no address associated with hostname +pub const EAI_NODATA = 7; + +/// hostname nor servname provided, or not known +pub const EAI_NONAME = 8; + +/// servname not supported for ai_socktype +pub const EAI_SERVICE = 9; + +/// ai_socktype not supported +pub const EAI_SOCKTYPE = 10; + +/// system error returned in errno +pub const EAI_SYSTEM = 11; + +/// invalid value for hints +pub const EAI_BADHINTS = 12; + +/// resolved protocol is unknown +pub const EAI_PROTOCOL = 13; + +/// argument buffer overflow +pub const EAI_OVERFLOW = 14; +pub const EAI_MAX = 15; + +pub const pthread_mutex_t = extern struct { + __sig: c_long = 0x32AAABA7, + __opaque: [__PTHREAD_MUTEX_SIZE__]u8 = [_]u8{0} ** __PTHREAD_MUTEX_SIZE__, +}; +pub const pthread_cond_t = extern struct { + __sig: c_long = 0x3CB0B1BB, + __opaque: [__PTHREAD_COND_SIZE__]u8 = [_]u8{0} ** __PTHREAD_COND_SIZE__, +}; +const __PTHREAD_MUTEX_SIZE__ = if (@sizeOf(usize) == 8) 56 else 40; +const __PTHREAD_COND_SIZE__ = if (@sizeOf(usize) == 8) 40 else 24; + +pub const pthread_attr_t = extern struct { + __sig: c_long, + __opaque: [56]u8, +}; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig new file mode 100644 index 000000000..a271b2e86 --- /dev/null +++ b/lib/std/c/dragonfly.zig @@ -0,0 +1,25 @@ +const std = @import("../std.zig"); +usingnamespace std.c; +extern "c" threadlocal var errno: c_int; +pub fn _errno() *c_int { + return &errno; +} + +pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; +pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; +pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; + +pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int; +pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; + +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; + +pub const pthread_attr_t = extern struct { // copied from freebsd + __size: [56]u8, + __align: c_long, +}; diff --git a/lib/std/c/emscripten.zig b/lib/std/c/emscripten.zig new file mode 100644 index 000000000..e91e1421c --- /dev/null +++ b/lib/std/c/emscripten.zig @@ -0,0 +1,8 @@ +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(4) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = 28; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 550b5a59b..95e27a7d9 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -10,3 +10,89 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize, data: ?*c_void) c_int; pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; + +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; + +/// address family for hostname not supported +pub const EAI_ADDRFAMILY = 1; + +/// name could not be resolved at this time +pub const EAI_AGAIN = 2; + +/// flags parameter had an invalid value +pub const EAI_BADFLAGS = 3; + +/// non-recoverable failure in name resolution +pub const EAI_FAIL = 4; + +/// address family not recognized +pub const EAI_FAMILY = 5; + +/// memory allocation failure +pub const EAI_MEMORY = 6; + +/// no address associated with hostname +pub const EAI_NODATA = 7; + +/// name does not resolve +pub const EAI_NONAME = 8; + +/// service not recognized for socket type +pub const EAI_SERVICE = 9; + +/// intended socket type was not recognized +pub const EAI_SOCKTYPE = 10; + +/// system error returned in errno +pub const EAI_SYSTEM = 11; + +/// invalid value for hints +pub const EAI_BADHINTS = 12; + +/// resolved protocol is unknown +pub const EAI_PROTOCOL = 13; + +/// argument buffer overflow +pub const EAI_OVERFLOW = 14; + +pub const EAI_MAX = 15; + +/// get address to use bind() +pub const AI_PASSIVE = 0x00000001; + +/// fill ai_canonname +pub const AI_CANONNAME = 0x00000002; + +/// prevent host name resolution +pub const AI_NUMERICHOST = 0x00000004; + +/// prevent service name resolution +pub const AI_NUMERICSERV = 0x00000008; + +/// valid flags for addrinfo (not a standard def, apps should not use it) +pub const AI_MASK = (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED); + +/// IPv6 and IPv4-mapped (with AI_V4MAPPED) +pub const AI_ALL = 0x00000100; + +/// accept IPv4-mapped if kernel supports +pub const AI_V4MAPPED_CFG = 0x00000200; + +/// only if any address is assigned +pub const AI_ADDRCONFIG = 0x00000400; + +/// accept IPv4-mapped IPv6 address +pub const AI_V4MAPPED = 0x00000800; + +/// special recommended flags for getipnodebyname +pub const AI_DEFAULT = (AI_V4MAPPED_CFG | AI_ADDRCONFIG); diff --git a/lib/std/c/fuchsia.zig b/lib/std/c/fuchsia.zig new file mode 100644 index 000000000..4f52b8900 --- /dev/null +++ b/lib/std/c/fuchsia.zig @@ -0,0 +1,8 @@ +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = 40; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig new file mode 100644 index 000000000..eb1e52a61 --- /dev/null +++ b/lib/std/c/haiku.zig @@ -0,0 +1,14 @@ +pub const pthread_mutex_t = extern struct { + flags: u32 = 0, + lock: i32 = 0, + unused: i32 = -42, + owner: i32 = -1, + owner_count: i32 = 0, +}; +pub const pthread_cond_t = extern struct { + flags: u32 = 0, + unused: i32 = -42, + mutex: ?*c_void = null, + waiter_count: i32 = 0, + lock: i32 = 0, +}; diff --git a/lib/std/c/hermit.zig b/lib/std/c/hermit.zig new file mode 100644 index 000000000..1730aa43a --- /dev/null +++ b/lib/std/c/hermit.zig @@ -0,0 +1,6 @@ +pub const pthread_mutex_t = extern struct { + inner: usize = ~@as(usize, 0), +}; +pub const pthread_cond_t = extern struct { + inner: usize = ~@as(usize, 0), +}; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 8936d2ab5..996947409 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -17,6 +17,41 @@ pub const _errno = switch (builtin.abi) { pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); +pub const AI_PASSIVE = 0x01; +pub const AI_CANONNAME = 0x02; +pub const AI_NUMERICHOST = 0x04; +pub const AI_V4MAPPED = 0x08; +pub const AI_ALL = 0x10; +pub const AI_ADDRCONFIG = 0x20; +pub const AI_NUMERICSERV = 0x400; + +pub const NI_NUMERICHOST = 0x01; +pub const NI_NUMERICSERV = 0x02; +pub const NI_NOFQDN = 0x04; +pub const NI_NAMEREQD = 0x08; +pub const NI_DGRAM = 0x10; +pub const NI_NUMERICSCOPE = 0x100; + +pub const EAI_BADFLAGS = -1; +pub const EAI_NONAME = -2; +pub const EAI_AGAIN = -3; +pub const EAI_FAIL = -4; +pub const EAI_FAMILY = -6; +pub const EAI_SOCKTYPE = -7; +pub const EAI_SERVICE = -8; +pub const EAI_MEMORY = -10; +pub const EAI_SYSTEM = -11; +pub const EAI_OVERFLOW = -12; + +pub const EAI_NODATA = -5; +pub const EAI_ADDRFAMILY = -9; +pub const EAI_INPROGRESS = -100; +pub const EAI_CANCELED = -101; +pub const EAI_NOTCANCELED = -102; +pub const EAI_ALLDONE = -103; +pub const EAI_INTR = -104; +pub const EAI_IDN_ENCODE = -105; + pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int; @@ -40,3 +75,35 @@ pub const dl_iterate_phdr_callback = extern fn (info: *dl_phdr_info, size: usize pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +pub extern "c" fn memfd_create(name: [*:0]const u8, flags: c_uint) c_int; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; + +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (builtin.abi) { + .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { + .aarch64 => 48, + .x86_64 => if (builtin.abi == .gnux32) 40 else 32, + .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + else => if (@sizeOf(usize) == 8) 40 else 24, + }, + else => unreachable, +}; + +pub const RTLD_LAZY = 1; +pub const RTLD_NOW = 2; +pub const RTLD_NOLOAD = 4; +pub const RTLD_NODELETE = 4096; +pub const RTLD_GLOBAL = 256; +pub const RTLD_LOCAL = 0; diff --git a/lib/std/c/minix.zig b/lib/std/c/minix.zig new file mode 100644 index 000000000..98ec087a9 --- /dev/null +++ b/lib/std/c/minix.zig @@ -0,0 +1,18 @@ +const builtin = @import("builtin"); +pub const pthread_mutex_t = extern struct { + size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, +}; +pub const pthread_cond_t = extern struct { + size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, +}; +const __SIZEOF_PTHREAD_COND_T = 48; +const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) { + .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.arch) { + .aarch64 => 48, + .x86_64 => if (builtin.abi == .gnux32) 40 else 32, + .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + else => if (@sizeOf(usize) == 8) 40 else 24, + }, + else => unreachable, +}; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 417c78db6..c253362ac 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -6,3 +6,32 @@ pub const _errno = __errno; pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +pub const pthread_mutex_t = extern struct { + ptm_magic: c_uint = 0x33330003, + ptm_errorcheck: padded_spin_t = 0, + ptm_unused: padded_spin_t = 0, + ptm_owner: usize = 0, + ptm_waiters: ?*u8 = null, + ptm_recursed: c_uint = 0, + ptm_spare2: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + ptc_magic: c_uint = 0x55550005, + ptc_lock: pthread_spin_t = 0, + ptc_waiters_first: ?*u8 = null, + ptc_waiters_last: ?*u8 = null, + ptc_mutex: ?*pthread_mutex_t = null, + ptc_private: ?*c_void = null, +}; +const pthread_spin_t = if (builtin.arch == .arm or .arch == .powerpc) c_int else u8; +const padded_spin_t = switch (builtin.arch) { + .sparc, .sparcel, .sparcv9, .i386, .x86_64, .le64 => u32, + else => spin_t, +}; + +pub const pthread_attr_t = extern struct { + pta_magic: u32, + pta_flags: c_int, + pta_private: *c_void, +}; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig new file mode 100644 index 000000000..22963b08c --- /dev/null +++ b/lib/std/c/openbsd.zig @@ -0,0 +1,6 @@ +pub const pthread_mutex_t = extern struct { + inner: ?*c_void = null, +}; +pub const pthread_cond_t = extern struct { + inner: ?*c_void = null, +}; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig new file mode 100644 index 000000000..7c70a01fc --- /dev/null +++ b/lib/std/c/solaris.zig @@ -0,0 +1,15 @@ +pub const pthread_mutex_t = extern struct { + __pthread_mutex_flag1: u16 = 0, + __pthread_mutex_flag2: u8 = 0, + __pthread_mutex_ceiling: u8 = 0, + __pthread_mutex_type: u16 = 0, + __pthread_mutex_magic: u16 = 0x4d58, + __pthread_mutex_lock: u64 = 0, + __pthread_mutex_data: u64 = 0, +}; +pub const pthread_cond_t = extern struct { + __pthread_cond_flag: u32 = 0, + __pthread_cond_type: u16 = 0, + __pthread_cond_magic: u16 = 0x4356, + __pthread_cond_data: u64 = 0, +}; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 9094f1cb5..ab99abe28 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -17,41 +17,53 @@ const TailQueue = std.TailQueue; const maxInt = std.math.maxInt; pub const ChildProcess = struct { - pub pid: if (os.windows.is_the_target) void else i32, - pub handle: if (os.windows.is_the_target) windows.HANDLE else void, - pub thread_handle: if (os.windows.is_the_target) windows.HANDLE else void, + pid: if (builtin.os == .windows) void else i32, + handle: if (builtin.os == .windows) windows.HANDLE else void, + thread_handle: if (builtin.os == .windows) windows.HANDLE else void, - pub allocator: *mem.Allocator, + allocator: *mem.Allocator, - pub stdin: ?File, - pub stdout: ?File, - pub stderr: ?File, + stdin: ?File, + stdout: ?File, + stderr: ?File, - pub term: ?(SpawnError!Term), + term: ?(SpawnError!Term), - pub argv: []const []const u8, + argv: []const []const u8, /// Leave as null to use the current env map using the supplied allocator. - pub env_map: ?*const BufMap, + env_map: ?*const BufMap, - pub stdin_behavior: StdIo, - pub stdout_behavior: StdIo, - pub stderr_behavior: StdIo, + stdin_behavior: StdIo, + stdout_behavior: StdIo, + stderr_behavior: StdIo, /// Set to change the user id when spawning the child process. - pub uid: if (os.windows.is_the_target) void else ?u32, + uid: if (builtin.os == .windows) void else ?u32, /// Set to change the group id when spawning the child process. - pub gid: if (os.windows.is_the_target) void else ?u32, + gid: if (builtin.os == .windows) void else ?u32, /// Set to change the current working directory when spawning the child process. - pub cwd: ?[]const u8, + cwd: ?[]const u8, - err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t, - llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node, + err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, + llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node, - pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError || - os.ChangeCurDirError || windows.CreateProcessError; + pub const SpawnError = error{ + OutOfMemory, + + /// POSIX-only. `StdIo.Ignore` was selected and opening `/dev/null` returned ENODEV. + NoDevice, + + /// Windows-only. One of: + /// * `cwd` was provided and it could not be re-encoded into UTF16LE, or + /// * The `PATH` or `PATHEXT` environment variable contained invalid UTF-8. + InvalidUtf8, + + /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process. + CurrentWorkingDirectoryUnlinked, + } || os.ExecveError || os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError || windows.WaitForSingleObjectError; pub const Term = union(enum) { Exited: u32, @@ -82,8 +94,8 @@ pub const ChildProcess = struct { .term = null, .env_map = null, .cwd = null, - .uid = if (os.windows.is_the_target) {} else null, - .gid = if (os.windows.is_the_target) {} else null, + .uid = if (builtin.os == .windows) {} else null, + .gid = if (builtin.os == .windows) {} else null, .stdin = null, .stdout = null, .stderr = null, @@ -102,22 +114,22 @@ pub const ChildProcess = struct { } /// On success must call `kill` or `wait`. - pub fn spawn(self: *ChildProcess) !void { - if (os.windows.is_the_target) { + pub fn spawn(self: *ChildProcess) SpawnError!void { + if (builtin.os == .windows) { return self.spawnWindows(); } else { return self.spawnPosix(); } } - pub fn spawnAndWait(self: *ChildProcess) !Term { + pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term { try self.spawn(); return self.wait(); } /// Forcibly terminates child process and then cleans up all resources. pub fn kill(self: *ChildProcess) !Term { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { return self.killWindows(1); } else { return self.killPosix(); @@ -147,7 +159,7 @@ pub const ChildProcess = struct { /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: *ChildProcess) !Term { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { return self.waitWindows(); } else { return self.waitPosix(); @@ -162,7 +174,13 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. - pub fn exec(allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, max_output_size: usize) !ExecResult { + pub fn exec( + allocator: *mem.Allocator, + argv: []const []const u8, + cwd: ?[]const u8, + env_map: ?*const BufMap, + max_output_size: usize, + ) !ExecResult { const child = try ChildProcess.init(argv, allocator); defer child.deinit(); @@ -217,9 +235,9 @@ pub const ChildProcess = struct { } fn waitUnwrappedWindows(self: *ChildProcess) !void { - const result = windows.WaitForSingleObject(self.handle, windows.INFINITE); + const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false); - self.term = (SpawnError!Term)(x: { + self.term = @as(SpawnError!Term, x: { var exit_code: windows.DWORD = undefined; if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) { break :x Term{ .Unknown = 0 }; @@ -241,7 +259,9 @@ pub const ChildProcess = struct { } fn handleWaitResult(self: *ChildProcess, status: u32) void { - self.term = self.cleanupAfterWait(status); + // TODO https://github.com/ziglang/zig/issues/3190 + var term = self.cleanupAfterWait(status); + self.term = term; } fn cleanupStreams(self: *ChildProcess) void { @@ -260,22 +280,38 @@ pub const ChildProcess = struct { } fn cleanupAfterWait(self: *ChildProcess, status: u32) !Term { - defer { - os.close(self.err_pipe[0]); - os.close(self.err_pipe[1]); - } + defer destroyPipe(self.err_pipe); - // Write maxInt(ErrInt) to the write end of the err_pipe. This is after - // waitpid, so this write is guaranteed to be after the child - // pid potentially wrote an error. This way we can do a blocking - // read on the error pipe and either get maxInt(ErrInt) (no error) or - // an error code. - try writeIntFd(self.err_pipe[1], maxInt(ErrInt)); - const err_int = try readIntFd(self.err_pipe[0]); - // Here we potentially return the fork child's error - // from the parent pid. - if (err_int != maxInt(ErrInt)) { - return @errSetCast(SpawnError, @intToError(err_int)); + if (builtin.os == .linux) { + var fd = [1]std.os.pollfd{std.os.pollfd{ + .fd = self.err_pipe[0], + .events = std.os.POLLIN, + .revents = undefined, + }}; + + // Check if the eventfd buffer stores a non-zero value by polling + // it, that's the error code returned by the child process. + _ = std.os.poll(&fd, 0) catch unreachable; + + // According to eventfd(2) the descriptro is readable if the counter + // has a value greater than 0 + if ((fd[0].revents & std.os.POLLIN) != 0) { + const err_int = try readIntFd(self.err_pipe[0]); + return @errSetCast(SpawnError, @intToError(err_int)); + } + } else { + // Write maxInt(ErrInt) to the write end of the err_pipe. This is after + // waitpid, so this write is guaranteed to be after the child + // pid potentially wrote an error. This way we can do a blocking + // read on the error pipe and either get maxInt(ErrInt) (no error) or + // an error code. + try writeIntFd(self.err_pipe[1], maxInt(ErrInt)); + const err_int = try readIntFd(self.err_pipe[0]); + // Here we potentially return the fork child's error from the parent + // pid. + if (err_int != maxInt(ErrInt)) { + return @errSetCast(SpawnError, @intToError(err_int)); + } } return statusToTerm(status); @@ -292,7 +328,7 @@ pub const ChildProcess = struct { Term{ .Unknown = status }; } - fn spawnPosix(self: *ChildProcess) !void { + fn spawnPosix(self: *ChildProcess) SpawnError!void { const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined; errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); @@ -309,7 +345,16 @@ pub const ChildProcess = struct { }; const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); - const dev_null_fd = if (any_ignore) try os.openC(c"/dev/null", os.O_RDWR, 0) else undefined; + const dev_null_fd = if (any_ignore) + os.openC("/dev/null", os.O_RDWR, 0) catch |err| switch (err) { + error.PathAlreadyExists => unreachable, + error.NoSpaceLeft => unreachable, + error.FileTooBig => unreachable, + error.DeviceBusy => unreachable, + else => |e| return e, + } + else + undefined; defer { if (any_ignore) os.close(dev_null_fd); } @@ -330,7 +375,16 @@ pub const ChildProcess = struct { // This pipe is used to communicate errors between the time of fork // and execve from the child process to the parent process. - const err_pipe = try os.pipe(); + const err_pipe = blk: { + if (builtin.os == .linux) { + const fd = try os.eventfd(0, 0); + // There's no distinction between the readable and the writeable + // end with eventfd + break :blk [2]os.fd_t{ fd, fd }; + } else { + break :blk try os.pipe(); + } + }; errdefer destroyPipe(err_pipe); const pid_result = try os.fork(); @@ -365,7 +419,8 @@ pub const ChildProcess = struct { os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); } - os.execve(self.allocator, self.argv, env_map) catch |err| forkChildErrReport(err_pipe[1], err); + const err = os.execvpe(self.allocator, self.argv, env_map); + forkChildErrReport(err_pipe[1], err); } // we are the parent @@ -402,7 +457,7 @@ pub const ChildProcess = struct { } } - fn spawnWindows(self: *ChildProcess) !void { + fn spawnWindows(self: *ChildProcess) SpawnError!void { const saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, @@ -411,11 +466,29 @@ pub const ChildProcess = struct { const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); - const nul_handle = if (any_ignore) blk: { - break :blk try windows.CreateFile("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null); - } else blk: { - break :blk undefined; - }; + // TODO use CreateFileW here since we are using a string literal for the path + const nul_handle = if (any_ignore) + windows.CreateFile( + "NUL", + windows.GENERIC_READ, + windows.FILE_SHARE_READ, + null, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ) catch |err| switch (err) { + error.SharingViolation => unreachable, // not possible for "NUL" + error.PathAlreadyExists => unreachable, // not possible for "NUL" + error.PipeBusy => unreachable, // not possible for "NUL" + error.InvalidUtf8 => unreachable, // not possible for "NUL" + error.BadPathName => unreachable, // not possible for "NUL" + error.FileNotFound => unreachable, // not possible for "NUL" + error.AccessDenied => unreachable, // not possible for "NUL" + error.NameTooLong => unreachable, // not possible for "NUL" + else => |e| return e, + } + else + undefined; defer { if (any_ignore) os.close(nul_handle); } @@ -509,9 +582,7 @@ pub const ChildProcess = struct { }; var piProcInfo: windows.PROCESS_INFORMATION = undefined; - const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null; - defer if (cwd_slice) |cwd| self.allocator.free(cwd); - const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null; + const cwd_w = if (self.cwd) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null; defer if (cwd_w) |cwd| self.allocator.free(cwd); const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; @@ -521,39 +592,56 @@ pub const ChildProcess = struct { // the cwd set in ChildProcess is in effect when choosing the executable path // to match posix semantics - const app_name = x: { + const app_path = x: { if (self.cwd) |cwd| { - const resolved = try fs.path.resolve(self.allocator, [_][]const u8{ cwd, self.argv[0] }); + const resolved = try fs.path.resolve(self.allocator, &[_][]const u8{ cwd, self.argv[0] }); defer self.allocator.free(resolved); break :x try cstr.addNullByte(self.allocator, resolved); } else { break :x try cstr.addNullByte(self.allocator, self.argv[0]); } }; - defer self.allocator.free(app_name); + defer self.allocator.free(app_path); - const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name); - defer self.allocator.free(app_name_w); + const app_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_path); + defer self.allocator.free(app_path_w); const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line); defer self.allocator.free(cmd_line_w); - windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { + windowsCreateProcess(app_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { if (no_path_err != error.FileNotFound) return no_path_err; - const PATH = try process.getEnvVarOwned(self.allocator, "PATH"); - defer self.allocator.free(PATH); - const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT"); - defer self.allocator.free(PATHEXT); + var free_path = true; + const PATH = process.getEnvVarOwned(self.allocator, "PATH") catch |err| switch (err) { + error.EnvironmentVariableNotFound => blk: { + free_path = false; + break :blk ""; + }, + else => |e| return e, + }; + defer if (free_path) self.allocator.free(PATH); + + var free_path_ext = true; + const PATHEXT = process.getEnvVarOwned(self.allocator, "PATHEXT") catch |err| switch (err) { + error.EnvironmentVariableNotFound => blk: { + free_path_ext = false; + break :blk ""; + }, + else => |e| return e, + }; + defer if (free_path_ext) self.allocator.free(PATHEXT); + + const app_name = self.argv[0]; var it = mem.tokenize(PATH, ";"); retry: while (it.next()) |search_path| { + const path_no_ext = try fs.path.join(self.allocator, &[_][]const u8{ search_path, app_name }); + defer self.allocator.free(path_no_ext); + var ext_it = mem.tokenize(PATHEXT, ";"); while (ext_it.next()) |app_ext| { - const app_basename = try mem.concat(self.allocator, u8, [_][]const u8{ app_name[0 .. app_name.len - 1], app_ext }); - defer self.allocator.free(app_basename); - - const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_basename }); + const joined_path = try mem.concat(self.allocator, u8, &[_][]const u8{ path_no_ext, app_ext }); defer self.allocator.free(joined_path); const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); @@ -613,7 +701,7 @@ pub const ChildProcess = struct { } }; -fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { +fn windowsCreateProcess(app_name: [*:0]u16, cmd_line: [*:0]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*:0]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { // TODO the docs for environment pointer say: // > A pointer to the environment block for the new process. If this parameter // > is NULL, the new process uses the environment of the calling process. @@ -710,13 +798,13 @@ fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const fn destroyPipe(pipe: [2]os.fd_t) void { os.close(pipe[0]); - os.close(pipe[1]); + if (pipe[0] != pipe[1]) os.close(pipe[1]); } // Child of fork calls this to report an error to the fork parent. // Then the child exits. fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { - writeIntFd(fd, ErrInt(@errorToInt(err))) catch {}; + writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {}; os.exit(1); } @@ -724,12 +812,12 @@ const ErrInt = @IntType(false, @sizeOf(anyerror) * 8); fn writeIntFd(fd: i32, value: ErrInt) !void { const stream = &File.openHandle(fd).outStream().stream; - stream.writeIntNative(ErrInt, value) catch return error.SystemResources; + stream.writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; } fn readIntFd(fd: i32) !ErrInt { const stream = &File.openHandle(fd).inStream().stream; - return stream.readIntNative(ErrInt) catch return error.SystemResources; + return @intCast(ErrInt, stream.readIntNative(u64) catch return error.SystemResources); } /// Caller must free result. diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 3890151d0..b1be21c4d 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -61,7 +61,7 @@ pub const Coff = struct { var magic: [2]u8 = undefined; try in.readNoEof(magic[0..]); - if (!mem.eql(u8, magic, "MZ")) + if (!mem.eql(u8, &magic, "MZ")) return error.InvalidPEMagic; // Seek to PE File Header (coff header) @@ -71,7 +71,7 @@ pub const Coff = struct { var pe_header_magic: [4]u8 = undefined; try in.readNoEof(pe_header_magic[0..]); - if (!mem.eql(u8, pe_header_magic, [_]u8{ 'P', 'E', 0, 0 })) + if (!mem.eql(u8, &pe_header_magic, &[_]u8{ 'P', 'E', 0, 0 })) return error.InvalidPEHeader; self.coff_header = CoffHeader{ @@ -163,7 +163,7 @@ pub const Coff = struct { var cv_signature: [4]u8 = undefined; // CodeView signature try in.readNoEof(cv_signature[0..]); // 'RSDS' indicates PDB70 format, used by lld. - if (!mem.eql(u8, cv_signature, "RSDS")) + if (!mem.eql(u8, &cv_signature, "RSDS")) return error.InvalidPEMagic; try in.readNoEof(self.guid[0..]); self.age = try in.readIntLittle(u32); @@ -179,7 +179,7 @@ pub const Coff = struct { if (byte != 0 and i == buffer.len) return error.NameTooLong; - return i; + return @as(usize, i); } pub fn loadSections(self: *Coff) !void { diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index b7703f2f7..922288600 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -34,10 +34,15 @@ pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; pub const X25519 = @import("crypto/x25519.zig").X25519; +const import_aes = @import("crypto/aes.zig"); +pub const AES128 = import_aes.AES128; +pub const AES256 = import_aes.AES256; + const std = @import("std.zig"); pub const randomBytes = std.os.getrandom; test "crypto" { + _ = @import("crypto/aes.zig"); _ = @import("crypto/blake2.zig"); _ = @import("crypto/chacha20.zig"); _ = @import("crypto/gimli.zig"); diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig new file mode 100644 index 000000000..1cc166f94 --- /dev/null +++ b/lib/std/crypto/aes.zig @@ -0,0 +1,649 @@ +// Based on Go stdlib implementation + +const std = @import("../std.zig"); +const mem = std.mem; +const testing = std.testing; + +// Apply sbox0 to each byte in w. +fn subw(w: u32) u32 { + return @as(u32, sbox0[w >> 24]) << 24 | @as(u32, sbox0[w >> 16 & 0xff]) << 16 | @as(u32, sbox0[w >> 8 & 0xff]) << 8 | @as(u32, sbox0[w & 0xff]); +} + +fn rotw(w: u32) u32 { + return w << 8 | w >> 24; +} + +// Encrypt one block from src into dst, using the expanded key xk. +fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { + var s0 = mem.readIntSliceBig(u32, src[0..4]); + var s1 = mem.readIntSliceBig(u32, src[4..8]); + var s2 = mem.readIntSliceBig(u32, src[8..12]); + var s3 = mem.readIntSliceBig(u32, src[12..16]); + + // First round just XORs input with key. + s0 ^= xk[0]; + s1 ^= xk[1]; + s2 ^= xk[2]; + s3 ^= xk[3]; + + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + var nr = xk.len / 4 - 2; // - 2: one above, one more below + var k: usize = 4; + var t0: u32 = undefined; + var t1: u32 = undefined; + var t2: u32 = undefined; + var t3: u32 = undefined; + var r: usize = 0; + while (r < nr) : (r += 1) { + t0 = xk[k + 0] ^ te0[@truncate(u8, s0 >> 24)] ^ te1[@truncate(u8, s1 >> 16)] ^ te2[@truncate(u8, s2 >> 8)] ^ te3[@truncate(u8, s3)]; + t1 = xk[k + 1] ^ te0[@truncate(u8, s1 >> 24)] ^ te1[@truncate(u8, s2 >> 16)] ^ te2[@truncate(u8, s3 >> 8)] ^ te3[@truncate(u8, s0)]; + t2 = xk[k + 2] ^ te0[@truncate(u8, s2 >> 24)] ^ te1[@truncate(u8, s3 >> 16)] ^ te2[@truncate(u8, s0 >> 8)] ^ te3[@truncate(u8, s1)]; + t3 = xk[k + 3] ^ te0[@truncate(u8, s3 >> 24)] ^ te1[@truncate(u8, s0 >> 16)] ^ te2[@truncate(u8, s1 >> 8)] ^ te3[@truncate(u8, s2)]; + k += 4; + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Last round uses s-box directly and XORs to produce output. + s0 = @as(u32, sbox0[t0 >> 24]) << 24 | @as(u32, sbox0[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t3 & 0xff]); + s1 = @as(u32, sbox0[t1 >> 24]) << 24 | @as(u32, sbox0[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t0 & 0xff]); + s2 = @as(u32, sbox0[t2 >> 24]) << 24 | @as(u32, sbox0[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t1 & 0xff]); + s3 = @as(u32, sbox0[t3 >> 24]) << 24 | @as(u32, sbox0[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t2 & 0xff]); + + s0 ^= xk[k + 0]; + s1 ^= xk[k + 1]; + s2 ^= xk[k + 2]; + s3 ^= xk[k + 3]; + + mem.writeIntSliceBig(u32, dst[0..4], s0); + mem.writeIntSliceBig(u32, dst[4..8], s1); + mem.writeIntSliceBig(u32, dst[8..12], s2); + mem.writeIntSliceBig(u32, dst[12..16], s3); +} + +// Decrypt one block from src into dst, using the expanded key xk. +pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { + var s0 = mem.readIntSliceBig(u32, src[0..4]); + var s1 = mem.readIntSliceBig(u32, src[4..8]); + var s2 = mem.readIntSliceBig(u32, src[8..12]); + var s3 = mem.readIntSliceBig(u32, src[12..16]); + + // First round just XORs input with key. + s0 ^= xk[0]; + s1 ^= xk[1]; + s2 ^= xk[2]; + s3 ^= xk[3]; + + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + var nr = xk.len / 4 - 2; // - 2: one above, one more below + var k: usize = 4; + var t0: u32 = undefined; + var t1: u32 = undefined; + var t2: u32 = undefined; + var t3: u32 = undefined; + var r: usize = 0; + while (r < nr) : (r += 1) { + t0 = xk[k + 0] ^ td0[@truncate(u8, s0 >> 24)] ^ td1[@truncate(u8, s3 >> 16)] ^ td2[@truncate(u8, s2 >> 8)] ^ td3[@truncate(u8, s1)]; + t1 = xk[k + 1] ^ td0[@truncate(u8, s1 >> 24)] ^ td1[@truncate(u8, s0 >> 16)] ^ td2[@truncate(u8, s3 >> 8)] ^ td3[@truncate(u8, s2)]; + t2 = xk[k + 2] ^ td0[@truncate(u8, s2 >> 24)] ^ td1[@truncate(u8, s1 >> 16)] ^ td2[@truncate(u8, s0 >> 8)] ^ td3[@truncate(u8, s3)]; + t3 = xk[k + 3] ^ td0[@truncate(u8, s3 >> 24)] ^ td1[@truncate(u8, s2 >> 16)] ^ td2[@truncate(u8, s1 >> 8)] ^ td3[@truncate(u8, s0)]; + k += 4; + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Last round uses s-box directly and XORs to produce output. + s0 = @as(u32, sbox1[t0 >> 24]) << 24 | @as(u32, sbox1[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t1 & 0xff]); + s1 = @as(u32, sbox1[t1 >> 24]) << 24 | @as(u32, sbox1[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t2 & 0xff]); + s2 = @as(u32, sbox1[t2 >> 24]) << 24 | @as(u32, sbox1[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t3 & 0xff]); + s3 = @as(u32, sbox1[t3 >> 24]) << 24 | @as(u32, sbox1[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t0 & 0xff]); + + s0 ^= xk[k + 0]; + s1 ^= xk[k + 1]; + s2 ^= xk[k + 2]; + s3 ^= xk[k + 3]; + + mem.writeIntSliceBig(u32, dst[0..4], s0); + mem.writeIntSliceBig(u32, dst[4..8], s1); + mem.writeIntSliceBig(u32, dst[8..12], s2); + mem.writeIntSliceBig(u32, dst[12..16], s3); +} + +fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize { + var n = std.math.min(dst.len, std.math.min(a.len, b.len)); + for (dst[0..n]) |_, i| { + dst[i] = a[i] ^ b[i]; + } + return n; +} + +pub const AES128 = AES(128); +pub const AES256 = AES(256); + +fn AES(comptime keysize: usize) type { + return struct { + const Self = @This(); + + const nn = (keysize / 8) + 28; + enc: [nn]u32, + dec: [nn]u32, + + pub fn init(key: [keysize / 8]u8) Self { + var ctx: Self = undefined; + expandKey(&key, ctx.enc[0..], ctx.dec[0..]); + return ctx; + } + + pub fn encrypt(ctx: Self, dst: []u8, src: []const u8) void { + encryptBlock(ctx.enc[0..], dst, src); + } + pub fn decrypt(ctx: Self, dst: []u8, src: []const u8) void { + decryptBlock(ctx.dec[0..], dst, src); + } + pub fn ctr(ctx: Self, dst: []u8, src: []const u8, iv: [16]u8) void { + std.debug.assert(dst.len >= src.len); + + var keystream: [16]u8 = undefined; + var ctrbuf = iv; + var n: usize = 0; + while (n < src.len) { + ctx.encrypt(keystream[0..], ctrbuf[0..]); + var ctr_i = std.mem.readIntSliceBig(u128, ctrbuf[0..]); + std.mem.writeIntSliceBig(u128, ctrbuf[0..], ctr_i +% 1); + + n += xorBytes(dst[n..], src[n..], &keystream); + } + } + }; +} + +test "ctr" { + // NIST SP 800-38A pp 55-58 + { + const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + const iv = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; + const in = [_]u8{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }; + const exp_out = [_]u8{ + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, + 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, + 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee, + }; + + var out: [exp_out.len]u8 = undefined; + var aes = AES128.init(key); + aes.ctr(out[0..], in[0..], iv); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } +} + +test "encrypt" { + // Appendix B + { + const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + const in = [_]u8{ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 }; + const exp_out = [_]u8{ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 }; + + var out: [exp_out.len]u8 = undefined; + var aes = AES128.init(key); + aes.encrypt(out[0..], in[0..]); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } + + // Appendix C.3 + { + const key = [_]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const in = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + const exp_out = [_]u8{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 }; + + var out: [exp_out.len]u8 = undefined; + var aes = AES256.init(key); + aes.encrypt(out[0..], in[0..]); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } +} + +test "decrypt" { + // Appendix B + { + const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + const in = [_]u8{ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 }; + const exp_out = [_]u8{ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 }; + + var out: [exp_out.len]u8 = undefined; + var aes = AES128.init(key); + aes.decrypt(out[0..], in[0..]); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } + + // Appendix C.3 + { + const key = [_]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const in = [_]u8{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 }; + const exp_out = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + + var out: [exp_out.len]u8 = undefined; + var aes = AES256.init(key); + aes.decrypt(out[0..], in[0..]); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } +} + +// Key expansion algorithm. See FIPS-197, Figure 11. +fn expandKey(key: []const u8, enc: []u32, dec: []u32) void { + var i: usize = 0; + var nk = key.len / 4; + while (i < nk) : (i += 1) { + enc[i] = mem.readIntSliceBig(u32, key[4 * i .. 4 * i + 4]); + } + while (i < enc.len) : (i += 1) { + var t = enc[i - 1]; + if (i % nk == 0) { + t = subw(rotw(t)) ^ (@as(u32, powx[i / nk - 1]) << 24); + } else if (nk > 6 and i % nk == 4) { + t = subw(t); + } + enc[i] = enc[i - nk] ^ t; + } + + var n = enc.len; + i = 0; + while (i < n) : (i += 4) { + var ei = n - i - 4; + var j: usize = 0; + while (j < 4) : (j += 1) { + var x = enc[ei + j]; + if (i > 0 and i + 4 < n) { + x = td0[sbox0[x >> 24]] ^ td1[sbox0[x >> 16 & 0xff]] ^ td2[sbox0[x >> 8 & 0xff]] ^ td3[sbox0[x & 0xff]]; + } + dec[i + j] = x; + } + } +} + +test "expand key" { + const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + const exp_enc = [_]u32{ + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, + 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605, + 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, + 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, + 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00, + 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, + 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, + 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f, + 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, + 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, + }; + const exp_dec = [_]u32{ + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, + 0xc7b5a63, 0x1319eafe, 0xb0398890, 0x664cfbb4, + 0xdf7d925a, 0x1f62b09d, 0xa320626e, 0xd6757324, + 0x12c07647, 0xc01f22c7, 0xbc42d2f3, 0x7555114a, + 0x6efcd876, 0xd2df5480, 0x7c5df034, 0xc917c3b9, + 0x6ea30afc, 0xbc238cf6, 0xae82a4b4, 0xb54a338d, + 0x90884413, 0xd280860a, 0x12a12842, 0x1bc89739, + 0x7c1f13f7, 0x4208c219, 0xc021ae48, 0x969bf7b, + 0xcc7505eb, 0x3e17d1ee, 0x82296c51, 0xc9481133, + 0x2b3708a7, 0xf262d405, 0xbc3ebdbf, 0x4b617d62, + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x9cf4f3c, + }; + var enc: [exp_enc.len]u32 = undefined; + var dec: [exp_dec.len]u32 = undefined; + expandKey(key[0..], enc[0..], dec[0..]); + testing.expectEqualSlices(u32, exp_enc[0..], enc[0..]); + testing.expectEqualSlices(u32, exp_dec[0..], dec[0..]); +} + +// constants + +const poly = 1 << 8 | 1 << 4 | 1 << 3 | 1 << 1 | 1 << 0; + +const powx = [16]u8{ + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, +}; + +const sbox0 = [256]u8{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +}; + +const sbox1 = [256]u8{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, +}; + +const te0 = [256]u32{ + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, +}; +const te1 = [256]u32{ + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, +}; +const te2 = [256]u32{ + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, +}; +const te3 = [256]u32{ + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, +}; + +const td0 = [256]u32{ + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, +}; +const td1 = [256]u32{ + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, +}; +const td2 = [256]u32{ + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, +}; +const td3 = [256]u32{ + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, +}; diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 7b0747da7..b8bb69419 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -1,7 +1,7 @@ // zig run benchmark.zig --release-fast --override-lib-dir .. const builtin = @import("builtin"); -const std = @import("../std.zig"); +const std = @import("std"); const time = std.time; const Timer = time.Timer; const crypto = std.crypto; @@ -67,7 +67,7 @@ pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 { var timer = try Timer.start(); const start = timer.lap(); while (offset < bytes) : (offset += in.len) { - Mac.create(key[0..], in[0..], key); + Mac.create(key[0..], in[0..], key[0..]); } const end = timer.read(); @@ -94,7 +94,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count { var i: usize = 0; while (i < exchange_count) : (i += 1) { - _ = DhKeyExchange.create(out[0..], out, in); + _ = DhKeyExchange.create(out[0..], out[0..], in[0..]); } } const end = timer.read(); @@ -114,7 +114,7 @@ fn usage() void { \\ --seed [int] \\ --help \\ - ); + , .{}); } fn mode(comptime x: comptime_int) comptime_int { @@ -125,15 +125,13 @@ fn mode(comptime x: comptime_int) comptime_int { fn printPad(stdout: var, s: []const u8) !void { var i: usize = 0; while (i < 12 - s.len) : (i += 1) { - try stdout.print(" "); + try stdout.print(" ", .{}); } - try stdout.print("{}", s); + try stdout.print("{}", .{s}); } pub fn main() !void { - var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = stdout_file.outStream(); - const stdout = &stdout_out_stream.stream; + const stdout = &std.io.getStdOut().outStream().stream; var buffer: [1024]u8 = undefined; var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); @@ -144,7 +142,7 @@ pub fn main() !void { var i: usize = 1; while (i < args.len) : (i += 1) { if (std.mem.eql(u8, args[i], "--mode")) { - try stdout.print("{}\n", builtin.mode); + try stdout.print("{}\n", .{builtin.mode}); return; } else if (std.mem.eql(u8, args[i], "--seed")) { i += 1; @@ -176,7 +174,7 @@ pub fn main() !void { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { const throughput = try benchmarkHash(H.ty, mode(32 * MiB)); try printPad(stdout, H.name); - try stdout.print(": {} MiB/s\n", throughput / (1 * MiB)); + try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)}); } } @@ -184,7 +182,7 @@ pub fn main() !void { if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) { const throughput = try benchmarkMac(M.ty, mode(128 * MiB)); try printPad(stdout, M.name); - try stdout.print(": {} MiB/s\n", throughput / (1 * MiB)); + try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)}); } } @@ -192,7 +190,7 @@ pub fn main() !void { if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { const throughput = try benchmarkKeyExchange(E.ty, mode(1000)); try printPad(stdout, E.name); - try stdout.print(": {} exchanges/s\n", throughput); + try stdout.print(": {} exchanges/s\n", .{throughput}); } } } diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index 6bb2764b9..aa866acbe 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -164,13 +164,13 @@ fn Blake2s(comptime out_len: usize) type { inline while (j < 10) : (j += 1) { inline for (rounds) |r| { v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; - v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16)); + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], @as(usize, 16)); v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12)); + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], @as(usize, 12)); v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; - v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8)); + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], @as(usize, 8)); v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7)); + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], @as(usize, 7)); } } @@ -256,7 +256,7 @@ test "blake2s256 aligned final" { var out: [Blake2s256.digest_length]u8 = undefined; var h = Blake2s256.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } @@ -398,13 +398,13 @@ fn Blake2b(comptime out_len: usize) type { inline while (j < 12) : (j += 1) { inline for (rounds) |r| { v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; - v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32)); + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], @as(usize, 32)); v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24)); + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], @as(usize, 24)); v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; - v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16)); + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], @as(usize, 16)); v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63)); + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], @as(usize, 63)); } } @@ -490,6 +490,6 @@ test "blake2b512 aligned final" { var out: [Blake2b512.digest_length]u8 = undefined; var h = Blake2b512.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 0d997e0d1..d67877b05 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -7,6 +7,7 @@ const assert = std.debug.assert; const testing = std.testing; const builtin = @import("builtin"); const maxInt = std.math.maxInt; +const Poly1305 = std.crypto.Poly1305; const QuarterRound = struct { a: usize, @@ -49,13 +50,13 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { // two-round cycles inline for (rounds) |r| { x[r.a] +%= x[r.b]; - x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16)); + x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16)); x[r.c] +%= x[r.d]; - x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12)); + x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12)); x[r.a] +%= x[r.b]; - x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8)); + x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8)); x[r.c] +%= x[r.d]; - x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7)); + x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7)); } } @@ -218,12 +219,12 @@ test "crypto.chacha20 test vector sunscreen" { }; chaCha20IETF(result[0..], input[0..], 1, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); // Chacha20 is self-reversing. var plaintext: [114]u8 = undefined; chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce); - testing.expect(mem.compare(u8, input, plaintext) == mem.Compare.Equal); + testing.expect(mem.order(u8, input, &plaintext) == .eq); } // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 @@ -258,7 +259,7 @@ test "crypto.chacha20 test vector 1" { const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); } test "crypto.chacha20 test vector 2" { @@ -292,7 +293,7 @@ test "crypto.chacha20 test vector 2" { const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); } test "crypto.chacha20 test vector 3" { @@ -326,7 +327,7 @@ test "crypto.chacha20 test vector 3" { const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); } test "crypto.chacha20 test vector 4" { @@ -360,7 +361,7 @@ test "crypto.chacha20 test vector 4" { const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); } test "crypto.chacha20 test vector 5" { @@ -432,5 +433,207 @@ test "crypto.chacha20 test vector 5" { }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - testing.expectEqualSlices(u8, expected_result, result); + testing.expectEqualSlices(u8, &expected_result, &result); +} + +pub const chacha20poly1305_tag_size = 16; + +pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { + assert(dst.len >= plaintext.len + chacha20poly1305_tag_size); + + // derive poly1305 key + var polyKey = [_]u8{0} ** 32; + chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce); + + // encrypt plaintext + chaCha20IETF(dst[0..plaintext.len], plaintext, 1, key, nonce); + + // construct mac + var mac = Poly1305.init(polyKey[0..]); + mac.update(data); + if (data.len % 16 != 0) { + const zeros = [_]u8{0} ** 16; + const padding = 16 - (data.len % 16); + mac.update(zeros[0..padding]); + } + mac.update(dst[0..plaintext.len]); + if (plaintext.len % 16 != 0) { + const zeros = [_]u8{0} ** 16; + const padding = 16 - (plaintext.len % 16); + mac.update(zeros[0..padding]); + } + var lens: [16]u8 = undefined; + mem.writeIntSliceLittle(u64, lens[0..8], data.len); + mem.writeIntSliceLittle(u64, lens[8..16], plaintext.len); + mac.update(lens[0..]); + mac.final(dst[plaintext.len..]); +} + +/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal. +pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { + if (msgAndTag.len < chacha20poly1305_tag_size) { + return error.InvalidMessage; + } + + // split ciphertext and tag + assert(dst.len >= msgAndTag.len - chacha20poly1305_tag_size); + var ciphertext = msgAndTag[0 .. msgAndTag.len - chacha20poly1305_tag_size]; + var polyTag = msgAndTag[ciphertext.len..]; + + // derive poly1305 key + var polyKey = [_]u8{0} ** 32; + chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce); + + // construct mac + var mac = Poly1305.init(polyKey[0..]); + + mac.update(data); + if (data.len % 16 != 0) { + const zeros = [_]u8{0} ** 16; + const padding = 16 - (data.len % 16); + mac.update(zeros[0..padding]); + } + mac.update(ciphertext); + if (ciphertext.len % 16 != 0) { + const zeros = [_]u8{0} ** 16; + const padding = 16 - (ciphertext.len % 16); + mac.update(zeros[0..padding]); + } + var lens: [16]u8 = undefined; + mem.writeIntSliceLittle(u64, lens[0..8], data.len); + mem.writeIntSliceLittle(u64, lens[8..16], ciphertext.len); + mac.update(lens[0..]); + var computedTag: [16]u8 = undefined; + mac.final(computedTag[0..]); + + // verify mac in constant time + // TODO: we can't currently guarantee that this will run in constant time. + // See https://github.com/ziglang/zig/issues/1776 + var acc: u8 = 0; + for (computedTag) |_, i| { + acc |= (computedTag[i] ^ polyTag[i]); + } + if (acc != 0) { + return error.AuthenticationFailed; + } + + // decrypt ciphertext + chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce); +} + +test "seal" { + { + const plaintext = ""; + const data = ""; + const key = [_]u8{ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + }; + const nonce = [_]u8{ 0x7, 0x0, 0x0, 0x0, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 }; + const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 }; + + var out: [exp_out.len]u8 = undefined; + chacha20poly1305Seal(out[0..], plaintext, data, key, nonce); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } + { + const plaintext = [_]u8{ + 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x74, 0x2e, + }; + const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; + const key = [_]u8{ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + }; + const nonce = [_]u8{ 0x7, 0x0, 0x0, 0x0, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 }; + const exp_out = [_]u8{ + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, + 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, + 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, + 0x1a, 0x71, 0xde, 0xa, 0x9e, 0x6, 0xb, 0x29, 0x5, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, + 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x3, 0xae, 0xe3, 0x28, 0x9, 0x1b, 0x58, + 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, + 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, + 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, + 0x6, 0x91, + }; + + var out: [exp_out.len]u8 = undefined; + chacha20poly1305Seal(out[0..], plaintext[0..], data[0..], key, nonce); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } +} + +test "open" { + { + const ciphertext = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 }; + const data = ""; + const key = [_]u8{ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + }; + const nonce = [_]u8{ 0x7, 0x0, 0x0, 0x0, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 }; + const exp_out = ""; + + var out: [exp_out.len]u8 = undefined; + try chacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + } + { + const ciphertext = [_]u8{ + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, + 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, + 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, + 0x1a, 0x71, 0xde, 0xa, 0x9e, 0x6, 0xb, 0x29, 0x5, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, + 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x3, 0xae, 0xe3, 0x28, 0x9, 0x1b, 0x58, + 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, + 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, + 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, + 0x6, 0x91, + }; + const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; + const key = [_]u8{ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + }; + const nonce = [_]u8{ 0x7, 0x0, 0x0, 0x0, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 }; + const exp_out = [_]u8{ + 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x74, 0x2e, + }; + + var out: [exp_out.len]u8 = undefined; + try chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, nonce); + testing.expectEqualSlices(u8, exp_out[0..], out[0..]); + + // corrupting the ciphertext, data, key, or nonce should cause a failure + var bad_ciphertext = ciphertext; + bad_ciphertext[0] ^= 1; + testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], bad_ciphertext[0..], data[0..], key, nonce)); + var bad_data = data; + bad_data[0] ^= 1; + testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], bad_data[0..], key, nonce)); + var bad_key = key; + bad_key[0] ^= 1; + testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], bad_key, nonce)); + var bad_nonce = nonce; + bad_nonce[0] ^= 1; + testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, bad_nonce)); + + // a short ciphertext should result in a different error + testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce)); + } } diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 0a0a5056c..1d835b231 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -34,9 +34,9 @@ pub const State = struct { pub fn permute(self: *Self) void { const state = &self.data; - var round = u32(24); + var round = @as(u32, 24); while (round > 0) : (round -= 1) { - var column = usize(0); + var column = @as(usize, 0); while (column < 4) : (column += 1) { const x = math.rotl(u32, state[column], 24); const y = math.rotl(u32, state[4 + column], 9); @@ -61,7 +61,7 @@ pub const State = struct { } pub fn squeeze(self: *Self, out: []u8) void { - var i = usize(0); + var i = @as(usize, 0); while (i + RATE <= out.len) : (i += RATE) { self.permute(); mem.copy(u8, out[i..], self.toSliceConst()[0..RATE]); @@ -79,11 +79,11 @@ test "permute" { var state = State{ .data = blk: { var input: [12]u32 = undefined; - var i = u32(0); + var i = @as(u32, 0); while (i < 12) : (i += 1) { input[i] = i * i * i + i *% 0x9e3779b9; } - testing.expectEqualSlices(u32, input, [_]u32{ + testing.expectEqualSlices(u32, &input, &[_]u32{ 0x00000000, 0x9e3779ba, 0x3c6ef37a, 0xdaa66d46, 0x78dde724, 0x1715611a, 0xb54cdb2e, 0x53845566, 0xf1bbcfc8, 0x8ff34a5a, 0x2e2ac522, 0xcc624026, @@ -92,7 +92,7 @@ test "permute" { }, }; state.permute(); - testing.expectEqualSlices(u32, state.data, [_]u32{ + testing.expectEqualSlices(u32, &state.data, &[_]u32{ 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68, 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8, 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6, @@ -163,6 +163,6 @@ test "hash" { var msg: [58 / 2]u8 = undefined; try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); var md: [32]u8 = undefined; - hash(&md, msg); - htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", md); + hash(&md, &msg); + htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md); } diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig index ddbb39a9d..41ce802dd 100644 --- a/lib/std/crypto/md5.zig +++ b/lib/std/crypto/md5.zig @@ -126,10 +126,10 @@ pub const Md5 = struct { while (i < 16) : (i += 1) { // NOTE: Performing or's separately improves perf by ~10% s[i] = 0; - s[i] |= u32(b[i * 4 + 0]); - s[i] |= u32(b[i * 4 + 1]) << 8; - s[i] |= u32(b[i * 4 + 2]) << 16; - s[i] |= u32(b[i * 4 + 3]) << 24; + s[i] |= @as(u32, b[i * 4 + 0]); + s[i] |= @as(u32, b[i * 4 + 1]) << 8; + s[i] |= @as(u32, b[i * 4 + 2]) << 16; + s[i] |= @as(u32, b[i * 4 + 3]) << 24; } var v: [4]u32 = [_]u32{ @@ -276,6 +276,6 @@ test "md5 aligned final" { var out: [Md5.digest_length]u8 = undefined; var h = Md5.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig index bd0b33e58..2395b1c7a 100644 --- a/lib/std/crypto/poly1305.zig +++ b/lib/std/crypto/poly1305.zig @@ -87,11 +87,11 @@ pub const Poly1305 = struct { // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff fn polyBlock(ctx: *Self) void { // s = h + c, without carry propagation - const s0 = u64(ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe - const s1 = u64(ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe - const s2 = u64(ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe - const s3 = u64(ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe - const s4 = u64(ctx.h[4]) + ctx.c[4]; // s4 <= 5 + const s0 = @as(u64, ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe + const s1 = @as(u64, ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe + const s2 = @as(u64, ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe + const s3 = @as(u64, ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe + const s4 = @as(u64, ctx.h[4]) + ctx.c[4]; // s4 <= 5 // Local all the things! const r0 = ctx.r[0]; // r0 <= 0fffffff @@ -197,7 +197,7 @@ pub const Poly1305 = struct { // check if we should subtract 2^130-5 by performing the // corresponding carry propagation. - const _u0 = u64(5) + ctx.h[0]; // <= 1_00000004 + const _u0 = @as(u64, 5) + ctx.h[0]; // <= 1_00000004 const _u1 = (_u0 >> 32) + ctx.h[1]; // <= 1_00000000 const _u2 = (_u1 >> 32) + ctx.h[2]; // <= 1_00000000 const _u3 = (_u2 >> 32) + ctx.h[3]; // <= 1_00000000 @@ -230,5 +230,5 @@ test "poly1305 rfc7439 vector1" { var mac: [16]u8 = undefined; Poly1305.create(mac[0..], msg, key); - std.testing.expectEqualSlices(u8, expected_mac, mac); + std.testing.expectEqualSlices(u8, expected_mac, &mac); } diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig index c5160a1f3..b4d6e5c0c 100644 --- a/lib/std/crypto/sha1.zig +++ b/lib/std/crypto/sha1.zig @@ -146,10 +146,10 @@ pub const Sha1 = struct { Rp(0, 1, 2, 3, 4, 15), }; inline for (round0a) |r| { - s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | (u32(b[r.i * 4 + 1]) << 16) | (u32(b[r.i * 4 + 2]) << 8) | (u32(b[r.i * 4 + 3]) << 0); + s[r.i] = (@as(u32, b[r.i * 4 + 0]) << 24) | (@as(u32, b[r.i * 4 + 1]) << 16) | (@as(u32, b[r.i * 4 + 2]) << 8) | (@as(u32, b[r.i * 4 + 3]) << 0); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); - v[r.b] = math.rotl(u32, v[r.b], u32(30)); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30)); } const round0b = comptime [_]RoundParam{ @@ -160,10 +160,10 @@ pub const Sha1 = struct { }; inline for (round0b) |r| { const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; - s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); - v[r.b] = math.rotl(u32, v[r.b], u32(30)); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30)); } const round1 = comptime [_]RoundParam{ @@ -190,10 +190,10 @@ pub const Sha1 = struct { }; inline for (round1) |r| { const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; - s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); - v[r.b] = math.rotl(u32, v[r.b], u32(30)); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30)); } const round2 = comptime [_]RoundParam{ @@ -220,10 +220,10 @@ pub const Sha1 = struct { }; inline for (round2) |r| { const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; - s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); - v[r.b] = math.rotl(u32, v[r.b], u32(30)); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30)); } const round3 = comptime [_]RoundParam{ @@ -250,10 +250,10 @@ pub const Sha1 = struct { }; inline for (round3) |r| { const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; - s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); - v[r.b] = math.rotl(u32, v[r.b], u32(30)); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30)); } d.s[0] +%= v[0]; @@ -297,6 +297,6 @@ test "sha1 aligned final" { var out: [Sha1.digest_length]u8 = undefined; var h = Sha1.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index b40a39d57..478cadd03 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -180,13 +180,13 @@ fn Sha2_32(comptime params: Sha2Params32) type { var i: usize = 0; while (i < 16) : (i += 1) { s[i] = 0; - s[i] |= u32(b[i * 4 + 0]) << 24; - s[i] |= u32(b[i * 4 + 1]) << 16; - s[i] |= u32(b[i * 4 + 2]) << 8; - s[i] |= u32(b[i * 4 + 3]) << 0; + s[i] |= @as(u32, b[i * 4 + 0]) << 24; + s[i] |= @as(u32, b[i * 4 + 1]) << 16; + s[i] |= @as(u32, b[i * 4 + 2]) << 8; + s[i] |= @as(u32, b[i * 4 + 3]) << 0; } while (i < 64) : (i += 1) { - s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u32, s[i - 15], u32(7)) ^ math.rotr(u32, s[i - 15], u32(18)) ^ (s[i - 15] >> 3)) +% (math.rotr(u32, s[i - 2], u32(17)) ^ math.rotr(u32, s[i - 2], u32(19)) ^ (s[i - 2] >> 10)); + s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u32, s[i - 15], @as(u32, 7)) ^ math.rotr(u32, s[i - 15], @as(u32, 18)) ^ (s[i - 15] >> 3)) +% (math.rotr(u32, s[i - 2], @as(u32, 17)) ^ math.rotr(u32, s[i - 2], @as(u32, 19)) ^ (s[i - 2] >> 10)); } var v: [8]u32 = [_]u32{ @@ -267,11 +267,11 @@ fn Sha2_32(comptime params: Sha2Params32) type { Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2), }; inline for (round0) |r| { - v[r.h] = v[r.h] +% (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; + v[r.h] = v[r.h] +% (math.rotr(u32, v[r.e], @as(u32, 6)) ^ math.rotr(u32, v[r.e], @as(u32, 11)) ^ math.rotr(u32, v[r.e], @as(u32, 25))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; v[r.d] = v[r.d] +% v[r.h]; - v[r.h] = v[r.h] +% (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + v[r.h] = v[r.h] +% (math.rotr(u32, v[r.a], @as(u32, 2)) ^ math.rotr(u32, v[r.a], @as(u32, 13)) ^ math.rotr(u32, v[r.a], @as(u32, 22))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); } d.s[0] +%= v[0]; @@ -343,7 +343,7 @@ test "sha256 aligned final" { var out: [Sha256.digest_length]u8 = undefined; var h = Sha256.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } @@ -522,17 +522,19 @@ fn Sha2_64(comptime params: Sha2Params64) type { var i: usize = 0; while (i < 16) : (i += 1) { s[i] = 0; - s[i] |= u64(b[i * 8 + 0]) << 56; - s[i] |= u64(b[i * 8 + 1]) << 48; - s[i] |= u64(b[i * 8 + 2]) << 40; - s[i] |= u64(b[i * 8 + 3]) << 32; - s[i] |= u64(b[i * 8 + 4]) << 24; - s[i] |= u64(b[i * 8 + 5]) << 16; - s[i] |= u64(b[i * 8 + 6]) << 8; - s[i] |= u64(b[i * 8 + 7]) << 0; + s[i] |= @as(u64, b[i * 8 + 0]) << 56; + s[i] |= @as(u64, b[i * 8 + 1]) << 48; + s[i] |= @as(u64, b[i * 8 + 2]) << 40; + s[i] |= @as(u64, b[i * 8 + 3]) << 32; + s[i] |= @as(u64, b[i * 8 + 4]) << 24; + s[i] |= @as(u64, b[i * 8 + 5]) << 16; + s[i] |= @as(u64, b[i * 8 + 6]) << 8; + s[i] |= @as(u64, b[i * 8 + 7]) << 0; } while (i < 80) : (i += 1) { - s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u64, s[i - 15], u64(1)) ^ math.rotr(u64, s[i - 15], u64(8)) ^ (s[i - 15] >> 7)) +% (math.rotr(u64, s[i - 2], u64(19)) ^ math.rotr(u64, s[i - 2], u64(61)) ^ (s[i - 2] >> 6)); + s[i] = s[i - 16] +% s[i - 7] +% + (math.rotr(u64, s[i - 15], @as(u64, 1)) ^ math.rotr(u64, s[i - 15], @as(u64, 8)) ^ (s[i - 15] >> 7)) +% + (math.rotr(u64, s[i - 2], @as(u64, 19)) ^ math.rotr(u64, s[i - 2], @as(u64, 61)) ^ (s[i - 2] >> 6)); } var v: [8]u64 = [_]u64{ @@ -629,11 +631,11 @@ fn Sha2_64(comptime params: Sha2Params64) type { Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817), }; inline for (round0) |r| { - v[r.h] = v[r.h] +% (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; + v[r.h] = v[r.h] +% (math.rotr(u64, v[r.e], @as(u64, 14)) ^ math.rotr(u64, v[r.e], @as(u64, 18)) ^ math.rotr(u64, v[r.e], @as(u64, 41))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; v[r.d] = v[r.d] +% v[r.h]; - v[r.h] = v[r.h] +% (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + v[r.h] = v[r.h] +% (math.rotr(u64, v[r.a], @as(u64, 28)) ^ math.rotr(u64, v[r.a], @as(u64, 34)) ^ math.rotr(u64, v[r.a], @as(u64, 39))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); } d.s[0] +%= v[0]; @@ -721,6 +723,6 @@ test "sha512 aligned final" { var out: [Sha512.digest_length]u8 = undefined; var h = Sha512.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 659e7a254..d7b2fbe25 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -133,7 +133,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { } x = 0; inline while (x < 5) : (x += 1) { - t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], usize(1)); + t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1)); y = 0; inline while (y < 5) : (y += 1) { s[x + y * 5] ^= t[0]; @@ -229,7 +229,7 @@ test "sha3-256 aligned final" { var out: [Sha3_256.digest_length]u8 = undefined; var h = Sha3_256.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } @@ -300,6 +300,6 @@ test "sha3-512 aligned final" { var out: [Sha3_512.digest_length]u8 = undefined; var h = Sha3_512.init(); - h.update(block); + h.update(&block); h.final(out[0..]); } diff --git a/lib/std/crypto/test.zig b/lib/std/crypto/test.zig index a0ddad6c8..1ff326cf3 100644 --- a/lib/std/crypto/test.zig +++ b/lib/std/crypto/test.zig @@ -8,7 +8,7 @@ pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, inpu var h: [expected.len / 2]u8 = undefined; Hasher.hash(input, h[0..]); - assertEqual(expected, h); + assertEqual(expected, &h); } // Assert `expected` == `input` where `input` is a bytestring. @@ -18,5 +18,5 @@ pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { r.* = fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; } - testing.expectEqualSlices(u8, expected_bytes, input); + testing.expectEqualSlices(u8, &expected_bytes, input); } diff --git a/lib/std/crypto/x25519.zig b/lib/std/crypto/x25519.zig index 7f9220c3f..16e3f073f 100644 --- a/lib/std/crypto/x25519.zig +++ b/lib/std/crypto/x25519.zig @@ -63,7 +63,7 @@ pub const X25519 = struct { var pos: isize = 254; while (pos >= 0) : (pos -= 1) { // constant time conditional swap before ladder step - const b = scalarBit(e, @intCast(usize, pos)); + const b = scalarBit(&e, @intCast(usize, pos)); swap ^= b; // xor trick avoids swapping at the end of the loop Fe.cswap(x2, x3, swap); Fe.cswap(z2, z3, swap); @@ -117,7 +117,7 @@ pub const X25519 = struct { pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool { var base_point = [_]u8{9} ++ [_]u8{0} ** 31; - return create(public_key, private_key, base_point); + return create(public_key, private_key, &base_point); } }; @@ -199,9 +199,9 @@ const Fe = struct { inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void { const j = (i + 1) % 10; - c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1); + c[i] = (t[i] + (@as(i64, 1) << shift)) >> (shift + 1); t[j] += c[i] * mult; - t[i] -= c[i] * (i64(1) << (shift + 1)); + t[i] -= c[i] * (@as(i64, 1) << (shift + 1)); } fn carry1(h: *Fe, t: []i64) void { @@ -256,15 +256,15 @@ const Fe = struct { var t: [10]i64 = undefined; t[0] = readIntSliceLittle(u32, s[0..4]); - t[1] = u32(readIntSliceLittle(u24, s[4..7])) << 6; - t[2] = u32(readIntSliceLittle(u24, s[7..10])) << 5; - t[3] = u32(readIntSliceLittle(u24, s[10..13])) << 3; - t[4] = u32(readIntSliceLittle(u24, s[13..16])) << 2; + t[1] = @as(u32, readIntSliceLittle(u24, s[4..7])) << 6; + t[2] = @as(u32, readIntSliceLittle(u24, s[7..10])) << 5; + t[3] = @as(u32, readIntSliceLittle(u24, s[10..13])) << 3; + t[4] = @as(u32, readIntSliceLittle(u24, s[13..16])) << 2; t[5] = readIntSliceLittle(u32, s[16..20]); - t[6] = u32(readIntSliceLittle(u24, s[20..23])) << 7; - t[7] = u32(readIntSliceLittle(u24, s[23..26])) << 5; - t[8] = u32(readIntSliceLittle(u24, s[26..29])) << 4; - t[9] = (u32(readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2; + t[6] = @as(u32, readIntSliceLittle(u24, s[20..23])) << 7; + t[7] = @as(u32, readIntSliceLittle(u24, s[23..26])) << 5; + t[8] = @as(u32, readIntSliceLittle(u24, s[26..29])) << 4; + t[9] = (@as(u32, readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2; carry1(h, t[0..]); } @@ -273,7 +273,7 @@ const Fe = struct { var t: [10]i64 = undefined; for (t[0..]) |_, i| { - t[i] = i64(f.b[i]) * g; + t[i] = @as(i64, f.b[i]) * g; } carry1(h, t[0..]); @@ -305,16 +305,16 @@ const Fe = struct { // t's become h var t: [10]i64 = undefined; - t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]); - t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]); - t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]); - t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]); - t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]); - t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]); - t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]); - t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]); - t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]); - t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]); + t[0] = f[0] * @as(i64, g[0]) + F[1] * @as(i64, G[9]) + f[2] * @as(i64, G[8]) + F[3] * @as(i64, G[7]) + f[4] * @as(i64, G[6]) + F[5] * @as(i64, G[5]) + f[6] * @as(i64, G[4]) + F[7] * @as(i64, G[3]) + f[8] * @as(i64, G[2]) + F[9] * @as(i64, G[1]); + t[1] = f[0] * @as(i64, g[1]) + f[1] * @as(i64, g[0]) + f[2] * @as(i64, G[9]) + f[3] * @as(i64, G[8]) + f[4] * @as(i64, G[7]) + f[5] * @as(i64, G[6]) + f[6] * @as(i64, G[5]) + f[7] * @as(i64, G[4]) + f[8] * @as(i64, G[3]) + f[9] * @as(i64, G[2]); + t[2] = f[0] * @as(i64, g[2]) + F[1] * @as(i64, g[1]) + f[2] * @as(i64, g[0]) + F[3] * @as(i64, G[9]) + f[4] * @as(i64, G[8]) + F[5] * @as(i64, G[7]) + f[6] * @as(i64, G[6]) + F[7] * @as(i64, G[5]) + f[8] * @as(i64, G[4]) + F[9] * @as(i64, G[3]); + t[3] = f[0] * @as(i64, g[3]) + f[1] * @as(i64, g[2]) + f[2] * @as(i64, g[1]) + f[3] * @as(i64, g[0]) + f[4] * @as(i64, G[9]) + f[5] * @as(i64, G[8]) + f[6] * @as(i64, G[7]) + f[7] * @as(i64, G[6]) + f[8] * @as(i64, G[5]) + f[9] * @as(i64, G[4]); + t[4] = f[0] * @as(i64, g[4]) + F[1] * @as(i64, g[3]) + f[2] * @as(i64, g[2]) + F[3] * @as(i64, g[1]) + f[4] * @as(i64, g[0]) + F[5] * @as(i64, G[9]) + f[6] * @as(i64, G[8]) + F[7] * @as(i64, G[7]) + f[8] * @as(i64, G[6]) + F[9] * @as(i64, G[5]); + t[5] = f[0] * @as(i64, g[5]) + f[1] * @as(i64, g[4]) + f[2] * @as(i64, g[3]) + f[3] * @as(i64, g[2]) + f[4] * @as(i64, g[1]) + f[5] * @as(i64, g[0]) + f[6] * @as(i64, G[9]) + f[7] * @as(i64, G[8]) + f[8] * @as(i64, G[7]) + f[9] * @as(i64, G[6]); + t[6] = f[0] * @as(i64, g[6]) + F[1] * @as(i64, g[5]) + f[2] * @as(i64, g[4]) + F[3] * @as(i64, g[3]) + f[4] * @as(i64, g[2]) + F[5] * @as(i64, g[1]) + f[6] * @as(i64, g[0]) + F[7] * @as(i64, G[9]) + f[8] * @as(i64, G[8]) + F[9] * @as(i64, G[7]); + t[7] = f[0] * @as(i64, g[7]) + f[1] * @as(i64, g[6]) + f[2] * @as(i64, g[5]) + f[3] * @as(i64, g[4]) + f[4] * @as(i64, g[3]) + f[5] * @as(i64, g[2]) + f[6] * @as(i64, g[1]) + f[7] * @as(i64, g[0]) + f[8] * @as(i64, G[9]) + f[9] * @as(i64, G[8]); + t[8] = f[0] * @as(i64, g[8]) + F[1] * @as(i64, g[7]) + f[2] * @as(i64, g[6]) + F[3] * @as(i64, g[5]) + f[4] * @as(i64, g[4]) + F[5] * @as(i64, g[3]) + f[6] * @as(i64, g[2]) + F[7] * @as(i64, g[1]) + f[8] * @as(i64, g[0]) + F[9] * @as(i64, G[9]); + t[9] = f[0] * @as(i64, g[9]) + f[1] * @as(i64, g[8]) + f[2] * @as(i64, g[7]) + f[3] * @as(i64, g[6]) + f[4] * @as(i64, g[5]) + f[5] * @as(i64, g[4]) + f[6] * @as(i64, g[3]) + f[7] * @as(i64, g[2]) + f[8] * @as(i64, g[1]) + f[9] * @as(i64, g[0]); carry2(h, t[0..]); } @@ -348,16 +348,16 @@ const Fe = struct { var t: [10]i64 = undefined; - t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38); - t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19); - t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19); - t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38); - t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38); - t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19); - t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19); - t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38); - t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38); - t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2); + t[0] = f0 * @as(i64, f0) + f1_2 * @as(i64, f9_38) + f2_2 * @as(i64, f8_19) + f3_2 * @as(i64, f7_38) + f4_2 * @as(i64, f6_19) + f5 * @as(i64, f5_38); + t[1] = f0_2 * @as(i64, f1) + f2 * @as(i64, f9_38) + f3_2 * @as(i64, f8_19) + f4 * @as(i64, f7_38) + f5_2 * @as(i64, f6_19); + t[2] = f0_2 * @as(i64, f2) + f1_2 * @as(i64, f1) + f3_2 * @as(i64, f9_38) + f4_2 * @as(i64, f8_19) + f5_2 * @as(i64, f7_38) + f6 * @as(i64, f6_19); + t[3] = f0_2 * @as(i64, f3) + f1_2 * @as(i64, f2) + f4 * @as(i64, f9_38) + f5_2 * @as(i64, f8_19) + f6 * @as(i64, f7_38); + t[4] = f0_2 * @as(i64, f4) + f1_2 * @as(i64, f3_2) + f2 * @as(i64, f2) + f5_2 * @as(i64, f9_38) + f6_2 * @as(i64, f8_19) + f7 * @as(i64, f7_38); + t[5] = f0_2 * @as(i64, f5) + f1_2 * @as(i64, f4) + f2_2 * @as(i64, f3) + f6 * @as(i64, f9_38) + f7_2 * @as(i64, f8_19); + t[6] = f0_2 * @as(i64, f6) + f1_2 * @as(i64, f5_2) + f2_2 * @as(i64, f4) + f3_2 * @as(i64, f3) + f7_2 * @as(i64, f9_38) + f8 * @as(i64, f8_19); + t[7] = f0_2 * @as(i64, f7) + f1_2 * @as(i64, f6) + f2_2 * @as(i64, f5) + f3_2 * @as(i64, f4) + f8 * @as(i64, f9_38); + t[8] = f0_2 * @as(i64, f8) + f1_2 * @as(i64, f7_2) + f2_2 * @as(i64, f6) + f3_2 * @as(i64, f5_2) + f4 * @as(i64, f4) + f9 * @as(i64, f9_38); + t[9] = f0_2 * @as(i64, f9) + f1_2 * @as(i64, f8) + f2_2 * @as(i64, f7) + f3_2 * @as(i64, f6) + f4 * @as(i64, f5_2); carry2(h, t[0..]); } @@ -500,7 +500,7 @@ const Fe = struct { if (i + 1 < 10) { t[i + 1] += c[i]; } - t[i] -= c[i] * (i32(1) << shift); + t[i] -= c[i] * (@as(i32, 1) << shift); } fn toBytes(s: []u8, h: *const Fe) void { @@ -511,7 +511,7 @@ const Fe = struct { t[i] = h.b[i]; } - var q = (19 * t[9] + ((i32(1) << 24))) >> 25; + var q = (19 * t[9] + ((@as(i32, 1) << 24))) >> 25; { var i: usize = 0; while (i < 5) : (i += 1) { @@ -581,8 +581,8 @@ test "x25519 public key calculation from secret key" { var pk_calculated: [32]u8 = undefined; try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); - std.testing.expect(X25519.createPublicKey(pk_calculated[0..], sk)); - std.testing.expect(std.mem.eql(u8, pk_calculated, pk_expected)); + std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk)); + std.testing.expect(std.mem.eql(u8, &pk_calculated, &pk_expected)); } test "x25519 rfc7748 vector1" { @@ -594,7 +594,7 @@ test "x25519 rfc7748 vector1" { var output: [32]u8 = undefined; std.testing.expect(X25519.create(output[0..], secret_key, public_key)); - std.testing.expect(std.mem.eql(u8, output, expected_output)); + std.testing.expect(std.mem.eql(u8, &output, expected_output)); } test "x25519 rfc7748 vector2" { @@ -606,11 +606,11 @@ test "x25519 rfc7748 vector2" { var output: [32]u8 = undefined; std.testing.expect(X25519.create(output[0..], secret_key, public_key)); - std.testing.expect(std.mem.eql(u8, output, expected_output)); + std.testing.expect(std.mem.eql(u8, &output, expected_output)); } test "x25519 rfc7748 one iteration" { - const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*; const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79"; var k: [32]u8 = initial_value; @@ -619,7 +619,7 @@ test "x25519 rfc7748 one iteration" { var i: usize = 0; while (i < 1) : (i += 1) { var output: [32]u8 = undefined; - std.testing.expect(X25519.create(output[0..], k, u)); + std.testing.expect(X25519.create(output[0..], &k, &u)); std.mem.copy(u8, u[0..], k[0..]); std.mem.copy(u8, k[0..], output[0..]); @@ -637,13 +637,13 @@ test "x25519 rfc7748 1,000 iterations" { const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51"; - var k: [32]u8 = initial_value; - var u: [32]u8 = initial_value; + var k: [32]u8 = initial_value.*; + var u: [32]u8 = initial_value.*; var i: usize = 0; while (i < 1000) : (i += 1) { var output: [32]u8 = undefined; - std.testing.expect(X25519.create(output[0..], k, u)); + std.testing.expect(X25519.create(output[0..], &k, &u)); std.mem.copy(u8, u[0..], k[0..]); std.mem.copy(u8, k[0..], output[0..]); @@ -660,13 +660,13 @@ test "x25519 rfc7748 1,000,000 iterations" { const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24"; - var k: [32]u8 = initial_value; - var u: [32]u8 = initial_value; + var k: [32]u8 = initial_value.*; + var u: [32]u8 = initial_value.*; var i: usize = 0; while (i < 1000000) : (i += 1) { var output: [32]u8 = undefined; - std.testing.expect(X25519.create(output[0..], k, u)); + std.testing.expect(X25519.create(output[0..], &k, &u)); std.mem.copy(u8, u[0..], k[0..]); std.mem.copy(u8, k[0..], output[0..]); diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index dd28e5044..5194dc1a1 100644 --- a/lib/std/cstr.zig +++ b/lib/std/cstr.zig @@ -9,7 +9,7 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { +pub fn cmp(a: [*:0]const u8, b: [*:0]const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} if (a[index] > b[index]) { @@ -27,23 +27,31 @@ test "cstr fns" { } fn testCStrFnsImpl() void { - testing.expect(cmp(c"aoeu", c"aoez") == -1); - testing.expect(mem.len(u8, c"123456789") == 9); + testing.expect(cmp("aoeu", "aoez") == -1); + testing.expect(mem.len(u8, "123456789") == 9); } -/// Returns a mutable slice with 1 more byte of length which is a null byte. +/// Returns a mutable, null-terminated slice with the same length as `slice`. /// Caller owns the returned memory. -pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 { +pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 { const result = try allocator.alloc(u8, slice.len + 1); mem.copy(u8, result, slice); result[slice.len] = 0; - return result; + return result[0..slice.len :0]; +} + +test "addNullByte" { + var buf: [30]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator; + const slice = try addNullByte(allocator, "hello"[0..4]); + testing.expect(slice.len == 4); + testing.expect(slice[4] == 0); } pub const NullTerminated2DArray = struct { allocator: *mem.Allocator, byte_count: usize, - ptr: ?[*]?[*]u8, + ptr: ?[*:null]?[*:0]u8, /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result @@ -83,7 +91,7 @@ pub const NullTerminated2DArray = struct { return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, - .ptr = @ptrCast(?[*]?[*]u8, buf.ptr), + .ptr = @ptrCast(?[*:null]?[*:0]u8, buf.ptr), }; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 25868d67a..dfdaca6d3 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -17,7 +17,7 @@ const maxInt = std.math.maxInt; const File = std.fs.File; const windows = std.os.windows; -const leb = @import("debug/leb128.zig"); +pub const leb = @import("debug/leb128.zig"); pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator; pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; @@ -45,18 +45,19 @@ var stderr_file_out_stream: File.OutStream = undefined; var stderr_stream: ?*io.OutStream(File.WriteError) = null; var stderr_mutex = std.Mutex.init(); -pub fn warn(comptime fmt: []const u8, args: ...) void { + +pub fn warn(comptime fmt: []const u8, args: var) void { const held = stderr_mutex.acquire(); defer held.release(); - const stderr = getStderrStream() catch return; + const stderr = getStderrStream(); stderr.print(fmt, args) catch return; } -pub fn getStderrStream() !*io.OutStream(File.WriteError) { +pub fn getStderrStream() *io.OutStream(File.WriteError) { if (stderr_stream) |st| { return st; } else { - stderr_file = try io.getStdErr(); + stderr_file = io.getStdErr(); stderr_file_out_stream = stderr_file.outStream(); const st = &stderr_file_out_stream.stream; stderr_stream = st; @@ -64,6 +65,10 @@ pub fn getStderrStream() !*io.OutStream(File.WriteError) { } } +pub fn getStderrMutex() *std.Mutex { + return &stderr_mutex; +} + /// TODO multithreaded awareness var self_debug_info: ?DebugInfo = null; @@ -85,17 +90,17 @@ fn wantTtyColor() bool { /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. /// TODO multithreaded awareness pub fn dumpCurrentStackTrace(start_addr: ?usize) void { - const stderr = getStderrStream() catch return; + const stderr = getStderrStream(); if (builtin.strip_debug_info) { - stderr.print("Unable to dump stack trace: debug info stripped\n") catch return; + stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; return; }; } @@ -104,13 +109,13 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { /// unbuffered, and ignores any error returned. /// TODO multithreaded awareness pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { - const stderr = getStderrStream() catch return; + const stderr = getStderrStream(); if (builtin.strip_debug_info) { - stderr.print("Unable to dump stack trace: debug info stripped\n") catch return; + stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; const tty_color = wantTtyColor(); @@ -133,7 +138,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { /// chopping off the irrelevant frames and shifting so that the returned addresses pointer /// equals the passed in addresses pointer. pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const addrs = stack_trace.instruction_addresses; const u32_addrs_len = @intCast(u32, addrs.len); const first_addr = first_address orelse { @@ -177,17 +182,17 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. /// TODO multithreaded awareness pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { - const stderr = getStderrStream() catch return; + const stderr = getStderrStream(); if (builtin.strip_debug_info) { - stderr.print("Unable to dump stack trace: debug info stripped\n") catch return; + stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; return; }; } @@ -206,16 +211,17 @@ pub fn assert(ok: bool) void { if (!ok) unreachable; // assertion failure } -pub fn panic(comptime format: []const u8, args: ...) noreturn { +pub fn panic(comptime format: []const u8, args: var) noreturn { @setCold(true); - const first_trace_addr = @returnAddress(); + // TODO: remove conditional once wasi / LLVM defines __builtin_return_address + const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress(); panicExtra(null, first_trace_addr, format, args); } /// TODO multithreaded awareness -var panicking: u8 = 0; // TODO make this a bool +var panicking: u8 = 0; -pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { +pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { @setCold(true); if (enable_segfault_handler) { @@ -224,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c resetSegfaultHandler(); } - if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { - // Panicked during a panic. - - // TODO detect if a different thread caused the panic, because in that case - // we would want to return here instead of calling abort, so that the thread - // which first called panic can finish printing a stack trace. - os.abort(); + switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { + 0 => { + const stderr = getStderrStream(); + stderr.print(format ++ "\n", args) catch os.abort(); + if (trace) |t| { + dumpStackTrace(t.*); + } + dumpCurrentStackTrace(first_trace_addr); + }, + 1 => { + // TODO detect if a different thread caused the panic, because in that case + // we would want to return here instead of calling abort, so that the thread + // which first called panic can finish printing a stack trace. + warn("Panicked during a panic. Aborting.\n", .{}); + }, + else => { + // Panicked while printing "Panicked during a panic." + }, } - const stderr = getStderrStream() catch os.abort(); - stderr.print(format ++ "\n", args) catch os.abort(); - if (trace) |t| { - dumpStackTrace(t.*); - } - dumpCurrentStackTrace(first_trace_addr); - os.abort(); } @@ -280,14 +290,23 @@ pub const StackIterator = struct { }; } + // On some architectures such as x86 the frame pointer is the address where + // the previous fp is stored, while on some other architectures such as + // RISC-V it points to the "top" of the frame, just above where the previous + // fp and the return address are stored. + const fp_adjust_factor = if (builtin.arch == .riscv32 or builtin.arch == .riscv64) + 2 * @sizeOf(usize) + else + 0; + fn next(self: *StackIterator) ?usize { - if (self.fp == 0) return null; - self.fp = @intToPtr(*const usize, self.fp).*; - if (self.fp == 0) return null; + if (self.fp <= fp_adjust_factor) return null; + self.fp = @intToPtr(*const usize, self.fp - fp_adjust_factor).*; + if (self.fp <= fp_adjust_factor) return null; if (self.first_addr) |addr| { - while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { - const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + while (self.fp > fp_adjust_factor) : (self.fp = @intToPtr(*const usize, self.fp - fp_adjust_factor).*) { + const return_address = @intToPtr(*const usize, self.fp - fp_adjust_factor + @sizeOf(usize)).*; if (addr == return_address) { self.first_addr = null; return return_address; @@ -295,13 +314,13 @@ pub const StackIterator = struct { } } - const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + const return_address = @intToPtr(*const usize, self.fp - fp_adjust_factor + @sizeOf(usize)).*; return return_address; } }; pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { - if (windows.is_the_target) { + if (builtin.os == .windows) { return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr); } var it = StackIterator.init(start_addr); @@ -333,10 +352,10 @@ pub fn writeCurrentStackTraceWindows( /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, /// make this `noasync fn` and remove the individual noasync calls. pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - if (windows.is_the_target) { + if (builtin.os == .windows) { return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_color); } - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color); } return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_color); @@ -361,13 +380,13 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres } else { // we have no information to add to the address if (tty_color) { - try out_stream.print("???:?:?: "); + try out_stream.print("???:?:?: ", .{}); setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in ??? (???)", relocated_address); + try out_stream.print("0x{x} in ??? (???)", .{relocated_address}); setTtyColor(TtyColor.Reset); - try out_stream.print("\n\n\n"); + try out_stream.print("\n\n\n", .{}); } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{relocated_address}); } return; }; @@ -387,7 +406,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; const vaddr_end = vaddr_start + proc_sym.CodeSize; if (relative_address >= vaddr_start and relative_address < vaddr_end) { - break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); + break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); } }, else => {}, @@ -494,18 +513,18 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres if (tty_color) { setTtyColor(TtyColor.White); if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); + try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); } else { - try out_stream.print("???:?:?"); + try out_stream.print("???:?:?", .{}); } setTtyColor(TtyColor.Reset); - try out_stream.print(": "); + try out_stream.print(": ", .{}); setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); + try out_stream.print("0x{x} in {} ({})", .{ relocated_address, symbol_name, obj_basename }); setTtyColor(TtyColor.Reset); if (opt_line_info) |line_info| { - try out_stream.print("\n"); + try out_stream.print("\n", .{}); if (printLineFromFileAnyOs(out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); @@ -531,13 +550,24 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres else => return err, } } else { - try out_stream.print("\n\n\n"); + try out_stream.print("\n\n\n", .{}); } } else { if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); + try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", .{ + li.file_name, + li.line, + li.column, + relocated_address, + symbol_name, + obj_basename, + }); } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); + try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", .{ + relocated_address, + symbol_name, + obj_basename, + }); } } } @@ -682,16 +712,16 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", .{address}); } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{address}); } return; }; - const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); + const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx)); const compile_unit_name = if (symbol.ofile) |ofile| blk: { - const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); + const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); break :blk fs.path.basename(ofile_path); } else "???"; if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { @@ -708,51 +738,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", .{ + address, symbol_name, compile_unit_name, + }); } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name); - } - }, - else => return err, - } -} - -/// This function works in freestanding mode. -/// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void -pub fn printSourceAtAddressDwarf( - debug_info: *DwarfInfo, - out_stream: var, - address: usize, - tty_color: bool, - comptime printLineFromFile: var, -) !void { - const compile_unit = findCompileUnit(debug_info, address) catch { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); - } - return; - }; - const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); - if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address)) |line_info| { - defer line_info.deinit(); - const symbol_name = getSymbolNameDwarf(debug_info, address) orelse "???"; - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_color, - printLineFromFile, - ); - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); + try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", .{ address, symbol_name, compile_unit_name }); } }, else => return err, @@ -760,7 +750,7 @@ pub fn printSourceAtAddressDwarf( } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); + return debug_info.printSourceAtAddress(out_stream, address, tty_color, printLineFromFileAnyOs); } fn printLineInfo( @@ -773,15 +763,14 @@ fn printLineInfo( comptime printLineFromFile: var, ) !void { if (tty_color) { - try out_stream.print( - WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", + try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", .{ line_info.file_name, line_info.line, line_info.column, address, symbol_name, compile_unit_name, - ); + }); if (printLineFromFile(out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); @@ -799,15 +788,14 @@ fn printLineInfo( else => return err, } } else { - try out_stream.print( - "{}:{}:{}: 0x{x} in {} ({})\n", + try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n", .{ line_info.file_name, line_info.line, line_info.column, address, symbol_name, compile_unit_name, - ); + }); } } @@ -823,10 +811,13 @@ pub const OpenSelfDebugInfoError = error{ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { if (builtin.strip_debug_info) return error.MissingDebugInfo; - if (windows.is_the_target) { + if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) { + return noasync root.os.debug.openSelfDebugInfo(allocator); + } + if (builtin.os == .windows) { return noasync openSelfDebugInfoWindows(allocator); } - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { return noasync openSelfDebugInfoMacOs(allocator); } return noasync openSelfDebugInfoPosix(allocator); @@ -852,7 +843,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const len = try di.coff.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; - const path = try fs.path.resolve(allocator, [_][]const u8{raw_path}); + const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); try di.pdb.openFile(di.coff, path); @@ -861,10 +852,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const signature = try pdb_stream.stream.readIntLittle(u32); const age = try pdb_stream.stream.readIntLittle(u32); var guid: [16]u8 = undefined; - try pdb_stream.stream.readNoEof(guid[0..]); + try pdb_stream.stream.readNoEof(&guid); if (version != 20000404) // VC70, only value observed by LLVM team return error.UnknownPDBVersion; - if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) + if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) return error.PDBMismatch; // We validated the executable and pdb match. @@ -901,7 +892,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { for (present) |_| { const name_offset = try pdb_stream.stream.readIntLittle(u32); const name_index = try pdb_stream.stream.readIntLittle(u32); - const name = mem.toSlice(u8, name_bytes.ptr + name_offset); + const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset)); if (mem.eql(u8, name, "/names")) { break :str_tab_index name_index; } @@ -994,7 +985,7 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { const word = try stream.readIntLittle(u32); var bit_i: u5 = 0; while (true) : (bit_i += 1) { - if (word & (u32(1) << bit_i) != 0) { + if (word & (@as(u32, 1) << bit_i) != 0) { try list.append(word_i * 32 + bit_i); } if (bit_i == maxInt(u5)) break; @@ -1019,8 +1010,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); di.compile_unit_list = ArrayList(CompileUnit).init(allocator); di.func_list = ArrayList(Func).init(allocator); - try scanAllFunctions(di); - try scanAllCompileUnits(di); + try di.scanAllFunctions(); + try di.scanAllCompileUnits(); } pub fn openElfDebugInfo( @@ -1093,7 +1084,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), else => {}, } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 + ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); } else { return error.MissingDebugInfo; }; @@ -1158,7 +1149,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { - var f = try File.openRead(line_info.file_name); + var f = try fs.cwd().openFile(line_info.file_name, .{}); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -1238,6 +1229,494 @@ pub const DwarfInfo = struct { pub fn readString(self: *DwarfInfo) ![]u8 { return readStringRaw(self.allocator(), self.dwarf_in_stream); } + + /// This function works in freestanding mode. + /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void + pub fn printSourceAtAddress( + self: *DwarfInfo, + out_stream: var, + address: usize, + tty_color: bool, + comptime printLineFromFile: var, + ) !void { + const compile_unit = self.findCompileUnit(address) catch { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", .{address}); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{address}); + } + return; + }; + const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); + if (self.getLineNumberInfo(compile_unit.*, address)) |line_info| { + defer line_info.deinit(); + const symbol_name = self.getSymbolName(address) orelse "???"; + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFile, + ); + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", .{ + address, compile_unit_name, + }); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", .{ address, compile_unit_name }); + } + }, + else => return err, + } + } + + fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { + for (di.func_list.toSliceConst()) |*func| { + if (func.pc_range) |range| { + if (address >= range.start and address < range.end) { + return func.name; + } + } + } + + return null; + } + + fn scanAllFunctions(di: *DwarfInfo) !void { + const debug_info_end = di.debug_info.offset + di.debug_info.size; + var this_unit_offset = di.debug_info.offset; + + while (this_unit_offset < debug_info_end) { + try di.dwarf_seekable_stream.seekTo(this_unit_offset); + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + if (unit_length == 0) return; + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try di.dwarf_in_stream.readInt(u16, di.endian); + if (version < 2 or version > 5) return error.InvalidDebugInfo; + + const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); + + const address_size = try di.dwarf_in_stream.readByte(); + if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; + + const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); + const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + + try di.dwarf_seekable_stream.seekTo(compile_unit_pos); + + const next_unit_pos = this_unit_offset + next_offset; + + while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { + const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue; + const after_die_offset = try di.dwarf_seekable_stream.getPos(); + + switch (die_obj.tag_id) { + DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { + const fn_name = x: { + var depth: i32 = 3; + var this_die_obj = die_obj; + // Prenvent endless loops + while (depth > 0) : (depth -= 1) { + if (this_die_obj.getAttr(DW.AT_name)) |_| { + const name = try this_die_obj.getAttrString(di, DW.AT_name); + break :x name; + } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { + // Follow the DIE it points to and repeat + const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); + if (ref_offset > next_offset) return error.InvalidDebugInfo; + try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { + // Follow the DIE it points to and repeat + const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); + if (ref_offset > next_offset) return error.InvalidDebugInfo; + try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + } else { + break :x null; + } + } + + break :x null; + }; + + const pc_range = x: { + if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { + if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { + const pc_end = switch (high_pc_value.*) { + FormValue.Address => |value| value, + FormValue.Const => |value| b: { + const offset = try value.asUnsignedLe(); + break :b (low_pc + offset); + }, + else => return error.InvalidDebugInfo, + }; + break :x PcRange{ + .start = low_pc, + .end = pc_end, + }; + } else { + break :x null; + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + break :x null; + } + }; + + try di.func_list.append(Func{ + .name = fn_name, + .pc_range = pc_range, + }); + }, + else => { + continue; + }, + } + + try di.dwarf_seekable_stream.seekTo(after_die_offset); + } + + this_unit_offset += next_offset; + } + } + + fn scanAllCompileUnits(di: *DwarfInfo) !void { + const debug_info_end = di.debug_info.offset + di.debug_info.size; + var this_unit_offset = di.debug_info.offset; + + while (this_unit_offset < debug_info_end) { + try di.dwarf_seekable_stream.seekTo(this_unit_offset); + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + if (unit_length == 0) return; + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try di.dwarf_in_stream.readInt(u16, di.endian); + if (version < 2 or version > 5) return error.InvalidDebugInfo; + + const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); + + const address_size = try di.dwarf_in_stream.readByte(); + if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; + + const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); + const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + + try di.dwarf_seekable_stream.seekTo(compile_unit_pos); + + const compile_unit_die = try di.allocator().create(Die); + compile_unit_die.* = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + + if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; + + const pc_range = x: { + if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { + if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { + const pc_end = switch (high_pc_value.*) { + FormValue.Address => |value| value, + FormValue.Const => |value| b: { + const offset = try value.asUnsignedLe(); + break :b (low_pc + offset); + }, + else => return error.InvalidDebugInfo, + }; + break :x PcRange{ + .start = low_pc, + .end = pc_end, + }; + } else { + break :x null; + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + break :x null; + } + }; + + try di.compile_unit_list.append(CompileUnit{ + .version = version, + .is_64 = is_64, + .pc_range = pc_range, + .die = compile_unit_die, + }); + + this_unit_offset += next_offset; + } + } + + fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { + for (di.compile_unit_list.toSlice()) |*compile_unit| { + if (compile_unit.pc_range) |range| { + if (target_address >= range.start and target_address < range.end) return compile_unit; + } + if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + var base_address: usize = 0; + if (di.debug_ranges) |debug_ranges| { + try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); + while (true) { + const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); + const end_addr = try di.dwarf_in_stream.readIntLittle(usize); + if (begin_addr == 0 and end_addr == 0) { + break; + } + if (begin_addr == maxInt(usize)) { + base_address = begin_addr; + continue; + } + if (target_address >= begin_addr and target_address < end_addr) { + return compile_unit; + } + } + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + continue; + } + } + return error.MissingDebugInfo; + } + + /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, + /// seeks in the stream and parses it. + fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { + for (di.abbrev_table_list.toSlice()) |*header| { + if (header.offset == abbrev_offset) { + return &header.table; + } + } + try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); + try di.abbrev_table_list.append(AbbrevTableHeader{ + .offset = abbrev_offset, + .table = try di.parseAbbrevTable(), + }); + return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; + } + + fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { + var result = AbbrevTable.init(di.allocator()); + while (true) { + const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); + if (abbrev_code == 0) return result; + try result.append(AbbrevTableEntry{ + .abbrev_code = abbrev_code, + .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), + .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, + .attrs = ArrayList(AbbrevAttr).init(di.allocator()), + }); + const attrs = &result.items[result.len - 1].attrs; + + while (true) { + const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); + const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); + if (attr_id == 0 and form_id == 0) break; + try attrs.append(AbbrevAttr{ + .attr_id = attr_id, + .form_id = form_id, + }); + } + } + } + + fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { + const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); + if (abbrev_code == 0) return null; + const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; + + var result = Die{ + .tag_id = table_entry.tag_id, + .has_children = table_entry.has_children, + .attrs = ArrayList(Die.Attr).init(di.allocator()), + }; + try result.attrs.resize(table_entry.attrs.len); + for (table_entry.attrs.toSliceConst()) |attr, i| { + result.attrs.items[i] = Die.Attr{ + .id = attr.attr_id, + .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), + }; + } + return result; + } + + fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { + const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); + const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); + + assert(line_info_offset < di.debug_line.size); + + try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset); + + var is_64: bool = undefined; + const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + if (unit_length == 0) { + return error.MissingDebugInfo; + } + const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); + + const version = try di.dwarf_in_stream.readInt(u16, di.endian); + // TODO support 3 and 5 + if (version != 2 and version != 4) return error.InvalidDebugInfo; + + const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); + const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; + + const minimum_instruction_length = try di.dwarf_in_stream.readByte(); + if (minimum_instruction_length == 0) return error.InvalidDebugInfo; + + if (version >= 4) { + // maximum_operations_per_instruction + _ = try di.dwarf_in_stream.readByte(); + } + + const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; + const line_base = try di.dwarf_in_stream.readByteSigned(); + + const line_range = try di.dwarf_in_stream.readByte(); + if (line_range == 0) return error.InvalidDebugInfo; + + const opcode_base = try di.dwarf_in_stream.readByte(); + + const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); + + { + var i: usize = 0; + while (i < opcode_base - 1) : (i += 1) { + standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); + } + } + + var include_directories = ArrayList([]u8).init(di.allocator()); + try include_directories.append(compile_unit_cwd); + while (true) { + const dir = try di.readString(); + if (dir.len == 0) break; + try include_directories.append(dir); + } + + var file_entries = ArrayList(FileEntry).init(di.allocator()); + var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); + + while (true) { + const file_name = try di.readString(); + if (file_name.len == 0) break; + const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); + const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); + const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + } + + try di.dwarf_seekable_stream.seekTo(prog_start_offset); + + while (true) { + const opcode = try di.dwarf_in_stream.readByte(); + + if (opcode == DW.LNS_extended_op) { + const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); + if (op_size < 1) return error.InvalidDebugInfo; + var sub_op = try di.dwarf_in_stream.readByte(); + switch (sub_op) { + DW.LNE_end_sequence => { + prog.end_sequence = true; + if (try prog.checkLineMatch()) |info| return info; + return error.MissingDebugInfo; + }, + DW.LNE_set_address => { + const addr = try di.dwarf_in_stream.readInt(usize, di.endian); + prog.address = addr; + }, + DW.LNE_define_file => { + const file_name = try di.readString(); + const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); + const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); + const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + }, + else => { + const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; + try di.dwarf_seekable_stream.seekBy(fwd_amt); + }, + } + } else if (opcode >= opcode_base) { + // special opcodes + const adjusted_opcode = opcode - opcode_base; + const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); + const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); + prog.line += inc_line; + prog.address += inc_addr; + if (try prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + } else { + switch (opcode) { + DW.LNS_copy => { + if (try prog.checkLineMatch()) |info| return info; + prog.basic_block = false; + }, + DW.LNS_advance_pc => { + const arg = try leb.readULEB128(usize, di.dwarf_in_stream); + prog.address += arg * minimum_instruction_length; + }, + DW.LNS_advance_line => { + const arg = try leb.readILEB128(i64, di.dwarf_in_stream); + prog.line += arg; + }, + DW.LNS_set_file => { + const arg = try leb.readULEB128(usize, di.dwarf_in_stream); + prog.file = arg; + }, + DW.LNS_set_column => { + const arg = try leb.readULEB128(u64, di.dwarf_in_stream); + prog.column = arg; + }, + DW.LNS_negate_stmt => { + prog.is_stmt = !prog.is_stmt; + }, + DW.LNS_set_basic_block => { + prog.basic_block = true; + }, + DW.LNS_const_add_pc => { + const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); + prog.address += inc_addr; + }, + DW.LNS_fixed_advance_pc => { + const arg = try di.dwarf_in_stream.readInt(u16, di.endian); + prog.address += arg; + }, + DW.LNS_set_prologue_end => {}, + else => { + if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; + const len_bytes = standard_opcode_lengths[opcode - 1]; + try di.dwarf_seekable_stream.seekBy(len_bytes); + }, + } + } + } + + return error.MissingDebugInfo; + } + + fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + const pos = di.debug_str.offset + offset; + try di.dwarf_seekable_stream.seekTo(pos); + return di.readString(); + } }; pub const DebugInfo = switch (builtin.os) { @@ -1263,8 +1742,7 @@ pub const DebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - .linux, .freebsd, .netbsd => DwarfInfo, - else => @compileError("Unsupported OS"), + else => DwarfInfo, }; const PcRange = struct { @@ -1376,7 +1854,7 @@ const Die = struct { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, - FormValue.StrPtr => |offset| getString(di, offset), + FormValue.StrPtr => |offset| di.getString(offset), else => error.InvalidDebugInfo, }; } @@ -1457,7 +1935,7 @@ const LineNumberProgram = struct { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try fs.path.join(self.file_entries.allocator, [_][]const u8{ dir_name, file_entry.file_name }); + const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name }); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, @@ -1489,12 +1967,6 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { return buf.toSlice(); } -fn getString(di: *DwarfInfo, offset: u64) ![]u8 { - const pos = di.debug_str.offset + offset; - try di.dwarf_seekable_stream.seekTo(pos); - return di.readString(); -} - // TODO the noasyncs here are workarounds fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { const buf = try allocator.alloc(u8, size); @@ -1542,13 +2014,14 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo // TODO the noasyncs here are workarounds fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try noasync in_stream.readIntLittle(u64) else u64(try noasync in_stream.readIntLittle(u32)); + return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32)); } // TODO the noasyncs here are workarounds fn parseFormValueTargetAddrSize(in_stream: var) !u64 { if (@sizeOf(usize) == 4) { - return u64(try noasync in_stream.readIntLittle(u32)); + // TODO this cast should not be needed + return @as(u64, try noasync in_stream.readIntLittle(u32)); } else if (@sizeOf(usize) == 8) { return noasync in_stream.readIntLittle(u64); } else { @@ -1611,7 +2084,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = try noasync leb.readULEB128(u64, in_stream); - const F = @typeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64)); + const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64)); var frame = try allocator.create(F); defer allocator.destroy(frame); return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64); @@ -1620,47 +2093,6 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 }; } -fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { - var result = AbbrevTable.init(di.allocator()); - while (true) { - const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); - if (abbrev_code == 0) return result; - try result.append(AbbrevTableEntry{ - .abbrev_code = abbrev_code, - .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), - .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, - .attrs = ArrayList(AbbrevAttr).init(di.allocator()), - }); - const attrs = &result.items[result.len - 1].attrs; - - while (true) { - const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); - const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); - if (attr_id == 0 and form_id == 0) break; - try attrs.append(AbbrevAttr{ - .attr_id = attr_id, - .form_id = form_id, - }); - } - } -} - -/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, -/// seeks in the stream and parses it. -fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { - for (di.abbrev_table_list.toSlice()) |*header| { - if (header.offset == abbrev_offset) { - return &header.table; - } - } - try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); - try di.abbrev_table_list.append(AbbrevTableHeader{ - .offset = abbrev_offset, - .table = try parseAbbrevTable(di), - }); - return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; -} - fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { for (abbrev_table.toSliceConst()) |*table_entry| { if (table_entry.abbrev_code == abbrev_code) return table_entry; @@ -1668,35 +2100,20 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con return null; } -fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { - const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); - if (abbrev_code == 0) return null; - const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; - - var result = Die{ - .tag_id = table_entry.tag_id, - .has_children = table_entry.has_children, - .attrs = ArrayList(Die.Attr).init(di.allocator()), - }; - try result.attrs.resize(table_entry.attrs.len); - for (table_entry.attrs.toSliceConst()) |attr, i| { - result.attrs.items[i] = Die.Attr{ - .id = attr.attr_id, - .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), - }; - } - return result; -} - fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { const ofile = symbol.ofile orelse return error.MissingDebugInfo; const gop = try di.ofiles.getOrPut(ofile); const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { errdefer _ = di.ofiles.remove(ofile); - const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); + const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); gop.kv.value = MachOFile{ - .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)), + .bytes = try std.fs.cwd().readFileAllocAligned( + di.ofiles.allocator, + ofile_path, + maxInt(usize), + @alignOf(macho.mach_header_64), + ), .sect_debug_info = null, .sect_debug_line = null, }; @@ -1712,7 +2129,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), else => {}, } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 + ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); } else { return error.MissingDebugInfo; }; @@ -1721,7 +2138,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) { - const sect_name = mem.toSliceConst(u8, §.sectname); + const sect_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, §.sectname)); if (mem.eql(u8, sect_name, "__debug_line")) { gop.kv.value.sect_debug_line = sect; } else if (mem.eql(u8, sect_name, "__debug_info")) { @@ -1832,7 +2249,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u // special opcodes const adjusted_opcode = opcode - opcode_base; const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); - const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); + const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); prog.line += inc_line; prog.address += inc_addr; if (try prog.checkLineMatch()) |info| return info; @@ -1886,388 +2303,11 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u return error.MissingDebugInfo; } -fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { - const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); - const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); - - assert(line_info_offset < di.debug_line.size); - - try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset); - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); - if (unit_length == 0) { - return error.MissingDebugInfo; - } - const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); - - const version = try di.dwarf_in_stream.readInt(u16, di.endian); - // TODO support 3 and 5 - if (version != 2 and version != 4) return error.InvalidDebugInfo; - - const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); - const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; - - const minimum_instruction_length = try di.dwarf_in_stream.readByte(); - if (minimum_instruction_length == 0) return error.InvalidDebugInfo; - - if (version >= 4) { - // maximum_operations_per_instruction - _ = try di.dwarf_in_stream.readByte(); - } - - const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; - const line_base = try di.dwarf_in_stream.readByteSigned(); - - const line_range = try di.dwarf_in_stream.readByte(); - if (line_range == 0) return error.InvalidDebugInfo; - - const opcode_base = try di.dwarf_in_stream.readByte(); - - const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); - - { - var i: usize = 0; - while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); - } - } - - var include_directories = ArrayList([]u8).init(di.allocator()); - try include_directories.append(compile_unit_cwd); - while (true) { - const dir = try di.readString(); - if (dir.len == 0) break; - try include_directories.append(dir); - } - - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); - - while (true) { - const file_name = try di.readString(); - if (file_name.len == 0) break; - const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); - const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); - const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - } - - try di.dwarf_seekable_stream.seekTo(prog_start_offset); - - while (true) { - const opcode = try di.dwarf_in_stream.readByte(); - - if (opcode == DW.LNS_extended_op) { - const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); - if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = try di.dwarf_in_stream.readByte(); - switch (sub_op) { - DW.LNE_end_sequence => { - prog.end_sequence = true; - if (try prog.checkLineMatch()) |info| return info; - return error.MissingDebugInfo; - }, - DW.LNE_set_address => { - const addr = try di.dwarf_in_stream.readInt(usize, di.endian); - prog.address = addr; - }, - DW.LNE_define_file => { - const file_name = try di.readString(); - const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); - const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); - const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - }, - else => { - const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekBy(fwd_amt); - }, - } - } else if (opcode >= opcode_base) { - // special opcodes - const adjusted_opcode = opcode - opcode_base; - const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); - const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); - prog.line += inc_line; - prog.address += inc_addr; - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - } else { - switch (opcode) { - DW.LNS_copy => { - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - }, - DW.LNS_advance_pc => { - const arg = try leb.readULEB128(usize, di.dwarf_in_stream); - prog.address += arg * minimum_instruction_length; - }, - DW.LNS_advance_line => { - const arg = try leb.readILEB128(i64, di.dwarf_in_stream); - prog.line += arg; - }, - DW.LNS_set_file => { - const arg = try leb.readULEB128(usize, di.dwarf_in_stream); - prog.file = arg; - }, - DW.LNS_set_column => { - const arg = try leb.readULEB128(u64, di.dwarf_in_stream); - prog.column = arg; - }, - DW.LNS_negate_stmt => { - prog.is_stmt = !prog.is_stmt; - }, - DW.LNS_set_basic_block => { - prog.basic_block = true; - }, - DW.LNS_const_add_pc => { - const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); - prog.address += inc_addr; - }, - DW.LNS_fixed_advance_pc => { - const arg = try di.dwarf_in_stream.readInt(u16, di.endian); - prog.address += arg; - }, - DW.LNS_set_prologue_end => {}, - else => { - if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; - const len_bytes = standard_opcode_lengths[opcode - 1]; - try di.dwarf_seekable_stream.seekBy(len_bytes); - }, - } - } - } - - return error.MissingDebugInfo; -} - const Func = struct { pc_range: ?PcRange, name: ?[]u8, }; -fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 { - for (di.func_list.toSliceConst()) |*func| { - if (func.pc_range) |range| { - if (address >= range.start and address < range.end) { - return func.name; - } - } - } - - return null; -} - -fn scanAllFunctions(di: *DwarfInfo) !void { - const debug_info_end = di.debug_info.offset + di.debug_info.size; - var this_unit_offset = di.debug_info.offset; - - while (this_unit_offset < debug_info_end) { - try di.dwarf_seekable_stream.seekTo(this_unit_offset); - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); - if (unit_length == 0) return; - const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); - - const version = try di.dwarf_in_stream.readInt(u16, di.endian); - if (version < 2 or version > 5) return error.InvalidDebugInfo; - - const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); - - const address_size = try di.dwarf_in_stream.readByte(); - if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - - const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); - const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); - - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - - const next_unit_pos = this_unit_offset + next_offset; - - while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { - const die_obj = (try parseDie(di, abbrev_table, is_64)) orelse continue; - const after_die_offset = try di.dwarf_seekable_stream.getPos(); - - switch (die_obj.tag_id) { - DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { - const fn_name = x: { - var depth: i32 = 3; - var this_die_obj = die_obj; - // Prenvent endless loops - while (depth > 0) : (depth -= 1) { - if (this_die_obj.getAttr(DW.AT_name)) |_| { - const name = try this_die_obj.getAttrString(di, DW.AT_name); - break :x name; - } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { - // Follow the DIE it points to and repeat - const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); - if (ref_offset > next_offset) return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { - // Follow the DIE it points to and repeat - const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); - if (ref_offset > next_offset) return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - } else { - break :x null; - } - } - - break :x null; - }; - - const pc_range = x: { - if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (high_pc_value.*) { - FormValue.Address => |value| value, - FormValue.Const => |value| b: { - const offset = try value.asUnsignedLe(); - break :b (low_pc + offset); - }, - else => return error.InvalidDebugInfo, - }; - break :x PcRange{ - .start = low_pc, - .end = pc_end, - }; - } else { - break :x null; - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - break :x null; - } - }; - - try di.func_list.append(Func{ - .name = fn_name, - .pc_range = pc_range, - }); - }, - else => { - continue; - }, - } - - try di.dwarf_seekable_stream.seekTo(after_die_offset); - } - - this_unit_offset += next_offset; - } -} - -fn scanAllCompileUnits(di: *DwarfInfo) !void { - const debug_info_end = di.debug_info.offset + di.debug_info.size; - var this_unit_offset = di.debug_info.offset; - - while (this_unit_offset < debug_info_end) { - try di.dwarf_seekable_stream.seekTo(this_unit_offset); - - var is_64: bool = undefined; - const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); - if (unit_length == 0) return; - const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); - - const version = try di.dwarf_in_stream.readInt(u16, di.endian); - if (version < 2 or version > 5) return error.InvalidDebugInfo; - - const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); - - const address_size = try di.dwarf_in_stream.readByte(); - if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - - const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); - const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); - - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - - const compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; - - if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - - const pc_range = x: { - if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (high_pc_value.*) { - FormValue.Address => |value| value, - FormValue.Const => |value| b: { - const offset = try value.asUnsignedLe(); - break :b (low_pc + offset); - }, - else => return error.InvalidDebugInfo, - }; - break :x PcRange{ - .start = low_pc, - .end = pc_end, - }; - } else { - break :x null; - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - break :x null; - } - }; - - try di.compile_unit_list.append(CompileUnit{ - .version = version, - .is_64 = is_64, - .pc_range = pc_range, - .die = compile_unit_die, - }); - - this_unit_offset += next_offset; - } -} - -fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { - for (di.compile_unit_list.toSlice()) |*compile_unit| { - if (compile_unit.pc_range) |range| { - if (target_address >= range.start and target_address < range.end) return compile_unit; - } - if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { - var base_address: usize = 0; - if (di.debug_ranges) |debug_ranges| { - try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); - while (true) { - const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); - const end_addr = try di.dwarf_in_stream.readIntLittle(usize); - if (begin_addr == 0 and end_addr == 0) { - break; - } - if (begin_addr == maxInt(usize)) { - base_address = begin_addr; - continue; - } - if (target_address >= begin_addr and target_address < end_addr) { - return compile_unit; - } - } - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - continue; - } - } - return error.MissingDebugInfo; -} - fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { // TODO https://github.com/ziglang/zig/issues/863 const size = (T.bit_count + 7) / 8; @@ -2298,12 +2338,13 @@ fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { } else { if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; ptr.* += 4; - return u64(first_32_bits); + // TODO this cast should not be needed + return @as(u64, first_32_bits); } } -fn readStringMem(ptr: *[*]const u8) []const u8 { - const result = mem.toSliceConst(u8, ptr.*); +fn readStringMem(ptr: *[*]const u8) [:0]const u8 { + const result = mem.toSliceConst(u8, @ptrCast([*:0]const u8, ptr.*)); ptr.* += result.len + 1; return result; } @@ -2315,7 +2356,8 @@ fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) return in_stream.readIntLittle(u64); } else { if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; - return u64(first_32_bits); + // TODO this cast should not be needed + return @as(u64, first_32_bits); } } @@ -2330,7 +2372,7 @@ var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; fn getDebugInfoAllocator() *mem.Allocator { if (debug_info_allocator) |a| return a; - debug_info_arena_allocator = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + debug_info_arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator); debug_info_allocator = &debug_info_arena_allocator.allocator; return &debug_info_arena_allocator.allocator; } @@ -2355,7 +2397,7 @@ pub fn attachSegfaultHandler() void { if (!have_segfault_handling_support) { @compileError("segfault handler not supported for this target"); } - if (windows.is_the_target) { + if (builtin.os == .windows) { windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); return; } @@ -2366,10 +2408,11 @@ pub fn attachSegfaultHandler() void { }; os.sigaction(os.SIGSEGV, &act, null); + os.sigaction(os.SIGILL, &act, null); } fn resetSegfaultHandler() void { - if (windows.is_the_target) { + if (builtin.os == .windows) { if (windows_segfault_handle) |handle| { assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0); windows_segfault_handle = null; @@ -2382,18 +2425,28 @@ fn resetSegfaultHandler() void { .flags = 0, }; os.sigaction(os.SIGSEGV, &act, null); + os.sigaction(os.SIGILL, &act, null); } -extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn { +fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) callconv(.C) noreturn { // Reset to the default handler so that if a segfault happens in this handler it will crash // the process. Also when this handler returns, the original instruction will be repeated // and the resulting segfault will crash the process rather than continually dump stack traces. resetSegfaultHandler(); const addr = @ptrToInt(info.fields.sigfault.addr); - std.debug.warn("Segmentation fault at address 0x{x}\n", addr); - + switch (sig) { + os.SIGSEGV => std.debug.warn("Segmentation fault at address 0x{x}\n", .{addr}), + os.SIGILL => std.debug.warn("Illegal instruction at address 0x{x}\n", .{addr}), + else => unreachable, + } switch (builtin.arch) { + .i386 => { + const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); + const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_EIP]); + const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_EBP]); + dumpStackTraceFromBase(bp, ip); + }, .x86_64 => { const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]); @@ -2422,13 +2475,13 @@ extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *con os.abort(); } -stdcallcc fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) c_long { +fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.Stdcall) c_long { const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress); switch (info.ExceptionRecord.ExceptionCode) { - windows.EXCEPTION_DATATYPE_MISALIGNMENT => panicExtra(null, exception_address, "Unaligned Memory Access"), - windows.EXCEPTION_ACCESS_VIOLATION => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", info.ExceptionRecord.ExceptionInformation[1]), - windows.EXCEPTION_ILLEGAL_INSTRUCTION => panicExtra(null, exception_address, "Illegal Instruction"), - windows.EXCEPTION_STACK_OVERFLOW => panicExtra(null, exception_address, "Stack Overflow"), + windows.EXCEPTION_DATATYPE_MISALIGNMENT => panicExtra(null, exception_address, "Unaligned Memory Access", .{}), + windows.EXCEPTION_ACCESS_VIOLATION => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", .{info.ExceptionRecord.ExceptionInformation[1]}), + windows.EXCEPTION_ILLEGAL_INSTRUCTION => panicExtra(null, exception_address, "Illegal Instruction", .{}), + windows.EXCEPTION_STACK_OVERFLOW => panicExtra(null, exception_address, "Stack Overflow", .{}), else => return windows.EXCEPTION_CONTINUE_SEARCH, } } @@ -2437,5 +2490,10 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { const sp = asm ("" : [argc] "={rsp}" (-> usize) ); - std.debug.warn("{} sp = 0x{x}\n", prefix, sp); + std.debug.warn("{} sp = 0x{x}\n", .{ prefix, sp }); +} + +// Reference everything so it gets tested. +test "" { + _ = leb; } diff --git a/lib/std/debug/failing_allocator.zig b/lib/std/debug/failing_allocator.zig index 5776d2319..081a29cd9 100644 --- a/lib/std/debug/failing_allocator.zig +++ b/lib/std/debug/failing_allocator.zig @@ -3,6 +3,14 @@ const mem = std.mem; /// Allocator that fails after N allocations, useful for making sure out of /// memory conditions are handled correctly. +/// +/// To use this, first initialize it and get an allocator with +/// +/// `const failing_allocator = &FailingAllocator.init(, +/// ).allocator;` +/// +/// Then use `failing_allocator` anywhere you would have used a +/// different allocator. pub const FailingAllocator = struct { allocator: mem.Allocator, index: usize, @@ -13,6 +21,14 @@ pub const FailingAllocator = struct { allocations: usize, deallocations: usize, + /// `fail_index` is the number of successful allocations you can + /// expect from this allocator. The next allocation will fail. + /// For example, if this is called with `fail_index` equal to 2, + /// the following test will pass: + /// + /// var a = try failing_alloc.create(i32); + /// var b = try failing_alloc.create(i32); + /// testing.expectError(error.OutOfMemory, failing_alloc.create(i32)); pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator { return FailingAllocator{ .internal_allocator = allocator, diff --git a/lib/std/debug/leb128.zig b/lib/std/debug/leb128.zig index cb59c5b0d..dba57e1f9 100644 --- a/lib/std/debug/leb128.zig +++ b/lib/std/debug/leb128.zig @@ -62,13 +62,13 @@ pub fn readILEB128(comptime T: type, in_stream: var) !T { var shift: usize = 0; while (true) { - const byte = u8(try in_stream.readByte()); + const byte: u8 = try in_stream.readByte(); if (shift > T.bit_count) return error.Overflow; var operand: UT = undefined; - if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) { + if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) { if (byte != 0x7f) return error.Overflow; } @@ -101,7 +101,7 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T { return error.Overflow; var operand: UT = undefined; - if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) { + if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) { if (byte != 0x7f) return error.Overflow; } diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 341378801..db912ec92 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -7,11 +7,13 @@ const assert = std.debug.assert; const testing = std.testing; const elf = std.elf; const windows = std.os.windows; +const system = std.os.system; const maxInt = std.math.maxInt; pub const DynLib = switch (builtin.os) { - .linux => LinuxDynLib, + .linux => if (builtin.link_libc) DlDynlib else LinuxDynLib, .windows => WindowsDynLib, + .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, else => void, }; @@ -22,7 +24,7 @@ pub const DynLib = switch (builtin.os) { // fashion. const LinkMap = extern struct { l_addr: usize, - l_name: [*]const u8, + l_name: [*:0]const u8, l_ld: ?*elf.Dyn, l_next: ?*LinkMap, l_prev: ?*LinkMap, @@ -99,12 +101,14 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { } pub const LinuxDynLib = struct { + pub const Error = ElfLib.Error; + elf_lib: ElfLib, fd: i32, memory: []align(mem.page_size) u8, /// Trusts the file - pub fn open(path: []const u8) !DynLib { + pub fn open(path: []const u8) !LinuxDynLib { const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); errdefer os.close(fd); @@ -121,26 +125,44 @@ pub const LinuxDynLib = struct { ); errdefer os.munmap(bytes); - return DynLib{ + return LinuxDynLib{ .elf_lib = try ElfLib.init(bytes), .fd = fd, .memory = bytes, }; } - pub fn close(self: *DynLib) void { + pub fn openC(path_c: [*:0]const u8) !LinuxDynLib { + return open(mem.toSlice(u8, path_c)); + } + + pub fn close(self: *LinuxDynLib) void { os.munmap(self.memory); os.close(self.fd); self.* = undefined; } - pub fn lookup(self: *DynLib, name: []const u8) ?usize { - return self.elf_lib.lookup("", name); + pub fn lookup(self: *LinuxDynLib, comptime T: type, name: [:0]const u8) ?T { + if (self.elf_lib.lookup("", name)) |symbol| { + return @intToPtr(T, symbol); + } else { + return null; + } } }; pub const ElfLib = struct { - strings: [*]u8, + pub const Error = error{ + NotElfFile, + NotDynamicLibrary, + MissingDynamicLinkingInformation, + BaseNotFound, + ElfStringSectionNotFound, + ElfSymSectionNotFound, + ElfHashTableNotFound, + }; + + strings: [*:0]u8, syms: [*]elf.Sym, hashtab: [*]os.Elf_Symndx, versym: ?[*]u16, @@ -151,7 +173,7 @@ pub const ElfLib = struct { pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { const eh = @ptrCast(*elf.Ehdr, bytes.ptr); if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; - if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary; + if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; const elf_addr = @ptrToInt(bytes.ptr); var ph_addr: usize = elf_addr + eh.e_phoff; @@ -175,7 +197,7 @@ pub const ElfLib = struct { const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; if (base == maxInt(usize)) return error.BaseNotFound; - var maybe_strings: ?[*]u8 = null; + var maybe_strings: ?[*:0]u8 = null; var maybe_syms: ?[*]elf.Sym = null; var maybe_hashtab: ?[*]os.Elf_Symndx = null; var maybe_versym: ?[*]u16 = null; @@ -186,7 +208,7 @@ pub const ElfLib = struct { while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_STRTAB => maybe_strings = @intToPtr([*:0]u8, p), elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), elf.DT_HASH => maybe_hashtab = @intToPtr([*]os.Elf_Symndx, p), elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), @@ -215,8 +237,8 @@ pub const ElfLib = struct { var i: usize = 0; while (i < self.hashtab[1]) : (i += 1) { - if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info & 0xf) & OK_TYPES)) continue; - if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == (@as(u32, 1) << @intCast(u5, self.syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (@as(u32, 1) << @intCast(u5, self.syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == self.syms[i].st_shndx) continue; if (!mem.eql(u8, name, mem.toSliceConst(u8, self.strings + self.syms[i].st_name))) continue; if (maybe_versym) |versym| { @@ -230,7 +252,7 @@ pub const ElfLib = struct { } }; -fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*:0]u8) bool { var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { @@ -245,13 +267,24 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ } pub const WindowsDynLib = struct { + pub const Error = error{FileNotFound}; + dll: windows.HMODULE, pub fn open(path: []const u8) !WindowsDynLib { - const wpath = try windows.sliceToPrefixedFileW(path); + const path_w = try windows.sliceToPrefixedFileW(path); + return openW(&path_w); + } + pub fn openC(path_c: [*:0]const u8) !WindowsDynLib { + const path_w = try windows.cStrToPrefixedFileW(path); + return openW(&path_w); + } + + pub fn openW(path_w: [*:0]const u16) !WindowsDynLib { return WindowsDynLib{ - .dll = try windows.LoadLibraryW(&wpath), + // + 4 to skip over the \??\ + .dll = try windows.LoadLibraryW(path_w + 4), }; } @@ -260,21 +293,59 @@ pub const WindowsDynLib = struct { self.* = undefined; } - pub fn lookup(self: *WindowsDynLib, name: []const u8) ?usize { - return @ptrToInt(windows.kernel32.GetProcAddress(self.dll, name.ptr)); + pub fn lookup(self: *WindowsDynLib, comptime T: type, name: [:0]const u8) ?T { + if (windows.kernel32.GetProcAddress(self.dll, name.ptr)) |addr| { + return @ptrCast(T, addr); + } else { + return null; + } + } +}; + +pub const DlDynlib = struct { + pub const Error = error{FileNotFound}; + + handle: *c_void, + + pub fn open(path: []const u8) !DlDynlib { + const path_c = try os.toPosixPath(path); + return openC(&path_c); + } + + pub fn openC(path_c: [*:0]const u8) !DlDynlib { + return DlDynlib{ + .handle = system.dlopen(path_c, system.RTLD_LAZY) orelse { + return error.FileNotFound; + }, + }; + } + + pub fn close(self: *DlDynlib) void { + _ = system.dlclose(self.handle); + self.* = undefined; + } + + pub fn lookup(self: *DlDynlib, comptime T: type, name: [:0]const u8) ?T { + // dlsym (and other dl-functions) secretly take shadow parameter - return address on stack + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826 + if (@call(.{ .modifier = .never_tail }, system.dlsym, .{ self.handle, name.ptr })) |symbol| { + return @ptrCast(T, symbol); + } else { + return null; + } } }; test "dynamic_library" { const libname = switch (builtin.os) { - .linux => "invalid_so.so", + .linux, .freebsd => "invalid_so.so", .windows => "invalid_dll.dll", - else => return, + .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib", + else => return error.SkipZigTest, }; const dynlib = DynLib.open(libname) catch |err| { testing.expect(err == error.FileNotFound); return; }; - @panic("Expected error from function"); } diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 37635895f..dc1638c5c 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -306,40 +306,31 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; -/// An unknown type. -pub const ET_NONE = 0; +/// File types +pub const ET = extern enum(u16) { + /// No file type + NONE = 0, -/// A relocatable file. -pub const ET_REL = 1; + /// Relocatable file + REL = 1, -/// An executable file. -pub const ET_EXEC = 2; + /// Executable file + EXEC = 2, -/// A shared object. -pub const ET_DYN = 3; + /// Shared object file + DYN = 3, -/// A core file. -pub const ET_CORE = 4; + /// Core file + CORE = 4, -pub const FileType = enum { - Relocatable, - Executable, - Shared, - Core, -}; - -pub const Arch = enum { - Sparc, - x86, - Mips, - PowerPc, - Arm, - SuperH, - IA_64, - x86_64, - AArch64, + /// Beginning of processor-specific codes + pub const LOPROC = 0xff00; + + /// Processor-specific + pub const HIPROC = 0xffff; }; +/// TODO delete this in favor of Elf64_Shdr pub const SectionHeader = struct { name: u32, sh_type: u32, @@ -358,8 +349,8 @@ pub const Elf = struct { in_stream: *io.InStream(anyerror), is_64: bool, endian: builtin.Endian, - file_type: FileType, - arch: Arch, + file_type: ET, + arch: EM, entry_addr: u64, program_header_offset: u64, section_header_offset: u64, @@ -390,7 +381,7 @@ pub const Elf = struct { var magic: [4]u8 = undefined; try in.readNoEof(magic[0..]); - if (!mem.eql(u8, magic, "\x7fELF")) return error.InvalidFormat; + if (!mem.eql(u8, &magic, "\x7fELF")) return error.InvalidFormat; elf.is_64 = switch (try in.readByte()) { 1 => false, @@ -410,26 +401,8 @@ pub const Elf = struct { // skip over padding try seekable_stream.seekBy(9); - elf.file_type = switch (try in.readInt(u16, elf.endian)) { - 1 => FileType.Relocatable, - 2 => FileType.Executable, - 3 => FileType.Shared, - 4 => FileType.Core, - else => return error.InvalidFormat, - }; - - elf.arch = switch (try in.readInt(u16, elf.endian)) { - 0x02 => Arch.Sparc, - 0x03 => Arch.x86, - 0x08 => Arch.Mips, - 0x14 => Arch.PowerPc, - 0x28 => Arch.Arm, - 0x2A => Arch.SuperH, - 0x32 => Arch.IA_64, - 0x3E => Arch.x86_64, - 0xb7 => Arch.AArch64, - else => return error.InvalidFormat, - }; + elf.file_type = try in.readEnum(ET, elf.endian); + elf.arch = try in.readEnum(EM, elf.endian); const elf_version = try in.readInt(u32, elf.endian); if (elf_version != 1) return error.InvalidFormat; @@ -439,9 +412,9 @@ pub const Elf = struct { elf.program_header_offset = try in.readInt(u64, elf.endian); elf.section_header_offset = try in.readInt(u64, elf.endian); } else { - elf.entry_addr = u64(try in.readInt(u32, elf.endian)); - elf.program_header_offset = u64(try in.readInt(u32, elf.endian)); - elf.section_header_offset = u64(try in.readInt(u32, elf.endian)); + elf.entry_addr = @as(u64, try in.readInt(u32, elf.endian)); + elf.program_header_offset = @as(u64, try in.readInt(u32, elf.endian)); + elf.section_header_offset = @as(u64, try in.readInt(u32, elf.endian)); } // skip over flags @@ -456,13 +429,13 @@ pub const Elf = struct { const ph_entry_count = try in.readInt(u16, elf.endian); const sh_entry_size = try in.readInt(u16, elf.endian); const sh_entry_count = try in.readInt(u16, elf.endian); - elf.string_section_index = usize(try in.readInt(u16, elf.endian)); + elf.string_section_index = @as(usize, try in.readInt(u16, elf.endian)); if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; - const sh_byte_count = u64(sh_entry_size) * u64(sh_entry_count); + const sh_byte_count = @as(u64, sh_entry_size) * @as(u64, sh_entry_count); const end_sh = try math.add(u64, elf.section_header_offset, sh_byte_count); - const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count); + const ph_byte_count = @as(u64, ph_entry_size) * @as(u64, ph_entry_count); const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count); const stream_end = try seekable_stream.getEndPos(); @@ -497,14 +470,14 @@ pub const Elf = struct { // TODO (multiple occurrences) allow implicit cast from %u32 -> %u64 ? elf_section.name = try in.readInt(u32, elf.endian); elf_section.sh_type = try in.readInt(u32, elf.endian); - elf_section.flags = u64(try in.readInt(u32, elf.endian)); - elf_section.addr = u64(try in.readInt(u32, elf.endian)); - elf_section.offset = u64(try in.readInt(u32, elf.endian)); - elf_section.size = u64(try in.readInt(u32, elf.endian)); + elf_section.flags = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.addr = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.offset = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.size = @as(u64, try in.readInt(u32, elf.endian)); elf_section.link = try in.readInt(u32, elf.endian); elf_section.info = try in.readInt(u32, elf.endian); - elf_section.addr_align = u64(try in.readInt(u32, elf.endian)); - elf_section.ent_size = u64(try in.readInt(u32, elf.endian)); + elf_section.addr_align = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.ent_size = @as(u64, try in.readInt(u32, elf.endian)); } } @@ -575,8 +548,8 @@ pub const Elf32_Versym = Elf32_Half; pub const Elf64_Versym = Elf64_Half; pub const Elf32_Ehdr = extern struct { e_ident: [EI_NIDENT]u8, - e_type: Elf32_Half, - e_machine: Elf32_Half, + e_type: ET, + e_machine: EM, e_version: Elf32_Word, e_entry: Elf32_Addr, e_phoff: Elf32_Off, @@ -591,8 +564,8 @@ pub const Elf32_Ehdr = extern struct { }; pub const Elf64_Ehdr = extern struct { e_ident: [EI_NIDENT]u8, - e_type: Elf64_Half, - e_machine: Elf64_Half, + e_type: ET, + e_machine: EM, e_version: Elf64_Word, e_entry: Elf64_Addr, e_phoff: Elf64_Off, @@ -900,3 +873,649 @@ pub const Verdaux = switch (@sizeOf(usize)) { 8 => Elf64_Verdaux, else => @compileError("expected pointer size of 32 or 64"), }; + +/// Machine architectures +/// See current registered ELF machine architectures at: +/// http://www.uxsglobal.com/developers/gabi/latest/ch4.eheader.html +/// The underscore prefix is because many of these start with numbers. +pub const EM = extern enum(u16) { + /// No machine + _NONE = 0, + + /// AT&T WE 32100 + _M32 = 1, + + /// SPARC + _SPARC = 2, + + /// Intel 386 + _386 = 3, + + /// Motorola 68000 + _68K = 4, + + /// Motorola 88000 + _88K = 5, + + /// Intel MCU + _IAMCU = 6, + + /// Intel 80860 + _860 = 7, + + /// MIPS R3000 + _MIPS = 8, + + /// IBM System/370 + _S370 = 9, + + /// MIPS RS3000 Little-endian + _MIPS_RS3_LE = 10, + + /// Hewlett-Packard PA-RISC + _PARISC = 15, + + /// Fujitsu VPP500 + _VPP500 = 17, + + /// Enhanced instruction set SPARC + _SPARC32PLUS = 18, + + /// Intel 80960 + _960 = 19, + + /// PowerPC + _PPC = 20, + + /// PowerPC64 + _PPC64 = 21, + + /// IBM System/390 + _S390 = 22, + + /// IBM SPU/SPC + _SPU = 23, + + /// NEC V800 + _V800 = 36, + + /// Fujitsu FR20 + _FR20 = 37, + + /// TRW RH-32 + _RH32 = 38, + + /// Motorola RCE + _RCE = 39, + + /// ARM + _ARM = 40, + + /// DEC Alpha + _ALPHA = 41, + + /// Hitachi SH + _SH = 42, + + /// SPARC V9 + _SPARCV9 = 43, + + /// Siemens TriCore + _TRICORE = 44, + + /// Argonaut RISC Core + _ARC = 45, + + /// Hitachi H8/300 + _H8_300 = 46, + + /// Hitachi H8/300H + _H8_300H = 47, + + /// Hitachi H8S + _H8S = 48, + + /// Hitachi H8/500 + _H8_500 = 49, + + /// Intel IA-64 processor architecture + _IA_64 = 50, + + /// Stanford MIPS-X + _MIPS_X = 51, + + /// Motorola ColdFire + _COLDFIRE = 52, + + /// Motorola M68HC12 + _68HC12 = 53, + + /// Fujitsu MMA Multimedia Accelerator + _MMA = 54, + + /// Siemens PCP + _PCP = 55, + + /// Sony nCPU embedded RISC processor + _NCPU = 56, + + /// Denso NDR1 microprocessor + _NDR1 = 57, + + /// Motorola Star*Core processor + _STARCORE = 58, + + /// Toyota ME16 processor + _ME16 = 59, + + /// STMicroelectronics ST100 processor + _ST100 = 60, + + /// Advanced Logic Corp. TinyJ embedded processor family + _TINYJ = 61, + + /// AMD x86-64 architecture + _X86_64 = 62, + + /// Sony DSP Processor + _PDSP = 63, + + /// Digital Equipment Corp. PDP-10 + _PDP10 = 64, + + /// Digital Equipment Corp. PDP-11 + _PDP11 = 65, + + /// Siemens FX66 microcontroller + _FX66 = 66, + + /// STMicroelectronics ST9+ 8/16 bit microcontroller + _ST9PLUS = 67, + + /// STMicroelectronics ST7 8-bit microcontroller + _ST7 = 68, + + /// Motorola MC68HC16 Microcontroller + _68HC16 = 69, + + /// Motorola MC68HC11 Microcontroller + _68HC11 = 70, + + /// Motorola MC68HC08 Microcontroller + _68HC08 = 71, + + /// Motorola MC68HC05 Microcontroller + _68HC05 = 72, + + /// Silicon Graphics SVx + _SVX = 73, + + /// STMicroelectronics ST19 8-bit microcontroller + _ST19 = 74, + + /// Digital VAX + _VAX = 75, + + /// Axis Communications 32-bit embedded processor + _CRIS = 76, + + /// Infineon Technologies 32-bit embedded processor + _JAVELIN = 77, + + /// Element 14 64-bit DSP Processor + _FIREPATH = 78, + + /// LSI Logic 16-bit DSP Processor + _ZSP = 79, + + /// Donald Knuth's educational 64-bit processor + _MMIX = 80, + + /// Harvard University machine-independent object files + _HUANY = 81, + + /// SiTera Prism + _PRISM = 82, + + /// Atmel AVR 8-bit microcontroller + _AVR = 83, + + /// Fujitsu FR30 + _FR30 = 84, + + /// Mitsubishi D10V + _D10V = 85, + + /// Mitsubishi D30V + _D30V = 86, + + /// NEC v850 + _V850 = 87, + + /// Mitsubishi M32R + _M32R = 88, + + /// Matsushita MN10300 + _MN10300 = 89, + + /// Matsushita MN10200 + _MN10200 = 90, + + /// picoJava + _PJ = 91, + + /// OpenRISC 32-bit embedded processor + _OPENRISC = 92, + + /// ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) + _ARC_COMPACT = 93, + + /// Tensilica Xtensa Architecture + _XTENSA = 94, + + /// Alphamosaic VideoCore processor + _VIDEOCORE = 95, + + /// Thompson Multimedia General Purpose Processor + _TMM_GPP = 96, + + /// National Semiconductor 32000 series + _NS32K = 97, + + /// Tenor Network TPC processor + _TPC = 98, + + /// Trebia SNP 1000 processor + _SNP1K = 99, + + /// STMicroelectronics (www.st.com) ST200 + _ST200 = 100, + + /// Ubicom IP2xxx microcontroller family + _IP2K = 101, + + /// MAX Processor + _MAX = 102, + + /// National Semiconductor CompactRISC microprocessor + _CR = 103, + + /// Fujitsu F2MC16 + _F2MC16 = 104, + + /// Texas Instruments embedded microcontroller msp430 + _MSP430 = 105, + + /// Analog Devices Blackfin (DSP) processor + _BLACKFIN = 106, + + /// S1C33 Family of Seiko Epson processors + _SE_C33 = 107, + + /// Sharp embedded microprocessor + _SEP = 108, + + /// Arca RISC Microprocessor + _ARCA = 109, + + /// Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University + _UNICORE = 110, + + /// eXcess: 16/32/64-bit configurable embedded CPU + _EXCESS = 111, + + /// Icera Semiconductor Inc. Deep Execution Processor + _DXP = 112, + + /// Altera Nios II soft-core processor + _ALTERA_NIOS2 = 113, + + /// National Semiconductor CompactRISC CRX + _CRX = 114, + + /// Motorola XGATE embedded processor + _XGATE = 115, + + /// Infineon C16x/XC16x processor + _C166 = 116, + + /// Renesas M16C series microprocessors + _M16C = 117, + + /// Microchip Technology dsPIC30F Digital Signal Controller + _DSPIC30F = 118, + + /// Freescale Communication Engine RISC core + _CE = 119, + + /// Renesas M32C series microprocessors + _M32C = 120, + + /// Altium TSK3000 core + _TSK3000 = 131, + + /// Freescale RS08 embedded processor + _RS08 = 132, + + /// Analog Devices SHARC family of 32-bit DSP processors + _SHARC = 133, + + /// Cyan Technology eCOG2 microprocessor + _ECOG2 = 134, + + /// Sunplus S+core7 RISC processor + _SCORE7 = 135, + + /// New Japan Radio (NJR) 24-bit DSP Processor + _DSP24 = 136, + + /// Broadcom VideoCore III processor + _VIDEOCORE3 = 137, + + /// RISC processor for Lattice FPGA architecture + _LATTICEMICO32 = 138, + + /// Seiko Epson C17 family + _SE_C17 = 139, + + /// The Texas Instruments TMS320C6000 DSP family + _TI_C6000 = 140, + + /// The Texas Instruments TMS320C2000 DSP family + _TI_C2000 = 141, + + /// The Texas Instruments TMS320C55x DSP family + _TI_C5500 = 142, + + /// STMicroelectronics 64bit VLIW Data Signal Processor + _MMDSP_PLUS = 160, + + /// Cypress M8C microprocessor + _CYPRESS_M8C = 161, + + /// Renesas R32C series microprocessors + _R32C = 162, + + /// NXP Semiconductors TriMedia architecture family + _TRIMEDIA = 163, + + /// Qualcomm Hexagon processor + _HEXAGON = 164, + + /// Intel 8051 and variants + _8051 = 165, + + /// STMicroelectronics STxP7x family of configurable and extensible RISC processors + _STXP7X = 166, + + /// Andes Technology compact code size embedded RISC processor family + _NDS32 = 167, + + /// Cyan Technology eCOG1X family + _ECOG1X = 168, + + /// Dallas Semiconductor MAXQ30 Core Micro-controllers + _MAXQ30 = 169, + + /// New Japan Radio (NJR) 16-bit DSP Processor + _XIMO16 = 170, + + /// M2000 Reconfigurable RISC Microprocessor + _MANIK = 171, + + /// Cray Inc. NV2 vector architecture + _CRAYNV2 = 172, + + /// Renesas RX family + _RX = 173, + + /// Imagination Technologies META processor architecture + _METAG = 174, + + /// MCST Elbrus general purpose hardware architecture + _MCST_ELBRUS = 175, + + /// Cyan Technology eCOG16 family + _ECOG16 = 176, + + /// National Semiconductor CompactRISC CR16 16-bit microprocessor + _CR16 = 177, + + /// Freescale Extended Time Processing Unit + _ETPU = 178, + + /// Infineon Technologies SLE9X core + _SLE9X = 179, + + /// Intel L10M + _L10M = 180, + + /// Intel K10M + _K10M = 181, + + /// ARM AArch64 + _AARCH64 = 183, + + /// Atmel Corporation 32-bit microprocessor family + _AVR32 = 185, + + /// STMicroeletronics STM8 8-bit microcontroller + _STM8 = 186, + + /// Tilera TILE64 multicore architecture family + _TILE64 = 187, + + /// Tilera TILEPro multicore architecture family + _TILEPRO = 188, + + /// NVIDIA CUDA architecture + _CUDA = 190, + + /// Tilera TILE-Gx multicore architecture family + _TILEGX = 191, + + /// CloudShield architecture family + _CLOUDSHIELD = 192, + + /// KIPO-KAIST Core-A 1st generation processor family + _COREA_1ST = 193, + + /// KIPO-KAIST Core-A 2nd generation processor family + _COREA_2ND = 194, + + /// Synopsys ARCompact V2 + _ARC_COMPACT2 = 195, + + /// Open8 8-bit RISC soft processor core + _OPEN8 = 196, + + /// Renesas RL78 family + _RL78 = 197, + + /// Broadcom VideoCore V processor + _VIDEOCORE5 = 198, + + /// Renesas 78KOR family + _78KOR = 199, + + /// Freescale 56800EX Digital Signal Controller (DSC) + _56800EX = 200, + + /// Beyond BA1 CPU architecture + _BA1 = 201, + + /// Beyond BA2 CPU architecture + _BA2 = 202, + + /// XMOS xCORE processor family + _XCORE = 203, + + /// Microchip 8-bit PIC(r) family + _MCHP_PIC = 204, + + /// Reserved by Intel + _INTEL205 = 205, + + /// Reserved by Intel + _INTEL206 = 206, + + /// Reserved by Intel + _INTEL207 = 207, + + /// Reserved by Intel + _INTEL208 = 208, + + /// Reserved by Intel + _INTEL209 = 209, + + /// KM211 KM32 32-bit processor + _KM32 = 210, + + /// KM211 KMX32 32-bit processor + _KMX32 = 211, + + /// KM211 KMX16 16-bit processor + _KMX16 = 212, + + /// KM211 KMX8 8-bit processor + _KMX8 = 213, + + /// KM211 KVARC processor + _KVARC = 214, + + /// Paneve CDP architecture family + _CDP = 215, + + /// Cognitive Smart Memory Processor + _COGE = 216, + + /// iCelero CoolEngine + _COOL = 217, + + /// Nanoradio Optimized RISC + _NORC = 218, + + /// CSR Kalimba architecture family + _CSR_KALIMBA = 219, + + /// AMD GPU architecture + _AMDGPU = 224, + + /// RISC-V + _RISCV = 243, + + /// Lanai 32-bit processor + _LANAI = 244, + + /// Linux kernel bpf virtual machine + _BPF = 247, +}; + +/// Section data should be writable during execution. +pub const SHF_WRITE = 0x1; + +/// Section occupies memory during program execution. +pub const SHF_ALLOC = 0x2; + +/// Section contains executable machine instructions. +pub const SHF_EXECINSTR = 0x4; + +/// The data in this section may be merged. +pub const SHF_MERGE = 0x10; + +/// The data in this section is null-terminated strings. +pub const SHF_STRINGS = 0x20; + +/// A field in this section holds a section header table index. +pub const SHF_INFO_LINK = 0x40; + +/// Adds special ordering requirements for link editors. +pub const SHF_LINK_ORDER = 0x80; + +/// This section requires special OS-specific processing to avoid incorrect +/// behavior. +pub const SHF_OS_NONCONFORMING = 0x100; + +/// This section is a member of a section group. +pub const SHF_GROUP = 0x200; + +/// This section holds Thread-Local Storage. +pub const SHF_TLS = 0x400; + +/// Identifies a section containing compressed data. +pub const SHF_COMPRESSED = 0x800; + +/// This section is excluded from the final executable or shared library. +pub const SHF_EXCLUDE = 0x80000000; + +/// Start of target-specific flags. +pub const SHF_MASKOS = 0x0ff00000; + +/// Bits indicating processor-specific flags. +pub const SHF_MASKPROC = 0xf0000000; + +/// All sections with the "d" flag are grouped together by the linker to form +/// the data section and the dp register is set to the start of the section by +/// the boot code. +pub const XCORE_SHF_DP_SECTION = 0x10000000; + +/// All sections with the "c" flag are grouped together by the linker to form +/// the constant pool and the cp register is set to the start of the constant +/// pool by the boot code. +pub const XCORE_SHF_CP_SECTION = 0x20000000; + +/// If an object file section does not have this flag set, then it may not hold +/// more than 2GB and can be freely referred to in objects using smaller code +/// models. Otherwise, only objects using larger code models can refer to them. +/// For example, a medium code model object can refer to data in a section that +/// sets this flag besides being able to refer to data in a section that does +/// not set it; likewise, a small code model object can refer only to code in a +/// section that does not set this flag. +pub const SHF_X86_64_LARGE = 0x10000000; + +/// All sections with the GPREL flag are grouped into a global data area +/// for faster accesses +pub const SHF_HEX_GPREL = 0x10000000; + +/// Section contains text/data which may be replicated in other sections. +/// Linker must retain only one copy. +pub const SHF_MIPS_NODUPES = 0x01000000; + +/// Linker must generate implicit hidden weak names. +pub const SHF_MIPS_NAMES = 0x02000000; + +/// Section data local to process. +pub const SHF_MIPS_LOCAL = 0x04000000; + +/// Do not strip this section. +pub const SHF_MIPS_NOSTRIP = 0x08000000; + +/// Section must be part of global data area. +pub const SHF_MIPS_GPREL = 0x10000000; + +/// This section should be merged. +pub const SHF_MIPS_MERGE = 0x20000000; + +/// Address size to be inferred from section entry size. +pub const SHF_MIPS_ADDR = 0x40000000; + +/// Section data is string data by default. +pub const SHF_MIPS_STRING = 0x80000000; + +/// Make code section unreadable when in execute-only mode +pub const SHF_ARM_PURECODE = 0x2000000; + +/// Execute +pub const PF_X = 1; + +/// Write +pub const PF_W = 2; + +/// Read +pub const PF_R = 4; + +/// Bits for operating system-specific semantics. +pub const PF_MASKOS = 0x0ff00000; + +/// Bits for processor-specific semantics. +pub const PF_MASKPROC = 0xf0000000; diff --git a/lib/std/event.zig b/lib/std/event.zig index 56c5223ba..2c72c2258 100644 --- a/lib/std/event.zig +++ b/lib/std/event.zig @@ -7,7 +7,6 @@ pub const RwLock = @import("event/rwlock.zig").RwLock; pub const RwLocked = @import("event/rwlocked.zig").RwLocked; pub const Loop = @import("event/loop.zig").Loop; pub const fs = @import("event/fs.zig"); -pub const net = @import("event/net.zig"); test "import event tests" { _ = @import("event/channel.zig"); @@ -19,5 +18,4 @@ test "import event tests" { _ = @import("event/rwlock.zig"); _ = @import("event/rwlocked.zig"); _ = @import("event/loop.zig"); - _ = @import("event/net.zig"); } diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig index 2f211d21e..2bcf6a4c5 100644 --- a/lib/std/event/channel.zig +++ b/lib/std/event/channel.zig @@ -4,13 +4,11 @@ const assert = std.debug.assert; const testing = std.testing; const Loop = std.event.Loop; -/// many producer, many consumer, thread-safe, runtime configurable buffer size -/// when buffer is empty, consumers suspend and are resumed by producers -/// when buffer is full, producers suspend and are resumed by consumers +/// Many producer, many consumer, thread-safe, runtime configurable buffer size. +/// When buffer is empty, consumers suspend and are resumed by producers. +/// When buffer is full, producers suspend and are resumed by consumers. pub fn Channel(comptime T: type) type { return struct { - loop: *Loop, - getters: std.atomic.Queue(GetNode), or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node), putters: std.atomic.Queue(PutNode), @@ -48,16 +46,21 @@ pub fn Channel(comptime T: type) type { tick_node: *Loop.NextTickNode, }; - /// call destroy when done - pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { - const buffer_nodes = try loop.allocator.alloc(T, capacity); - errdefer loop.allocator.free(buffer_nodes); + const global_event_loop = Loop.instance orelse + @compileError("std.event.Channel currently only works with event-based I/O"); + + /// Call `deinit` to free resources when done. + /// `buffer` must live until `deinit` is called. + /// For a zero length buffer, use `[0]T{}`. + /// TODO https://github.com/ziglang/zig/issues/2765 + pub fn init(self: *SelfChannel, buffer: []T) void { + // The ring buffer implementation only works with power of 2 buffer sizes + // because of relying on subtracting across zero. For example (0 -% 1) % 10 == 5 + assert(buffer.len == 0 or @popCount(usize, buffer.len) == 1); - const self = try loop.allocator.create(SelfChannel); self.* = SelfChannel{ - .loop = loop, .buffer_len = 0, - .buffer_nodes = buffer_nodes, + .buffer_nodes = buffer, .buffer_index = 0, .dispatch_lock = 0, .need_dispatch = 0, @@ -67,21 +70,19 @@ pub fn Channel(comptime T: type) type { .get_count = 0, .put_count = 0, }; - errdefer loop.allocator.destroy(self); - - return self; } - /// must be called when all calls to put and get have suspended and no more calls occur - pub fn destroy(self: *SelfChannel) void { + /// Must be called when all calls to put and get have suspended and no more calls occur. + /// This can be omitted if caller can guarantee that the suspended putters and getters + /// do not need to be run to completion. Note that this may leave awaiters hanging. + pub fn deinit(self: *SelfChannel) void { while (self.getters.get()) |get_node| { resume get_node.data.tick_node.data; } while (self.putters.get()) |put_node| { resume put_node.data.tick_node.data; } - self.loop.allocator.free(self.buffer_nodes); - self.loop.allocator.destroy(self); + self.* = undefined; } /// puts a data item in the channel. The function returns when the value has been added to the @@ -94,17 +95,6 @@ pub fn Channel(comptime T: type) type { .data = data, }); - // TODO test canceling a put() - errdefer { - _ = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst); - const need_dispatch = !self.putters.remove(&queue_node); - self.loop.cancelOnNextTick(&my_tick_node); - if (need_dispatch) { - // oops we made the put_count incorrect for a period of time. fix by dispatching. - _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst); - self.dispatch(); - } - } suspend { self.putters.put(&queue_node); _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst); @@ -126,18 +116,6 @@ pub fn Channel(comptime T: type) type { }, }); - // TODO test canceling a get() - errdefer { - _ = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); - const need_dispatch = !self.getters.remove(&queue_node); - self.loop.cancelOnNextTick(&my_tick_node); - if (need_dispatch) { - // oops we made the get_count incorrect for a period of time. fix by dispatching. - _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); - self.dispatch(); - } - } - suspend { self.getters.put(&queue_node); _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); @@ -156,11 +134,9 @@ pub fn Channel(comptime T: type) type { // } //} - /// Await this function to get an item from the channel. If the buffer is empty and there are no - /// puts waiting, this returns null. - /// Await is necessary for locking purposes. The function will be resumed after checking the channel - /// for data and will not wait for data to be available. - pub async fn getOrNull(self: *SelfChannel) ?T { + /// Get an item from the channel. If the buffer is empty and there are no + /// puts waiting, this returns `null`. + pub fn getOrNull(self: *SelfChannel) ?T { // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: ?T = null; @@ -177,19 +153,6 @@ pub fn Channel(comptime T: type) type { }); or_null_node.data = &queue_node; - // TODO test canceling getOrNull - errdefer { - _ = self.or_null_queue.remove(&or_null_node); - _ = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); - const need_dispatch = !self.getters.remove(&queue_node); - self.loop.cancelOnNextTick(&my_tick_node); - if (need_dispatch) { - // oops we made the get_count incorrect for a period of time. fix by dispatching. - _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); - self.dispatch(); - } - } - suspend { self.getters.put(&queue_node); _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); @@ -202,7 +165,7 @@ pub fn Channel(comptime T: type) type { fn dispatch(self: *SelfChannel) void { // set the "need dispatch" flag - _ = @atomicRmw(u8, &self.need_dispatch, .Xchg, 1, .SeqCst); + @atomicStore(u8, &self.need_dispatch, 1, .SeqCst); lock: while (true) { // set the lock flag @@ -210,7 +173,7 @@ pub fn Channel(comptime T: type) type { if (prev_lock != 0) return; // clear the need_dispatch flag since we're about to do it - _ = @atomicRmw(u8, &self.need_dispatch, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.need_dispatch, 0, .SeqCst); while (true) { one_dispatch: { @@ -225,14 +188,14 @@ pub fn Channel(comptime T: type) type { const get_node = &self.getters.get().?.data; switch (get_node.data) { GetNode.Data.Normal => |info| { - info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len]; }, GetNode.Data.OrNull => |info| { _ = self.or_null_queue.remove(info.or_null); - info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len]; }, } - self.loop.onNextTick(get_node.tick_node); + global_event_loop.onNextTick(get_node.tick_node); self.buffer_len -= 1; get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); @@ -252,8 +215,8 @@ pub fn Channel(comptime T: type) type { info.ptr.* = put_node.data; }, } - self.loop.onNextTick(get_node.tick_node); - self.loop.onNextTick(put_node.tick_node); + global_event_loop.onNextTick(get_node.tick_node); + global_event_loop.onNextTick(put_node.tick_node); get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); put_count = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst); @@ -263,8 +226,8 @@ pub fn Channel(comptime T: type) type { while (self.buffer_len != self.buffer_nodes.len and put_count != 0) { const put_node = &self.putters.get().?.data; - self.buffer_nodes[self.buffer_index] = put_node.data; - self.loop.onNextTick(put_node.tick_node); + self.buffer_nodes[self.buffer_index % self.buffer_nodes.len] = put_node.data; + global_event_loop.onNextTick(put_node.tick_node); self.buffer_index +%= 1; self.buffer_len += 1; @@ -280,7 +243,7 @@ pub fn Channel(comptime T: type) type { var remove_count: usize = 0; while (self.or_null_queue.get()) |or_null_node| { remove_count += @boolToInt(self.getters.remove(or_null_node.data)); - self.loop.onNextTick(or_null_node.data.data.tick_node); + global_event_loop.onNextTick(or_null_node.data.data.tick_node); } if (remove_count != 0) { _ = @atomicRmw(usize, &self.get_count, .Sub, remove_count, .SeqCst); @@ -306,24 +269,48 @@ pub fn Channel(comptime T: type) type { test "std.event.Channel" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; + // https://github.com/ziglang/zig/issues/3251 - if (std.os.freebsd.is_the_target) return error.SkipZigTest; + if (builtin.os == .freebsd) return error.SkipZigTest; - var loop: Loop = undefined; - // TODO make a multi threaded test - try loop.initSingleThreaded(std.heap.direct_allocator); - defer loop.deinit(); + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; - const channel = try Channel(i32).create(&loop, 0); - defer channel.destroy(); + var channel: Channel(i32) = undefined; + channel.init([0]i32{}); + defer channel.deinit(); - const handle = async testChannelGetter(&loop, channel); - const putter = async testChannelPutter(channel); + var handle = async testChannelGetter(&channel); + var putter = async testChannelPutter(&channel); - loop.run(); + await handle; + await putter; } -async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { +test "std.event.Channel wraparound" { + + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; + + const channel_size = 2; + + var buf: [channel_size]i32 = undefined; + var channel: Channel(i32) = undefined; + channel.init(&buf); + defer channel.deinit(); + + // add items to channel and pull them out until + // the buffer wraps around, make sure it doesn't crash. + var result: i32 = undefined; + channel.put(5); + testing.expectEqual(@as(i32, 5), channel.get()); + channel.put(6); + testing.expectEqual(@as(i32, 6), channel.get()); + channel.put(7); + testing.expectEqual(@as(i32, 7), channel.get()); +} + +async fn testChannelGetter(channel: *Channel(i32)) void { const value1 = channel.get(); testing.expect(value1 == 1234); diff --git a/lib/std/event/fs.zig b/lib/std/event/fs.zig index 4490e1dea..ce88ac4dc 100644 --- a/lib/std/event/fs.zig +++ b/lib/std/event/fs.zig @@ -9,6 +9,12 @@ const windows = os.windows; const Loop = event.Loop; const fd_t = os.fd_t; const File = std.fs.File; +const Allocator = mem.Allocator; + +//! TODO mege this with `std.fs` + +const global_event_loop = Loop.instance orelse + @compileError("std.event.fs currently only works with event-based I/O"); pub const RequestNode = std.atomic.Queue(Request).Node; @@ -58,8 +64,7 @@ pub const Request = struct { }; pub const Open = struct { - /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 - path: []const u8, + path: [:0]const u8, flags: u32, mode: File.Mode, result: Error!fd_t, @@ -68,8 +73,7 @@ pub const Request = struct { }; pub const WriteFile = struct { - /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 - path: []const u8, + path: [:0]const u8, contents: []const u8, mode: File.Mode, result: Error!void, @@ -86,15 +90,16 @@ pub const Request = struct { pub const PWriteVError = error{OutOfMemory} || File.WriteError; /// data - just the inner references - must live until pwritev frame completes. -pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { +pub fn pwritev(allocator: *Allocator, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { switch (builtin.os) { .macosx, .linux, .freebsd, .netbsd, + .dragonfly, => { - const iovecs = try loop.allocator.alloc(os.iovec_const, data.len); - defer loop.allocator.free(iovecs); + const iovecs = try allocator.alloc(os.iovec_const, data.len); + defer allocator.free(iovecs); for (data) |buf, i| { iovecs[i] = os.iovec_const{ @@ -103,31 +108,31 @@ pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) P }; } - return pwritevPosix(loop, fd, iovecs, offset); + return pwritevPosix(fd, iovecs, offset); }, .windows => { - const data_copy = try std.mem.dupe(loop.allocator, []const u8, data); - defer loop.allocator.free(data_copy); - return pwritevWindows(loop, fd, data, offset); + const data_copy = try std.mem.dupe(allocator, []const u8, data); + defer allocator.free(data_copy); + return pwritevWindows(fd, data, offset); }, else => @compileError("Unsupported OS"), } } /// data must outlive the returned frame -pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { +pub fn pwritevWindows(fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { if (data.len == 0) return; - if (data.len == 1) return pwriteWindows(loop, fd, data[0], offset); + if (data.len == 1) return pwriteWindows(fd, data[0], offset); // TODO do these in parallel var off = offset; for (data) |buf| { - try pwriteWindows(loop, fd, buf, off); + try pwriteWindows(fd, buf, off); off += buf.len; } } -pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { +pub fn pwriteWindows(fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { var resume_node = Loop.ResumeNode.Basic{ .base = Loop.ResumeNode{ .id = Loop.ResumeNode.Id.Basic, @@ -142,9 +147,9 @@ pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.Wi }, }; // TODO only call create io completion port once per fd - _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); - loop.beginOneEvent(); - errdefer loop.finishOneEvent(); + _ = windows.CreateIoCompletionPort(fd, global_event_loop.os_data.io_port, undefined, undefined); + global_event_loop.beginOneEvent(); + errdefer global_event_loop.finishOneEvent(); errdefer { _ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped); @@ -167,12 +172,7 @@ pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.Wi } /// iovecs must live until pwritev frame completes. -pub fn pwritevPosix( - loop: *Loop, - fd: fd_t, - iovecs: []const os.iovec_const, - offset: usize, -) os.WriteError!void { +pub fn pwritevPosix(fd: fd_t, iovecs: []const os.iovec_const, offset: usize) os.WriteError!void { var req_node = RequestNode{ .prev = null, .next = null, @@ -195,21 +195,17 @@ pub fn pwritevPosix( }, }; - errdefer loop.posixFsCancel(&req_node); + errdefer global_event_loop.posixFsCancel(&req_node); suspend { - loop.posixFsRequest(&req_node); + global_event_loop.posixFsRequest(&req_node); } return req_node.data.msg.PWriteV.result; } /// iovecs must live until pwritev frame completes. -pub fn writevPosix( - loop: *Loop, - fd: fd_t, - iovecs: []const os.iovec_const, -) os.WriteError!void { +pub fn writevPosix(fd: fd_t, iovecs: []const os.iovec_const) os.WriteError!void { var req_node = RequestNode{ .prev = null, .next = null, @@ -232,7 +228,7 @@ pub fn writevPosix( }; suspend { - loop.posixFsRequest(&req_node); + global_event_loop.posixFsRequest(&req_node); } return req_node.data.msg.WriteV.result; @@ -241,16 +237,17 @@ pub fn writevPosix( pub const PReadVError = error{OutOfMemory} || File.ReadError; /// data - just the inner references - must live until preadv frame completes. -pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { +pub fn preadv(allocator: *Allocator, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { assert(data.len != 0); switch (builtin.os) { .macosx, .linux, .freebsd, .netbsd, + .dragonfly, => { - const iovecs = try loop.allocator.alloc(os.iovec, data.len); - defer loop.allocator.free(iovecs); + const iovecs = try allocator.alloc(os.iovec, data.len); + defer allocator.free(iovecs); for (data) |buf, i| { iovecs[i] = os.iovec{ @@ -259,21 +256,21 @@ pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVEr }; } - return preadvPosix(loop, fd, iovecs, offset); + return preadvPosix(fd, iovecs, offset); }, .windows => { - const data_copy = try std.mem.dupe(loop.allocator, []u8, data); - defer loop.allocator.free(data_copy); - return preadvWindows(loop, fd, data_copy, offset); + const data_copy = try std.mem.dupe(allocator, []u8, data); + defer allocator.free(data_copy); + return preadvWindows(fd, data_copy, offset); }, else => @compileError("Unsupported OS"), } } /// data must outlive the returned frame -pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { +pub fn preadvWindows(fd: fd_t, data: []const []u8, offset: u64) !usize { assert(data.len != 0); - if (data.len == 1) return preadWindows(loop, fd, data[0], offset); + if (data.len == 1) return preadWindows(fd, data[0], offset); // TODO do these in parallel? var off: usize = 0; @@ -281,7 +278,7 @@ pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !us var inner_off: usize = 0; while (true) { const v = data[iov_i]; - const amt_read = try preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off); + const amt_read = try preadWindows(fd, v[inner_off .. v.len - inner_off], offset + off); off += amt_read; inner_off += amt_read; if (inner_off == v.len) { @@ -295,7 +292,7 @@ pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !us } } -pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { +pub fn preadWindows(fd: fd_t, data: []u8, offset: u64) !usize { var resume_node = Loop.ResumeNode.Basic{ .base = Loop.ResumeNode{ .id = Loop.ResumeNode.Id.Basic, @@ -310,9 +307,9 @@ pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { }, }; // TODO only call create io completion port once per fd - _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined; - loop.beginOneEvent(); - errdefer loop.finishOneEvent(); + _ = windows.CreateIoCompletionPort(fd, global_event_loop.os_data.io_port, undefined, undefined) catch undefined; + global_event_loop.beginOneEvent(); + errdefer global_event_loop.finishOneEvent(); errdefer { _ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped); @@ -326,20 +323,15 @@ pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { windows.ERROR.IO_PENDING => unreachable, windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, - windows.ERROR.HANDLE_EOF => return usize(bytes_transferred), + windows.ERROR.HANDLE_EOF => return @as(usize, bytes_transferred), else => |err| return windows.unexpectedError(err), } } - return usize(bytes_transferred); + return @as(usize, bytes_transferred); } /// iovecs must live until preadv frame completes -pub fn preadvPosix( - loop: *Loop, - fd: fd_t, - iovecs: []const os.iovec, - offset: usize, -) os.ReadError!usize { +pub fn preadvPosix(fd: fd_t, iovecs: []const os.iovec, offset: usize) os.ReadError!usize { var req_node = RequestNode{ .prev = null, .next = null, @@ -362,21 +354,16 @@ pub fn preadvPosix( }, }; - errdefer loop.posixFsCancel(&req_node); + errdefer global_event_loop.posixFsCancel(&req_node); suspend { - loop.posixFsRequest(&req_node); + global_event_loop.posixFsRequest(&req_node); } return req_node.data.msg.PReadV.result; } -pub fn openPosix( - loop: *Loop, - path: []const u8, - flags: u32, - mode: File.Mode, -) File.OpenError!fd_t { +pub fn openPosix(path: []const u8, flags: u32, mode: File.Mode) File.OpenError!fd_t { const path_c = try std.os.toPosixPath(path); var req_node = RequestNode{ @@ -401,20 +388,21 @@ pub fn openPosix( }, }; - errdefer loop.posixFsCancel(&req_node); + errdefer global_event_loop.posixFsCancel(&req_node); suspend { - loop.posixFsRequest(&req_node); + global_event_loop.posixFsRequest(&req_node); } return req_node.data.msg.Open.result; } -pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { +pub fn openRead(path: []const u8) File.OpenError!fd_t { switch (builtin.os) { - .macosx, .linux, .freebsd, .netbsd => { - const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; - return openPosix(loop, path, flags, File.default_mode); + .macosx, .linux, .freebsd, .netbsd, .dragonfly => { + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; + return openPosix(path, flags, File.default_mode); }, .windows => return windows.CreateFile( @@ -433,20 +421,22 @@ pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { /// Creates if does not exist. Truncates the file if it exists. /// Uses the default mode. -pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { - return openWriteMode(loop, path, File.default_mode); +pub fn openWrite(path: []const u8) File.OpenError!fd_t { + return openWriteMode(path, File.default_mode); } /// Creates if does not exist. Truncates the file if it exists. -pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { +pub fn openWriteMode(path: []const u8, mode: File.Mode) File.OpenError!fd_t { switch (builtin.os) { .macosx, .linux, .freebsd, .netbsd, + .dragonfly, => { - const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; - return openPosix(loop, path, flags, File.default_mode); + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; + return openPosix(path, flags, File.default_mode); }, .windows => return windows.CreateFile( path, @@ -462,15 +452,12 @@ pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenEr } /// Creates if does not exist. Does not truncate. -pub fn openReadWrite( - loop: *Loop, - path: []const u8, - mode: File.Mode, -) File.OpenError!fd_t { +pub fn openReadWrite(path: []const u8, mode: File.Mode) File.OpenError!fd_t { switch (builtin.os) { - .macosx, .linux, .freebsd, .netbsd => { - const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC; - return openPosix(loop, path, flags, mode); + .macosx, .linux, .freebsd, .netbsd, .dragonfly => { + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const flags = O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC; + return openPosix(path, flags, mode); }, .windows => return windows.CreateFile( @@ -494,11 +481,11 @@ pub fn openReadWrite( /// If you call `setHandle` then finishing will close the fd; otherwise finishing /// will deallocate the `CloseOperation`. pub const CloseOperation = struct { - loop: *Loop, + allocator: *Allocator, os_data: OsData, const OsData = switch (builtin.os) { - .linux, .macosx, .freebsd, .netbsd => OsDataPosix, + .linux, .macosx, .freebsd, .netbsd, .dragonfly => OsDataPosix, .windows => struct { handle: ?fd_t, @@ -512,12 +499,12 @@ pub const CloseOperation = struct { close_req_node: RequestNode, }; - pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) { - const self = try loop.allocator.create(CloseOperation); + pub fn start(allocator: *Allocator) (error{OutOfMemory}!*CloseOperation) { + const self = try allocator.create(CloseOperation); self.* = CloseOperation{ - .loop = loop, + .allocator = allocator, .os_data = switch (builtin.os) { - .linux, .macosx, .freebsd, .netbsd => initOsDataPosix(self), + .linux, .macosx, .freebsd, .netbsd, .dragonfly => initOsDataPosix(self), .windows => OsData{ .handle = null }, else => @compileError("Unsupported OS"), }, @@ -548,18 +535,19 @@ pub const CloseOperation = struct { .macosx, .freebsd, .netbsd, + .dragonfly, => { if (self.os_data.have_fd) { - self.loop.posixFsRequest(&self.os_data.close_req_node); + global_event_loop.posixFsRequest(&self.os_data.close_req_node); } else { - self.loop.allocator.destroy(self); + self.allocator.destroy(self); } }, .windows => { if (self.os_data.handle) |handle| { os.close(handle); } - self.loop.allocator.destroy(self); + self.allocator.destroy(self); }, else => @compileError("Unsupported OS"), } @@ -571,6 +559,7 @@ pub const CloseOperation = struct { .macosx, .freebsd, .netbsd, + .dragonfly, => { self.os_data.close_req_node.data.msg.Close.fd = handle; self.os_data.have_fd = true; @@ -589,6 +578,7 @@ pub const CloseOperation = struct { .macosx, .freebsd, .netbsd, + .dragonfly, => { self.os_data.have_fd = false; }, @@ -605,6 +595,7 @@ pub const CloseOperation = struct { .macosx, .freebsd, .netbsd, + .dragonfly, => { assert(self.os_data.have_fd); return self.os_data.close_req_node.data.msg.Close.fd; @@ -619,24 +610,25 @@ pub const CloseOperation = struct { /// contents must remain alive until writeFile completes. /// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate -pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { - return writeFileMode(loop, path, contents, File.default_mode); +pub fn writeFile(allocator: *Allocator, path: []const u8, contents: []const u8) !void { + return writeFileMode(allocator, path, contents, File.default_mode); } /// contents must remain alive until writeFile completes. -pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { +pub fn writeFileMode(allocator: *Allocator, path: []const u8, contents: []const u8, mode: File.Mode) !void { switch (builtin.os) { .linux, .macosx, .freebsd, .netbsd, - => return writeFileModeThread(loop, path, contents, mode), - .windows => return writeFileWindows(loop, path, contents), + .dragonfly, + => return writeFileModeThread(allocator, path, contents, mode), + .windows => return writeFileWindows(path, contents), else => @compileError("Unsupported OS"), } } -fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { +fn writeFileWindows(path: []const u8, contents: []const u8) !void { const handle = try windows.CreateFile( path, windows.GENERIC_WRITE, @@ -648,12 +640,12 @@ fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { ); defer os.close(handle); - try pwriteWindows(loop, handle, contents, 0); + try pwriteWindows(handle, contents, 0); } -fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { - const path_with_null = try std.cstr.addNullByte(loop.allocator, path); - defer loop.allocator.free(path_with_null); +fn writeFileModeThread(allocator: *Allocator, path: []const u8, contents: []const u8, mode: File.Mode) !void { + const path_with_null = try std.cstr.addNullByte(allocator, path); + defer allocator.free(path_with_null); var req_node = RequestNode{ .prev = null, @@ -677,10 +669,10 @@ fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode }, }; - errdefer loop.posixFsCancel(&req_node); + errdefer global_event_loop.posixFsCancel(&req_node); suspend { - loop.posixFsRequest(&req_node); + global_event_loop.posixFsRequest(&req_node); } return req_node.data.msg.WriteFile.result; @@ -689,21 +681,21 @@ fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode /// The frame resumes when the last data has been confirmed written, but before the file handle /// is closed. /// Caller owns returned memory. -pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 { - var close_op = try CloseOperation.start(loop); +pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) ![]u8 { + var close_op = try CloseOperation.start(allocator); defer close_op.finish(); - const fd = try openRead(loop, file_path); + const fd = try openRead(file_path); close_op.setHandle(fd); - var list = std.ArrayList(u8).init(loop.allocator); + var list = std.ArrayList(u8).init(allocator); defer list.deinit(); while (true) { try list.ensureCapacity(list.len + mem.page_size); const buf = list.items[list.len..]; const buf_array = [_][]u8{buf}; - const amt = try preadv(loop, fd, buf_array, list.len); + const amt = try preadv(allocator, fd, &buf_array, list.len); list.len += amt; if (list.len > max_size) { return error.FileTooBig; @@ -729,610 +721,607 @@ fn hashString(s: []const u16) u32 { return @truncate(u32, std.hash.Wyhash.hash(0, @sliceToBytes(s))); } -//pub const WatchEventError = error{ -// UserResourceLimitReached, -// SystemResources, -// AccessDenied, -// Unexpected, // TODO remove this possibility -//}; -// -//pub fn Watch(comptime V: type) type { -// return struct { -// channel: *event.Channel(Event.Error!Event), -// os_data: OsData, -// -// const OsData = switch (builtin.os) { -// .macosx, .freebsd, .netbsd => struct { -// file_table: FileTable, -// table_lock: event.Lock, -// -// const FileTable = std.StringHashmap(*Put); -// const Put = struct { -// putter: anyframe, -// value_ptr: *V, -// }; -// }, -// -// .linux => LinuxOsData, -// .windows => WindowsOsData, -// -// else => @compileError("Unsupported OS"), -// }; -// -// const WindowsOsData = struct { -// table_lock: event.Lock, -// dir_table: DirTable, -// all_putters: std.atomic.Queue(anyframe), -// ref_count: std.atomic.Int(usize), -// -// const DirTable = std.StringHashMap(*Dir); -// const FileTable = std.HashMap([]const u16, V, hashString, eqlString); -// -// const Dir = struct { -// putter: anyframe, -// file_table: FileTable, -// table_lock: event.Lock, -// }; -// }; -// -// const LinuxOsData = struct { -// putter: anyframe, -// inotify_fd: i32, -// wd_table: WdTable, -// table_lock: event.Lock, -// -// const WdTable = std.AutoHashMap(i32, Dir); -// const FileTable = std.StringHashMap(V); -// -// const Dir = struct { -// dirname: []const u8, -// file_table: FileTable, -// }; -// }; -// -// const FileToHandle = std.StringHashMap(anyframe); -// -// const Self = @This(); -// -// pub const Event = struct { -// id: Id, -// data: V, -// -// pub const Id = WatchEventId; -// pub const Error = WatchEventError; -// }; -// -// pub fn create(loop: *Loop, event_buf_count: usize) !*Self { -// const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count); -// errdefer channel.destroy(); -// -// switch (builtin.os) { -// .linux => { -// const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); -// errdefer os.close(inotify_fd); -// -// var result: *Self = undefined; -// _ = try async linuxEventPutter(inotify_fd, channel, &result); -// return result; -// }, -// -// .windows => { -// const self = try loop.allocator.create(Self); -// errdefer loop.allocator.destroy(self); -// self.* = Self{ -// .channel = channel, -// .os_data = OsData{ -// .table_lock = event.Lock.init(loop), -// .dir_table = OsData.DirTable.init(loop.allocator), -// .ref_count = std.atomic.Int(usize).init(1), -// .all_putters = std.atomic.Queue(anyframe).init(), -// }, -// }; -// return self; -// }, -// -// .macosx, .freebsd, .netbsd => { -// const self = try loop.allocator.create(Self); -// errdefer loop.allocator.destroy(self); -// -// self.* = Self{ -// .channel = channel, -// .os_data = OsData{ -// .table_lock = event.Lock.init(loop), -// .file_table = OsData.FileTable.init(loop.allocator), -// }, -// }; -// return self; -// }, -// else => @compileError("Unsupported OS"), -// } -// } -// -// /// All addFile calls and removeFile calls must have completed. -// pub fn destroy(self: *Self) void { -// switch (builtin.os) { -// .macosx, .freebsd, .netbsd => { -// // TODO we need to cancel the frames before destroying the lock -// self.os_data.table_lock.deinit(); -// var it = self.os_data.file_table.iterator(); -// while (it.next()) |entry| { -// cancel entry.value.putter; -// self.channel.loop.allocator.free(entry.key); -// } -// self.channel.destroy(); -// }, -// .linux => cancel self.os_data.putter, -// .windows => { -// while (self.os_data.all_putters.get()) |putter_node| { -// cancel putter_node.data; -// } -// self.deref(); -// }, -// else => @compileError("Unsupported OS"), -// } -// } -// -// fn ref(self: *Self) void { -// _ = self.os_data.ref_count.incr(); -// } -// -// fn deref(self: *Self) void { -// if (self.os_data.ref_count.decr() == 1) { -// const allocator = self.channel.loop.allocator; -// self.os_data.table_lock.deinit(); -// var it = self.os_data.dir_table.iterator(); -// while (it.next()) |entry| { -// allocator.free(entry.key); -// allocator.destroy(entry.value); -// } -// self.os_data.dir_table.deinit(); -// self.channel.destroy(); -// allocator.destroy(self); -// } -// } -// -// pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { -// switch (builtin.os) { -// .macosx, .freebsd, .netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable), -// .linux => return await (async addFileLinux(self, file_path, value) catch unreachable), -// .windows => return await (async addFileWindows(self, file_path, value) catch unreachable), -// else => @compileError("Unsupported OS"), -// } -// } -// -// async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { -// const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [_][]const u8{file_path}); -// var resolved_path_consumed = false; -// defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path); -// -// var close_op = try CloseOperation.start(self.channel.loop); -// var close_op_consumed = false; -// defer if (!close_op_consumed) close_op.finish(); -// -// const flags = if (os.darwin.is_the_target) os.O_SYMLINK | os.O_EVTONLY else 0; -// const mode = 0; -// const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable); -// close_op.setHandle(fd); -// -// var put_data: *OsData.Put = undefined; -// const putter = try async self.kqPutEvents(close_op, value, &put_data); -// close_op_consumed = true; -// errdefer cancel putter; -// -// const result = blk: { -// const held = await (async self.os_data.table_lock.acquire() catch unreachable); -// defer held.release(); -// -// const gop = try self.os_data.file_table.getOrPut(resolved_path); -// if (gop.found_existing) { -// const prev_value = gop.kv.value.value_ptr.*; -// cancel gop.kv.value.putter; -// gop.kv.value = put_data; -// break :blk prev_value; -// } else { -// resolved_path_consumed = true; -// gop.kv.value = put_data; -// break :blk null; -// } -// }; -// -// return result; -// } -// -// async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void { -// var value_copy = value; -// var put = OsData.Put{ -// .putter = @frame(), -// .value_ptr = &value_copy, -// }; -// out_put.* = &put; -// self.channel.loop.beginOneEvent(); -// -// defer { -// close_op.finish(); -// self.channel.loop.finishOneEvent(); -// } -// -// while (true) { -// if (await (async self.channel.loop.bsdWaitKev( -// @intCast(usize, close_op.getHandle()), -// os.EVFILT_VNODE, -// os.NOTE_WRITE | os.NOTE_DELETE, -// ) catch unreachable)) |kev| { -// // TODO handle EV_ERROR -// if (kev.fflags & os.NOTE_DELETE != 0) { -// await (async self.channel.put(Self.Event{ -// .id = Event.Id.Delete, -// .data = value_copy, -// }) catch unreachable); -// } else if (kev.fflags & os.NOTE_WRITE != 0) { -// await (async self.channel.put(Self.Event{ -// .id = Event.Id.CloseWrite, -// .data = value_copy, -// }) catch unreachable); -// } -// } else |err| switch (err) { -// error.EventNotFound => unreachable, -// error.ProcessNotFound => unreachable, -// error.Overflow => unreachable, -// error.AccessDenied, error.SystemResources => |casted_err| { -// await (async self.channel.put(casted_err) catch unreachable); -// }, -// } -// } -// } -// -// async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V { -// const value_copy = value; -// -// const dirname = std.fs.path.dirname(file_path) orelse "."; -// const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname); -// var dirname_with_null_consumed = false; -// defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null); -// -// const basename = std.fs.path.basename(file_path); -// const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename); -// var basename_with_null_consumed = false; -// defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null); -// -// const wd = try os.inotify_add_watchC( -// self.os_data.inotify_fd, -// dirname_with_null.ptr, -// os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK, -// ); -// // wd is either a newly created watch or an existing one. -// -// const held = await (async self.os_data.table_lock.acquire() catch unreachable); -// defer held.release(); -// -// const gop = try self.os_data.wd_table.getOrPut(wd); -// if (!gop.found_existing) { -// gop.kv.value = OsData.Dir{ -// .dirname = dirname_with_null, -// .file_table = OsData.FileTable.init(self.channel.loop.allocator), -// }; -// dirname_with_null_consumed = true; -// } -// const dir = &gop.kv.value; -// -// const file_table_gop = try dir.file_table.getOrPut(basename_with_null); -// if (file_table_gop.found_existing) { -// const prev_value = file_table_gop.kv.value; -// file_table_gop.kv.value = value_copy; -// return prev_value; -// } else { -// file_table_gop.kv.value = value_copy; -// basename_with_null_consumed = true; -// return null; -// } -// } -// -// async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V { -// const value_copy = value; -// // TODO we might need to convert dirname and basename to canonical file paths ("short"?) -// -// const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, std.fs.path.dirname(file_path) orelse "."); -// var dirname_consumed = false; -// defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname); -// -// const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname); -// defer self.channel.loop.allocator.free(dirname_utf16le); -// -// // TODO https://github.com/ziglang/zig/issues/265 -// const basename = std.fs.path.basename(file_path); -// const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename); -// var basename_utf16le_null_consumed = false; -// defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null); -// const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1]; -// -// const dir_handle = try windows.CreateFileW( -// dirname_utf16le.ptr, -// windows.FILE_LIST_DIRECTORY, -// windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE, -// null, -// windows.OPEN_EXISTING, -// windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED, -// null, -// ); -// var dir_handle_consumed = false; -// defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle); -// -// const held = await (async self.os_data.table_lock.acquire() catch unreachable); -// defer held.release(); -// -// const gop = try self.os_data.dir_table.getOrPut(dirname); -// if (gop.found_existing) { -// const dir = gop.kv.value; -// const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable); -// defer held_dir_lock.release(); -// -// const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null); -// if (file_gop.found_existing) { -// const prev_value = file_gop.kv.value; -// file_gop.kv.value = value_copy; -// return prev_value; -// } else { -// file_gop.kv.value = value_copy; -// basename_utf16le_null_consumed = true; -// return null; -// } -// } else { -// errdefer _ = self.os_data.dir_table.remove(dirname); -// const dir = try self.channel.loop.allocator.create(OsData.Dir); -// errdefer self.channel.loop.allocator.destroy(dir); -// -// dir.* = OsData.Dir{ -// .file_table = OsData.FileTable.init(self.channel.loop.allocator), -// .table_lock = event.Lock.init(self.channel.loop), -// .putter = undefined, -// }; -// gop.kv.value = dir; -// assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null); -// basename_utf16le_null_consumed = true; -// -// dir.putter = try async self.windowsDirReader(dir_handle, dir); -// dir_handle_consumed = true; -// -// dirname_consumed = true; -// -// return null; -// } -// } -// -// async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void { -// self.ref(); -// defer self.deref(); -// -// defer os.close(dir_handle); -// -// var putter_node = std.atomic.Queue(anyframe).Node{ -// .data = @frame(), -// .prev = null, -// .next = null, -// }; -// self.os_data.all_putters.put(&putter_node); -// defer _ = self.os_data.all_putters.remove(&putter_node); -// -// var resume_node = Loop.ResumeNode.Basic{ -// .base = Loop.ResumeNode{ -// .id = Loop.ResumeNode.Id.Basic, -// .handle = @frame(), -// .overlapped = windows.OVERLAPPED{ -// .Internal = 0, -// .InternalHigh = 0, -// .Offset = 0, -// .OffsetHigh = 0, -// .hEvent = null, -// }, -// }, -// }; -// var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; -// -// // TODO handle this error not in the channel but in the setup -// _ = windows.CreateIoCompletionPort( -// dir_handle, -// self.channel.loop.os_data.io_port, -// undefined, -// undefined, -// ) catch |err| { -// await (async self.channel.put(err) catch unreachable); -// return; -// }; -// -// while (true) { -// { -// // TODO only 1 beginOneEvent for the whole function -// self.channel.loop.beginOneEvent(); -// errdefer self.channel.loop.finishOneEvent(); -// errdefer { -// _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped); -// } -// suspend { -// _ = windows.kernel32.ReadDirectoryChangesW( -// dir_handle, -// &event_buf, -// @intCast(windows.DWORD, event_buf.len), -// windows.FALSE, // watch subtree -// windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | -// windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | -// windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | -// windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, -// null, // number of bytes transferred (unused for async) -// &resume_node.base.overlapped, -// null, // completion routine - unused because we use IOCP -// ); -// } -// } -// var bytes_transferred: windows.DWORD = undefined; -// if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { -// const err = switch (windows.kernel32.GetLastError()) { -// else => |err| windows.unexpectedError(err), -// }; -// await (async self.channel.put(err) catch unreachable); -// } else { -// // can't use @bytesToSlice because of the special variable length name field -// var ptr = event_buf[0..].ptr; -// const end_ptr = ptr + bytes_transferred; -// var ev: *windows.FILE_NOTIFY_INFORMATION = undefined; -// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) { -// ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr); -// const emit = switch (ev.Action) { -// windows.FILE_ACTION_REMOVED => WatchEventId.Delete, -// windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite, -// else => null, -// }; -// if (emit) |id| { -// const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2]; -// const user_value = blk: { -// const held = await (async dir.table_lock.acquire() catch unreachable); -// defer held.release(); -// -// if (dir.file_table.get(basename_utf16le)) |entry| { -// break :blk entry.value; -// } else { -// break :blk null; -// } -// }; -// if (user_value) |v| { -// await (async self.channel.put(Event{ -// .id = id, -// .data = v, -// }) catch unreachable); -// } -// } -// if (ev.NextEntryOffset == 0) break; -// } -// } -// } -// } -// -// pub async fn removeFile(self: *Self, file_path: []const u8) ?V { -// @panic("TODO"); -// } -// -// async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void { -// const loop = channel.loop; -// -// var watch = Self{ -// .channel = channel, -// .os_data = OsData{ -// .putter = @frame(), -// .inotify_fd = inotify_fd, -// .wd_table = OsData.WdTable.init(loop.allocator), -// .table_lock = event.Lock.init(loop), -// }, -// }; -// out_watch.* = &watch; -// -// loop.beginOneEvent(); -// -// defer { -// watch.os_data.table_lock.deinit(); -// var wd_it = watch.os_data.wd_table.iterator(); -// while (wd_it.next()) |wd_entry| { -// var file_it = wd_entry.value.file_table.iterator(); -// while (file_it.next()) |file_entry| { -// loop.allocator.free(file_entry.key); -// } -// loop.allocator.free(wd_entry.value.dirname); -// } -// loop.finishOneEvent(); -// os.close(inotify_fd); -// channel.destroy(); -// } -// -// var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined; -// -// while (true) { -// const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len); -// const errno = os.linux.getErrno(rc); -// switch (errno) { -// 0 => { -// // can't use @bytesToSlice because of the special variable length name field -// var ptr = event_buf[0..].ptr; -// const end_ptr = ptr + event_buf.len; -// var ev: *os.linux.inotify_event = undefined; -// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) { -// ev = @ptrCast(*os.linux.inotify_event, ptr); -// if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) { -// const basename_ptr = ptr + @sizeOf(os.linux.inotify_event); -// const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1]; -// const user_value = blk: { -// const held = await (async watch.os_data.table_lock.acquire() catch unreachable); -// defer held.release(); -// -// const dir = &watch.os_data.wd_table.get(ev.wd).?.value; -// if (dir.file_table.get(basename_with_null)) |entry| { -// break :blk entry.value; -// } else { -// break :blk null; -// } -// }; -// if (user_value) |v| { -// await (async channel.put(Event{ -// .id = WatchEventId.CloseWrite, -// .data = v, -// }) catch unreachable); -// } -// } -// } -// }, -// os.linux.EINTR => continue, -// os.linux.EINVAL => unreachable, -// os.linux.EFAULT => unreachable, -// os.linux.EAGAIN => { -// (await (async loop.linuxWaitFd( -// inotify_fd, -// os.linux.EPOLLET | os.linux.EPOLLIN, -// ) catch unreachable)) catch |err| { -// const transformed_err = switch (err) { -// error.FileDescriptorAlreadyPresentInSet => unreachable, -// error.OperationCausesCircularLoop => unreachable, -// error.FileDescriptorNotRegistered => unreachable, -// error.FileDescriptorIncompatibleWithEpoll => unreachable, -// error.Unexpected => unreachable, -// else => |e| e, -// }; -// await (async channel.put(transformed_err) catch unreachable); -// }; -// }, -// else => unreachable, -// } -// } -// } -// }; -//} +pub const WatchEventError = error{ + UserResourceLimitReached, + SystemResources, + AccessDenied, + Unexpected, // TODO remove this possibility +}; + +pub fn Watch(comptime V: type) type { + return struct { + channel: *event.Channel(Event.Error!Event), + os_data: OsData, + allocator: *Allocator, + + const OsData = switch (builtin.os) { + // TODO https://github.com/ziglang/zig/issues/3778 + .macosx, .freebsd, .netbsd, .dragonfly => KqOsData, + .linux => LinuxOsData, + .windows => WindowsOsData, + + else => @compileError("Unsupported OS"), + }; + + const KqOsData = struct { + file_table: FileTable, + table_lock: event.Lock, + + const FileTable = std.StringHashMap(*Put); + const Put = struct { + putter_frame: @Frame(kqPutEvents), + cancelled: bool = false, + value: V, + }; + }; + + const WindowsOsData = struct { + table_lock: event.Lock, + dir_table: DirTable, + all_putters: std.atomic.Queue(Put), + ref_count: std.atomic.Int(usize), + + const Put = struct { + putter: anyframe, + cancelled: bool = false, + }; + + const DirTable = std.StringHashMap(*Dir); + const FileTable = std.HashMap([]const u16, V, hashString, eqlString); + + const Dir = struct { + putter_frame: @Frame(windowsDirReader), + file_table: FileTable, + table_lock: event.Lock, + }; + }; + + const LinuxOsData = struct { + putter_frame: @Frame(linuxEventPutter), + inotify_fd: i32, + wd_table: WdTable, + table_lock: event.Lock, + cancelled: bool = false, + + const WdTable = std.AutoHashMap(i32, Dir); + const FileTable = std.StringHashMap(V); + + const Dir = struct { + dirname: []const u8, + file_table: FileTable, + }; + }; + + const Self = @This(); + + pub const Event = struct { + id: Id, + data: V, + + pub const Id = WatchEventId; + pub const Error = WatchEventError; + }; + + pub fn init(allocator: *Allocator, event_buf_count: usize) !*Self { + const channel = try allocator.create(event.Channel(Event.Error!Event)); + errdefer allocator.destroy(channel); + var buf = try allocator.alloc(Event.Error!Event, event_buf_count); + errdefer allocator.free(buf); + channel.init(buf); + errdefer channel.deinit(); + + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + + switch (builtin.os) { + .linux => { + const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC); + errdefer os.close(inotify_fd); + + self.* = Self{ + .allocator = allocator, + .channel = channel, + .os_data = OsData{ + .putter_frame = undefined, + .inotify_fd = inotify_fd, + .wd_table = OsData.WdTable.init(allocator), + .table_lock = event.Lock.init(), + }, + }; + + self.os_data.putter_frame = async self.linuxEventPutter(); + return self; + }, + + .windows => { + self.* = Self{ + .allocator = allocator, + .channel = channel, + .os_data = OsData{ + .table_lock = event.Lock.init(), + .dir_table = OsData.DirTable.init(allocator), + .ref_count = std.atomic.Int(usize).init(1), + .all_putters = std.atomic.Queue(anyframe).init(), + }, + }; + return self; + }, + + .macosx, .freebsd, .netbsd, .dragonfly => { + self.* = Self{ + .allocator = allocator, + .channel = channel, + .os_data = OsData{ + .table_lock = event.Lock.init(), + .file_table = OsData.FileTable.init(allocator), + }, + }; + return self; + }, + else => @compileError("Unsupported OS"), + } + } + + /// All addFile calls and removeFile calls must have completed. + pub fn deinit(self: *Self) void { + switch (builtin.os) { + .macosx, .freebsd, .netbsd, .dragonfly => { + // TODO we need to cancel the frames before destroying the lock + self.os_data.table_lock.deinit(); + var it = self.os_data.file_table.iterator(); + while (it.next()) |entry| { + entry.cancelled = true; + await entry.value.putter; + self.allocator.free(entry.key); + self.allocator.free(entry.value); + } + self.channel.deinit(); + self.allocator.destroy(self.channel.buffer_nodes); + self.allocator.destroy(self); + }, + .linux => { + self.os_data.cancelled = true; + await self.os_data.putter_frame; + self.allocator.destroy(self); + }, + .windows => { + while (self.os_data.all_putters.get()) |putter_node| { + putter_node.cancelled = true; + await putter_node.frame; + } + self.deref(); + }, + else => @compileError("Unsupported OS"), + } + } + + fn ref(self: *Self) void { + _ = self.os_data.ref_count.incr(); + } + + fn deref(self: *Self) void { + if (self.os_data.ref_count.decr() == 1) { + self.os_data.table_lock.deinit(); + var it = self.os_data.dir_table.iterator(); + while (it.next()) |entry| { + self.allocator.free(entry.key); + self.allocator.destroy(entry.value); + } + self.os_data.dir_table.deinit(); + self.channel.deinit(); + self.allocator.destroy(self.channel.buffer_nodes); + self.allocator.destroy(self); + } + } + + pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V { + switch (builtin.os) { + .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value), + .linux => return addFileLinux(self, file_path, value), + .windows => return addFileWindows(self, file_path, value), + else => @compileError("Unsupported OS"), + } + } + + fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { + const resolved_path = try std.fs.path.resolve(self.allocator, [_][]const u8{file_path}); + var resolved_path_consumed = false; + defer if (!resolved_path_consumed) self.allocator.free(resolved_path); + + var close_op = try CloseOperation.start(self.allocator); + var close_op_consumed = false; + defer if (!close_op_consumed) close_op.finish(); + + const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0; + const mode = 0; + const fd = try openPosix(self.allocator, resolved_path, flags, mode); + close_op.setHandle(fd); + + var put = try self.allocator.create(OsData.Put); + errdefer self.allocator.destroy(put); + put.* = OsData.Put{ + .value = value, + .putter_frame = undefined, + }; + put.putter_frame = async self.kqPutEvents(close_op, put); + close_op_consumed = true; + errdefer { + put.cancelled = true; + await put.putter_frame; + } + + const result = blk: { + const held = self.os_data.table_lock.acquire(); + defer held.release(); + + const gop = try self.os_data.file_table.getOrPut(resolved_path); + if (gop.found_existing) { + const prev_value = gop.kv.value.value; + await gop.kv.value.putter_frame; + gop.kv.value = put; + break :blk prev_value; + } else { + resolved_path_consumed = true; + gop.kv.value = put; + break :blk null; + } + }; + + return result; + } + + fn kqPutEvents(self: *Self, close_op: *CloseOperation, put: *OsData.Put) void { + global_event_loop.beginOneEvent(); + + defer { + close_op.finish(); + global_event_loop.finishOneEvent(); + } + + while (!put.cancelled) { + if (global_event_loop.bsdWaitKev( + @intCast(usize, close_op.getHandle()), + os.EVFILT_VNODE, + os.NOTE_WRITE | os.NOTE_DELETE, + )) |kev| { + // TODO handle EV_ERROR + if (kev.fflags & os.NOTE_DELETE != 0) { + self.channel.put(Self.Event{ + .id = Event.Id.Delete, + .data = put.value, + }); + } else if (kev.fflags & os.NOTE_WRITE != 0) { + self.channel.put(Self.Event{ + .id = Event.Id.CloseWrite, + .data = put.value, + }); + } + } else |err| switch (err) { + error.EventNotFound => unreachable, + error.ProcessNotFound => unreachable, + error.Overflow => unreachable, + error.AccessDenied, error.SystemResources => |casted_err| { + self.channel.put(casted_err); + }, + } + } + } + + fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V { + const dirname = std.fs.path.dirname(file_path) orelse "."; + const dirname_with_null = try std.cstr.addNullByte(self.allocator, dirname); + var dirname_with_null_consumed = false; + defer if (!dirname_with_null_consumed) self.channel.free(dirname_with_null); + + const basename = std.fs.path.basename(file_path); + const basename_with_null = try std.cstr.addNullByte(self.allocator, basename); + var basename_with_null_consumed = false; + defer if (!basename_with_null_consumed) self.allocator.free(basename_with_null); + + const wd = try os.inotify_add_watchC( + self.os_data.inotify_fd, + dirname_with_null.ptr, + os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK, + ); + // wd is either a newly created watch or an existing one. + + const held = self.os_data.table_lock.acquire(); + defer held.release(); + + const gop = try self.os_data.wd_table.getOrPut(wd); + if (!gop.found_existing) { + gop.kv.value = OsData.Dir{ + .dirname = dirname_with_null, + .file_table = OsData.FileTable.init(self.allocator), + }; + dirname_with_null_consumed = true; + } + const dir = &gop.kv.value; + + const file_table_gop = try dir.file_table.getOrPut(basename_with_null); + if (file_table_gop.found_existing) { + const prev_value = file_table_gop.kv.value; + file_table_gop.kv.value = value; + return prev_value; + } else { + file_table_gop.kv.value = value; + basename_with_null_consumed = true; + return null; + } + } + + fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V { + // TODO we might need to convert dirname and basename to canonical file paths ("short"?) + const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse "."); + var dirname_consumed = false; + defer if (!dirname_consumed) self.allocator.free(dirname); + + const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname); + defer self.allocator.free(dirname_utf16le); + + // TODO https://github.com/ziglang/zig/issues/265 + const basename = std.fs.path.basename(file_path); + const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, basename); + var basename_utf16le_null_consumed = false; + defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null); + const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1]; + + const dir_handle = try windows.CreateFileW( + dirname_utf16le.ptr, + windows.FILE_LIST_DIRECTORY, + windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE, + null, + windows.OPEN_EXISTING, + windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED, + null, + ); + var dir_handle_consumed = false; + defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle); + + const held = self.os_data.table_lock.acquire(); + defer held.release(); + + const gop = try self.os_data.dir_table.getOrPut(dirname); + if (gop.found_existing) { + const dir = gop.kv.value; + const held_dir_lock = dir.table_lock.acquire(); + defer held_dir_lock.release(); + + const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null); + if (file_gop.found_existing) { + const prev_value = file_gop.kv.value; + file_gop.kv.value = value; + return prev_value; + } else { + file_gop.kv.value = value; + basename_utf16le_null_consumed = true; + return null; + } + } else { + errdefer _ = self.os_data.dir_table.remove(dirname); + const dir = try self.allocator.create(OsData.Dir); + errdefer self.allocator.destroy(dir); + + dir.* = OsData.Dir{ + .file_table = OsData.FileTable.init(self.allocator), + .table_lock = event.Lock.init(), + .putter_frame = undefined, + }; + gop.kv.value = dir; + assert((try dir.file_table.put(basename_utf16le_no_null, value)) == null); + basename_utf16le_null_consumed = true; + + dir.putter_frame = async self.windowsDirReader(dir_handle, dir); + dir_handle_consumed = true; + + dirname_consumed = true; + + return null; + } + } + + fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void { + self.ref(); + defer self.deref(); + + defer os.close(dir_handle); + + var putter_node = std.atomic.Queue(anyframe).Node{ + .data = .{ .putter = @frame() }, + .prev = null, + .next = null, + }; + self.os_data.all_putters.put(&putter_node); + defer _ = self.os_data.all_putters.remove(&putter_node); + + var resume_node = Loop.ResumeNode.Basic{ + .base = Loop.ResumeNode{ + .id = Loop.ResumeNode.Id.Basic, + .handle = @frame(), + .overlapped = windows.OVERLAPPED{ + .Internal = 0, + .InternalHigh = 0, + .Offset = 0, + .OffsetHigh = 0, + .hEvent = null, + }, + }, + }; + var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; + + // TODO handle this error not in the channel but in the setup + _ = windows.CreateIoCompletionPort( + dir_handle, + global_event_loop.os_data.io_port, + undefined, + undefined, + ) catch |err| { + self.channel.put(err); + return; + }; + + while (!putter_node.data.cancelled) { + { + // TODO only 1 beginOneEvent for the whole function + global_event_loop.beginOneEvent(); + errdefer global_event_loop.finishOneEvent(); + errdefer { + _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped); + } + suspend { + _ = windows.kernel32.ReadDirectoryChangesW( + dir_handle, + &event_buf, + @intCast(windows.DWORD, event_buf.len), + windows.FALSE, // watch subtree + windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | + windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | + windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | + windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, + null, // number of bytes transferred (unused for async) + &resume_node.base.overlapped, + null, // completion routine - unused because we use IOCP + ); + } + } + var bytes_transferred: windows.DWORD = undefined; + if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { + const err = switch (windows.kernel32.GetLastError()) { + else => |err| windows.unexpectedError(err), + }; + self.channel.put(err); + } else { + // can't use @bytesToSlice because of the special variable length name field + var ptr = event_buf[0..].ptr; + const end_ptr = ptr + bytes_transferred; + var ev: *windows.FILE_NOTIFY_INFORMATION = undefined; + while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) { + ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr); + const emit = switch (ev.Action) { + windows.FILE_ACTION_REMOVED => WatchEventId.Delete, + windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite, + else => null, + }; + if (emit) |id| { + const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2]; + const user_value = blk: { + const held = dir.table_lock.acquire(); + defer held.release(); + + if (dir.file_table.get(basename_utf16le)) |entry| { + break :blk entry.value; + } else { + break :blk null; + } + }; + if (user_value) |v| { + self.channel.put(Event{ + .id = id, + .data = v, + }); + } + } + if (ev.NextEntryOffset == 0) break; + } + } + } + } + + pub fn removeFile(self: *Self, file_path: []const u8) ?V { + @panic("TODO"); + } + + fn linuxEventPutter(self: *Self) void { + global_event_loop.beginOneEvent(); + + defer { + self.os_data.table_lock.deinit(); + var wd_it = self.os_data.wd_table.iterator(); + while (wd_it.next()) |wd_entry| { + var file_it = wd_entry.value.file_table.iterator(); + while (file_it.next()) |file_entry| { + self.allocator.free(file_entry.key); + } + self.allocator.free(wd_entry.value.dirname); + wd_entry.value.file_table.deinit(); + } + self.os_data.wd_table.deinit(); + global_event_loop.finishOneEvent(); + os.close(self.os_data.inotify_fd); + self.channel.deinit(); + self.allocator.free(self.channel.buffer_nodes); + } + + var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined; + + while (!self.os_data.cancelled) { + const rc = os.linux.read(self.os_data.inotify_fd, &event_buf, event_buf.len); + const errno = os.linux.getErrno(rc); + switch (errno) { + 0 => { + // can't use @bytesToSlice because of the special variable length name field + var ptr = event_buf[0..].ptr; + const end_ptr = ptr + event_buf.len; + var ev: *os.linux.inotify_event = undefined; + while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) { + ev = @ptrCast(*os.linux.inotify_event, ptr); + if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) { + const basename_ptr = ptr + @sizeOf(os.linux.inotify_event); + // `ev.len` counts all bytes in `ev.name` including terminating null byte. + const basename_with_null = basename_ptr[0..ev.len]; + const user_value = blk: { + const held = self.os_data.table_lock.acquire(); + defer held.release(); + + const dir = &self.os_data.wd_table.get(ev.wd).?.value; + if (dir.file_table.get(basename_with_null)) |entry| { + break :blk entry.value; + } else { + break :blk null; + } + }; + if (user_value) |v| { + self.channel.put(Event{ + .id = WatchEventId.CloseWrite, + .data = v, + }); + } + } + + ptr = @alignCast(@alignOf(os.linux.inotify_event), ptr + @sizeOf(os.linux.inotify_event) + ev.len); + } + }, + os.linux.EINTR => continue, + os.linux.EINVAL => unreachable, + os.linux.EFAULT => unreachable, + os.linux.EAGAIN => { + global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT); + }, + else => unreachable, + } + } + } + }; +} const test_tmp_dir = "std_event_fs_test"; -// TODO this test is disabled until the async function rewrite is finished. -//test "write a file, watch it, write it again" { -// return error.SkipZigTest; -// const allocator = std.heap.direct_allocator; -// -// // TODO move this into event loop too -// try os.makePath(allocator, test_tmp_dir); -// defer os.deleteTree(allocator, test_tmp_dir) catch {}; -// -// var loop: Loop = undefined; -// try loop.initMultiThreaded(allocator); -// defer loop.deinit(); -// -// var result: anyerror!void = error.ResultNeverWritten; -// const handle = try async testFsWatchCantFail(&loop, &result); -// defer cancel handle; -// -// loop.run(); -// return result; -//} +test "write a file, watch it, write it again" { + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; -fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { - result.* = testFsWatch(loop); + const allocator = std.heap.page_allocator; + + // TODO move this into event loop too + try os.makePath(allocator, test_tmp_dir); + defer os.deleteTree(test_tmp_dir) catch {}; + + return testFsWatch(&allocator); } -fn testFsWatch(loop: *Loop) !void { - const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); - defer loop.allocator.free(file_path); +fn testFsWatch(allocator: *Allocator) !void { + const file_path = try std.fs.path.join(allocator, [_][]const u8{ test_tmp_dir, "file.txt" }); + defer allocator.free(file_path); const contents = \\line 1 @@ -1341,27 +1330,27 @@ fn testFsWatch(loop: *Loop) !void { const line2_offset = 7; // first just write then read the file - try writeFile(loop, file_path, contents); + try writeFile(allocator, file_path, contents); - const read_contents = try readFile(loop, file_path, 1024 * 1024); + const read_contents = try readFile(allocator, file_path, 1024 * 1024); testing.expectEqualSlices(u8, contents, read_contents); // now watch the file - var watch = try Watch(void).create(loop, 0); - defer watch.destroy(); + var watch = try Watch(void).init(allocator, 0); + defer watch.deinit(); testing.expect((try watch.addFile(file_path, {})) == null); - const ev = async watch.channel.get(); + const ev = watch.channel.get(); var ev_consumed = false; defer if (!ev_consumed) await ev; // overwrite line 2 - const fd = try await openReadWrite(loop, file_path, File.default_mode); + const fd = try await openReadWrite(file_path, File.default_mode); { defer os.close(fd); - try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset); + try pwritev(allocator, fd, []const []const u8{"lorem ipsum"}, line2_offset); } ev_consumed = true; @@ -1369,7 +1358,7 @@ fn testFsWatch(loop: *Loop) !void { WatchEventId.CloseWrite => {}, WatchEventId.Delete => @panic("wrong event"), } - const contents_updated = try readFile(loop, file_path, 1024 * 1024); + const contents_updated = try readFile(allocator, file_path, 1024 * 1024); testing.expectEqualSlices(u8, \\line 1 \\lorem ipsum @@ -1381,16 +1370,15 @@ fn testFsWatch(loop: *Loop) !void { pub const OutStream = struct { fd: fd_t, stream: Stream, - loop: *Loop, + allocator: *Allocator, offset: usize, pub const Error = File.WriteError; pub const Stream = event.io.OutStream(Error); - pub fn init(loop: *Loop, fd: fd_t, offset: usize) OutStream { + pub fn init(allocator: *Allocator, fd: fd_t, offset: usize) OutStream { return OutStream{ .fd = fd, - .loop = loop, .offset = offset, .stream = Stream{ .writeFn = writeFn }, }; @@ -1400,23 +1388,22 @@ pub const OutStream = struct { const self = @fieldParentPtr(OutStream, "stream", out_stream); const offset = self.offset; self.offset += bytes.len; - return pwritev(self.loop, self.fd, [][]const u8{bytes}, offset); + return pwritev(self.allocator, self.fd, [_][]const u8{bytes}, offset); } }; pub const InStream = struct { fd: fd_t, stream: Stream, - loop: *Loop, + allocator: *Allocator, offset: usize, pub const Error = PReadVError; // TODO make this not have OutOfMemory pub const Stream = event.io.InStream(Error); - pub fn init(loop: *Loop, fd: fd_t, offset: usize) InStream { + pub fn init(allocator: *Allocator, fd: fd_t, offset: usize) InStream { return InStream{ .fd = fd, - .loop = loop, .offset = offset, .stream = Stream{ .readFn = readFn }, }; @@ -1424,7 +1411,7 @@ pub const InStream = struct { fn readFn(in_stream: *Stream, bytes: []u8) Error!usize { const self = @fieldParentPtr(InStream, "stream", in_stream); - const amt = try preadv(self.loop, self.fd, [][]u8{bytes}, self.offset); + const amt = try preadv(self.allocator, self.fd, [_][]u8{bytes}, self.offset); self.offset += amt; return amt; } diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig index 1e3508de4..492582da7 100644 --- a/lib/std/event/future.zig +++ b/lib/std/event/future.zig @@ -3,7 +3,6 @@ const assert = std.debug.assert; const testing = std.testing; const builtin = @import("builtin"); const Lock = std.event.Lock; -const Loop = std.event.Loop; /// This is a value that starts out unavailable, until resolve() is called /// While it is unavailable, functions suspend when they try to get() it, @@ -13,20 +12,21 @@ pub fn Future(comptime T: type) type { return struct { lock: Lock, data: T, + available: Available, - /// TODO make this an enum - /// 0 - not started - /// 1 - started - /// 2 - finished - available: u8, + const Available = enum(u8) { + NotStarted, + Started, + Finished, + }; const Self = @This(); const Queue = std.atomic.Queue(anyframe); - pub fn init(loop: *Loop) Self { + pub fn init() Self { return Self{ - .lock = Lock.initLocked(loop), - .available = 0, + .lock = Lock.initLocked(), + .available = .NotStarted, .data = undefined, }; } @@ -35,7 +35,7 @@ pub fn Future(comptime T: type) type { /// available. /// Thread-safe. pub async fn get(self: *Self) *T { - if (@atomicLoad(u8, &self.available, .SeqCst) == 2) { + if (@atomicLoad(Available, &self.available, .SeqCst) == .Finished) { return &self.data; } const held = self.lock.acquire(); @@ -47,7 +47,7 @@ pub fn Future(comptime T: type) type { /// Gets the data without waiting for it. If it's available, a pointer is /// returned. Otherwise, null is returned. pub fn getOrNull(self: *Self) ?*T { - if (@atomicLoad(u8, &self.available, .SeqCst) == 2) { + if (@atomicLoad(Available, &self.available, .SeqCst) == .Finished) { return &self.data; } else { return null; @@ -60,14 +60,14 @@ pub fn Future(comptime T: type) type { /// It's not required to call start() before resolve() but it can be useful since /// this method is thread-safe. pub async fn start(self: *Self) ?*T { - const state = @cmpxchgStrong(u8, &self.available, 0, 1, .SeqCst, .SeqCst) orelse return null; + const state = @cmpxchgStrong(Available, &self.available, .NotStarted, .Started, .SeqCst, .SeqCst) orelse return null; switch (state) { - 1 => { + .Started => { const held = self.lock.acquire(); held.release(); return &self.data; }, - 2 => return &self.data, + .Finished => return &self.data, else => unreachable, } } @@ -75,8 +75,8 @@ pub fn Future(comptime T: type) type { /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void { - const prev = @atomicRmw(u8, &self.available, .Xchg, 2, .SeqCst); - assert(prev == 0 or prev == 1); // resolve() called twice + const prev = @atomicRmw(Available, &self.available, .Xchg, .Finished, .SeqCst); + assert(prev != .Finished); // resolve() called twice Lock.Held.release(Lock.Held{ .lock = &self.lock }); } }; @@ -86,21 +86,15 @@ test "std.event.Future" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/3251 - if (std.os.freebsd.is_the_target) return error.SkipZigTest; + if (builtin.os == .freebsd) return error.SkipZigTest; + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; - const allocator = std.heap.direct_allocator; - - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - const handle = async testFuture(&loop); - - loop.run(); + const handle = async testFuture(); } -fn testFuture(loop: *Loop) void { - var future = Future(i32).init(loop); +fn testFuture() void { + var future = Future(i32).init(); var a = async waitOnFuture(&future); var b = async waitOnFuture(&future); diff --git a/lib/std/event/group.zig b/lib/std/event/group.zig index f96b938f8..266809661 100644 --- a/lib/std/event/group.zig +++ b/lib/std/event/group.zig @@ -1,15 +1,16 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const Lock = std.event.Lock; -const Loop = std.event.Loop; const testing = std.testing; +const Allocator = std.mem.Allocator; /// ReturnType must be `void` or `E!void` pub fn Group(comptime ReturnType: type) type { return struct { frame_stack: Stack, - alloc_stack: Stack, + alloc_stack: AllocStack, lock: Lock, + allocator: *Allocator, const Self = @This(); @@ -18,21 +19,30 @@ pub fn Group(comptime ReturnType: type) type { else => void, }; const Stack = std.atomic.Stack(anyframe->ReturnType); + const AllocStack = std.atomic.Stack(Node); - pub fn init(loop: *Loop) Self { + pub const Node = struct { + bytes: []const u8 = [0]u8{}, + handle: anyframe->ReturnType, + }; + + pub fn init(allocator: *Allocator) Self { return Self{ .frame_stack = Stack.init(), - .alloc_stack = Stack.init(), - .lock = Lock.init(loop), + .alloc_stack = AllocStack.init(), + .lock = Lock.init(), + .allocator = allocator, }; } /// Add a frame to the group. Thread-safe. pub fn add(self: *Self, handle: anyframe->ReturnType) (error{OutOfMemory}!void) { - const node = try self.lock.loop.allocator.create(Stack.Node); - node.* = Stack.Node{ + const node = try self.allocator.create(AllocStack.Node); + node.* = AllocStack.Node{ .next = undefined, - .data = handle, + .data = Node{ + .handle = handle, + }, }; self.alloc_stack.push(node); } @@ -46,6 +56,26 @@ pub fn Group(comptime ReturnType: type) type { self.frame_stack.push(node); } + /// This is equivalent to adding a frame to the group but the memory of its frame is + /// allocated by the group and freed by `wait`. + /// `func` must be async and have return type `ReturnType`. + /// Thread-safe. + pub fn call(self: *Self, comptime func: var, args: var) error{OutOfMemory}!void { + var frame = try self.allocator.create(@TypeOf(@call(.{ .modifier = .async_kw }, func, args))); + errdefer self.allocator.destroy(frame); + const node = try self.allocator.create(AllocStack.Node); + errdefer self.allocator.destroy(node); + node.* = AllocStack.Node{ + .next = undefined, + .data = Node{ + .handle = frame, + .bytes = std.mem.asBytes(frame), + }, + }; + frame.* = @call(.{ .modifier = .async_kw }, func, args); + self.alloc_stack.push(node); + } + /// Wait for all the calls and promises of the group to complete. /// Thread-safe. /// Safe to call any number of times. @@ -65,8 +95,7 @@ pub fn Group(comptime ReturnType: type) type { } } while (self.alloc_stack.pop()) |node| { - const handle = node.data; - self.lock.loop.allocator.destroy(node); + const handle = node.data.handle; if (Error == void) { await handle; } else { @@ -74,6 +103,8 @@ pub fn Group(comptime ReturnType: type) type { result = err; }; } + self.allocator.free(node.data.bytes); + self.allocator.destroy(node); } return result; } @@ -84,20 +115,15 @@ test "std.event.Group" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; - const allocator = std.heap.direct_allocator; + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - const handle = async testGroup(&loop); - - loop.run(); + const handle = async testGroup(std.heap.page_allocator); } -async fn testGroup(loop: *Loop) void { +async fn testGroup(allocator: *Allocator) void { var count: usize = 0; - var group = Group(void).init(loop); + var group = Group(void).init(allocator); var sleep_a_little_frame = async sleepALittle(&count); group.add(&sleep_a_little_frame) catch @panic("memory"); var increase_by_ten_frame = async increaseByTen(&count); @@ -105,7 +131,7 @@ async fn testGroup(loop: *Loop) void { group.wait(); testing.expect(count == 11); - var another = Group(anyerror!void).init(loop); + var another = Group(anyerror!void).init(allocator); var something_else_frame = async somethingElse(); another.add(&something_else_frame) catch @panic("memory"); var something_that_fails_frame = async doSomethingThatFails(); diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig index a0b1fd3e5..a95c5bf7e 100644 --- a/lib/std/event/lock.zig +++ b/lib/std/event/lock.zig @@ -9,27 +9,30 @@ const Loop = std.event.Loop; /// Functions which are waiting for the lock are suspended, and /// are resumed when the lock is released, in order. /// Allows only one actor to hold the lock. +/// TODO: make this API also work in blocking I/O mode. pub const Lock = struct { - loop: *Loop, shared_bit: u8, // TODO make this a bool queue: Queue, queue_empty_bit: u8, // TODO make this a bool const Queue = std.atomic.Queue(anyframe); + const global_event_loop = Loop.instance orelse + @compileError("std.event.Lock currently only works with event-based I/O"); + pub const Held = struct { lock: *Lock, pub fn release(self: Held) void { // Resume the next item from the queue. if (self.lock.queue.get()) |node| { - self.lock.loop.onNextTick(node); + global_event_loop.onNextTick(node); return; } // We need to release the lock. - _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst); - _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); + @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); // There might be a queue item. If we know the queue is empty, we can be done, // because the other actor will try to obtain the lock. @@ -48,13 +51,13 @@ pub const Lock = struct { // Resume the next item from the queue. if (self.lock.queue.get()) |node| { - self.lock.loop.onNextTick(node); + global_event_loop.onNextTick(node); return; } // Release the lock again. - _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst); - _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); + @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); // Find out if we can be done. if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) { @@ -64,18 +67,16 @@ pub const Lock = struct { } }; - pub fn init(loop: *Loop) Lock { + pub fn init() Lock { return Lock{ - .loop = loop, .shared_bit = 0, .queue = Queue.init(), .queue_empty_bit = 1, }; } - pub fn initLocked(loop: *Loop) Lock { + pub fn initLocked() Lock { return Lock{ - .loop = loop, .shared_bit = 1, .queue = Queue.init(), .queue_empty_bit = 1, @@ -100,7 +101,7 @@ pub const Lock = struct { // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor // will attempt to grab the lock. - _ = @atomicRmw(u8, &self.queue_empty_bit, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.queue_empty_bit, 0, .SeqCst); const old_bit = @atomicRmw(u8, &self.shared_bit, .Xchg, 1, .SeqCst); if (old_bit == 0) { @@ -118,32 +119,29 @@ pub const Lock = struct { test "std.event.Lock" { // TODO https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; + // TODO https://github.com/ziglang/zig/issues/3251 - if (std.os.freebsd.is_the_target) return error.SkipZigTest; + if (builtin.os == .freebsd) return error.SkipZigTest; - const allocator = std.heap.direct_allocator; + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var lock = Lock.init(&loop); + var lock = Lock.init(); defer lock.deinit(); - _ = async testLock(&loop, &lock); - loop.run(); + _ = async testLock(&lock); testing.expectEqualSlices(i32, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len, shared_test_data); } -async fn testLock(loop: *Loop, lock: *Lock) void { +async fn testLock(lock: *Lock) void { var handle1 = async lockRunner(lock); var tick_node1 = Loop.NextTickNode{ .prev = undefined, .next = undefined, .data = &handle1, }; - loop.onNextTick(&tick_node1); + Loop.instance.?.onNextTick(&tick_node1); var handle2 = async lockRunner(lock); var tick_node2 = Loop.NextTickNode{ @@ -151,7 +149,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void { .next = undefined, .data = &handle2, }; - loop.onNextTick(&tick_node2); + Loop.instance.?.onNextTick(&tick_node2); var handle3 = async lockRunner(lock); var tick_node3 = Loop.NextTickNode{ @@ -159,7 +157,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void { .next = undefined, .data = &handle3, }; - loop.onNextTick(&tick_node3); + Loop.instance.?.onNextTick(&tick_node3); await handle1; await handle2; diff --git a/lib/std/event/locked.zig b/lib/std/event/locked.zig index aeedf3558..5e9c0ea10 100644 --- a/lib/std/event/locked.zig +++ b/lib/std/event/locked.zig @@ -1,6 +1,5 @@ const std = @import("../std.zig"); const Lock = std.event.Lock; -const Loop = std.event.Loop; /// Thread-safe async/await lock that protects one piece of data. /// Functions which are waiting for the lock are suspended, and @@ -21,9 +20,9 @@ pub fn Locked(comptime T: type) type { } }; - pub fn init(loop: *Loop, data: T) Self { + pub fn init(data: T) Self { return Self{ - .lock = Lock.init(loop), + .lock = Lock.init(), .private_data = data, }; } @@ -35,7 +34,7 @@ pub fn Locked(comptime T: type) type { pub async fn acquire(self: *Self) HeldLock { return HeldLock{ // TODO guaranteed allocation elision - .held = await (async self.lock.acquire() catch unreachable), + .held = self.lock.acquire(), .value = &self.private_data, }; } diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index d0d36abc0..e80266c64 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -42,7 +42,7 @@ pub const Loop = struct { }, else => {}, }; - pub const Overlapped = @typeOf(overlapped_init); + pub const Overlapped = @TypeOf(overlapped_init); pub const Id = enum { Basic, @@ -51,7 +51,7 @@ pub const Loop = struct { }; pub const EventFd = switch (builtin.os) { - .macosx, .freebsd, .netbsd => KEventFd, + .macosx, .freebsd, .netbsd, .dragonfly => KEventFd, .linux => struct { base: ResumeNode, epoll_op: u32, @@ -61,7 +61,7 @@ pub const Loop = struct { base: ResumeNode, completion_key: usize, }, - else => @compileError("unsupported OS"), + else => struct {}, }; const KEventFd = struct { @@ -70,7 +70,7 @@ pub const Loop = struct { }; pub const Basic = switch (builtin.os) { - .macosx, .freebsd, .netbsd => KEventBasic, + .macosx, .freebsd, .netbsd, .dragonfly => KEventBasic, .linux => struct { base: ResumeNode, }, @@ -96,11 +96,11 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 - pub fn init(self: *Loop, allocator: *mem.Allocator) !void { + pub fn init(self: *Loop) !void { if (builtin.single_threaded) { - return self.initSingleThreaded(allocator); + return self.initSingleThreaded(); } else { - return self.initMultiThreaded(allocator); + return self.initMultiThreaded(); } } @@ -108,25 +108,28 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 - pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { - return self.initInternal(allocator, 1); + pub fn initSingleThreaded(self: *Loop) !void { + return self.initThreadPool(1); } - /// The allocator must be thread-safe because we use it for multiplexing - /// async functions onto kernel threads. /// After initialization, call run(). + /// This is the same as `initThreadPool` using `Thread.cpuCount` to determine the thread + /// pool size. /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 - pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { - if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); + pub fn initMultiThreaded(self: *Loop) !void { + if (builtin.single_threaded) + @compileError("initMultiThreaded unavailable when building in single-threaded mode"); const core_count = try Thread.cpuCount(); - return self.initInternal(allocator, core_count); + return self.initThreadPool(core_count); } /// Thread count is the total thread count. The thread pool size will be /// max(thread_count - 1, 0) - fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { + pub fn initThreadPool(self: *Loop, thread_count: usize) !void { + // TODO: https://github.com/ziglang/zig/issues/3539 + const allocator = std.heap.page_allocator; self.* = Loop{ .pending_event_count = 1, .allocator = allocator, @@ -234,7 +237,7 @@ pub const Loop = struct { var extra_thread_index: usize = 0; errdefer { // writing 8 bytes to an eventfd cannot fail - os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; while (extra_thread_index != 0) { extra_thread_index -= 1; self.extra_threads[extra_thread_index].wait(); @@ -244,7 +247,7 @@ pub const Loop = struct { self.extra_threads[extra_thread_index] = try Thread.spawn(self, workerRun); } }, - .macosx, .freebsd, .netbsd => { + .macosx, .freebsd, .netbsd, .dragonfly => { self.os_data.kqfd = try os.kqueue(); errdefer os.close(self.os_data.kqfd); @@ -263,7 +266,7 @@ pub const Loop = struct { }, }; - const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + const empty_kevs = &[0]os.Kevent{}; for (self.eventfd_resume_nodes) |*eventfd_node, i| { eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ @@ -286,7 +289,7 @@ pub const Loop = struct { .next = undefined, }; self.available_eventfd_resume_nodes.push(eventfd_node); - const kevent_array = (*const [1]os.Kevent)(&eventfd_node.data.kevent); + const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.data.kevent); _ = try os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null); eventfd_node.data.kevent.flags = os.EV_CLEAR | os.EV_ENABLE; eventfd_node.data.kevent.fflags = os.NOTE_TRIGGER; @@ -302,7 +305,7 @@ pub const Loop = struct { .data = 0, .udata = @ptrToInt(&self.final_resume_node), }; - const final_kev_arr = (*const [1]os.Kevent)(&self.os_data.final_kevent); + const final_kev_arr = @as(*const [1]os.Kevent, &self.os_data.final_kevent); _ = try os.kevent(self.os_data.kqfd, final_kev_arr, empty_kevs, null); self.os_data.final_kevent.flags = os.EV_ENABLE; self.os_data.final_kevent.fflags = os.NOTE_TRIGGER; @@ -409,7 +412,7 @@ pub const Loop = struct { os.close(self.os_data.epollfd); self.allocator.free(self.eventfd_resume_nodes); }, - .macosx, .freebsd, .netbsd => { + .macosx, .freebsd, .netbsd, .dragonfly => { os.close(self.os_data.kqfd); os.close(self.os_data.fs_kqfd); }, @@ -448,22 +451,67 @@ pub const Loop = struct { self.finishOneEvent(); } - pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { - defer self.linuxRemoveFd(fd); + pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) void { + assert(flags & os.EPOLLET == os.EPOLLET); + assert(flags & os.EPOLLONESHOT == os.EPOLLONESHOT); + var resume_node = ResumeNode.Basic{ + .base = ResumeNode{ + .id = .Basic, + .handle = @frame(), + .overlapped = ResumeNode.overlapped_init, + }, + }; + var need_to_delete = false; + defer if (need_to_delete) self.linuxRemoveFd(fd); + suspend { - var resume_node = ResumeNode.Basic{ - .base = ResumeNode{ - .id = .Basic, - .handle = @frame(), - .overlapped = ResumeNode.overlapped_init, + if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| { + need_to_delete = true; + } else |err| switch (err) { + error.FileDescriptorNotRegistered => unreachable, + error.OperationCausesCircularLoop => unreachable, + error.FileDescriptorIncompatibleWithEpoll => unreachable, + error.FileDescriptorAlreadyPresentInSet => unreachable, // evented writes to the same fd is not thread-safe + + error.SystemResources, + error.UserResourceLimitReached, + error.Unexpected, + => { + // Fall back to a blocking poll(). Ideally this codepath is never hit, since + // epoll should be just fine. But this is better than incorrect behavior. + var poll_flags: i16 = 0; + if ((flags & os.EPOLLIN) != 0) poll_flags |= os.POLLIN; + if ((flags & os.EPOLLOUT) != 0) poll_flags |= os.POLLOUT; + var pfd = [1]os.pollfd{os.pollfd{ + .fd = fd, + .events = poll_flags, + .revents = undefined, + }}; + _ = os.poll(&pfd, -1) catch |poll_err| switch (poll_err) { + error.SystemResources, + error.Unexpected, + => { + // Even poll() didn't work. The best we can do now is sleep for a + // small duration and then hope that something changed. + std.time.sleep(1 * std.time.millisecond); + }, + }; + resume @frame(); }, - }; - try self.linuxAddFd(fd, &resume_node.base, flags); + } } } - pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) !void { - return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN); + pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) void { + return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN); + } + + pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) void { + return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT); + } + + pub fn waitUntilFdWritableOrReadable(self: *Loop, fd: os.fd_t) void { + return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN); } pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent { @@ -523,9 +571,9 @@ pub const Loop = struct { const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = next_tick_node.data; switch (builtin.os) { - .macosx, .freebsd, .netbsd => { - const kevent_array = (*const [1]os.Kevent)(&eventfd_node.kevent); - const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + .macosx, .freebsd, .netbsd, .dragonfly => { + const kevent_array = @as(*const [1]os.Kevent, &eventfd_node.kevent); + const empty_kevs = &[0]os.Kevent{}; _ = os.kevent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch { self.next_tick_queue.unget(next_tick_node); self.available_eventfd_resume_nodes.push(resume_stack_node); @@ -587,6 +635,7 @@ pub const Loop = struct { .macosx, .freebsd, .netbsd, + .dragonfly, => self.os_data.fs_thread.wait(), else => {}, } @@ -596,12 +645,6 @@ pub const Loop = struct { } } - /// This is equivalent to function call, except it calls `startCpuBoundOperation` first. - pub fn call(comptime func: var, args: ...) @typeOf(func).ReturnType { - startCpuBoundOperation(); - return func(args); - } - /// Yielding lets the event loop run, starting any unstarted async operations. /// Note that async operations automatically start when a function yields for any other reason, /// for example, when async I/O is performed. This function is intended to be used only when @@ -641,13 +684,13 @@ pub const Loop = struct { .linux => { self.posixFsRequest(&self.os_data.fs_end_request); // writing 8 bytes to an eventfd cannot fail - os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + noasync os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; return; }, - .macosx, .freebsd, .netbsd => { + .macosx, .freebsd, .netbsd, .dragonfly => { self.posixFsRequest(&self.os_data.fs_end_request); - const final_kevent = (*const [1]os.Kevent)(&self.os_data.final_kevent); - const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent); + const empty_kevs = &[0]os.Kevent{}; // cannot fail because we already added it and this just enables it _ = os.kevent(self.os_data.kqfd, final_kevent, empty_kevs, null) catch unreachable; return; @@ -702,9 +745,9 @@ pub const Loop = struct { } } }, - .macosx, .freebsd, .netbsd => { + .macosx, .freebsd, .netbsd, .dragonfly => { var eventlist: [1]os.Kevent = undefined; - const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + const empty_kevs = &[0]os.Kevent{}; const count = os.kevent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable; for (eventlist[0..count]) |ev| { const resume_node = @intToPtr(*ResumeNode, ev.udata); @@ -765,13 +808,13 @@ pub const Loop = struct { self.beginOneEvent(); // finished in posixFsRun after processing the msg self.os_data.fs_queue.put(request_node); switch (builtin.os) { - .macosx, .freebsd, .netbsd => { - const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wake); - const empty_kevs = ([*]os.Kevent)(undefined)[0..0]; + .macosx, .freebsd, .netbsd, .dragonfly => { + const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake); + const empty_kevs = &[0]os.Kevent{}; _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; }, .linux => { - _ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + @atomicStore(i32, &self.os_data.fs_queue_item, 1, AtomicOrder.SeqCst); const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1); switch (os.linux.getErrno(rc)) { 0 => {}, @@ -789,36 +832,39 @@ pub const Loop = struct { } } + // TODO make this whole function noasync + // https://github.com/ziglang/zig/issues/3157 fn posixFsRun(self: *Loop) void { while (true) { if (builtin.os == .linux) { - _ = @atomicRmw(i32, &self.os_data.fs_queue_item, .Xchg, 0, .SeqCst); + @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); } while (self.os_data.fs_queue.get()) |node| { switch (node.data.msg) { .End => return, .WriteV => |*msg| { - msg.result = os.writev(msg.fd, msg.iov); + msg.result = noasync os.writev(msg.fd, msg.iov); }, .PWriteV => |*msg| { - msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); + msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset); }, .PReadV => |*msg| { - msg.result = os.preadv(msg.fd, msg.iov, msg.offset); + msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset); }, .Open => |*msg| { - msg.result = os.openC(msg.path.ptr, msg.flags, msg.mode); + msg.result = noasync os.openC(msg.path.ptr, msg.flags, msg.mode); }, - .Close => |*msg| os.close(msg.fd), + .Close => |*msg| noasync os.close(msg.fd), .WriteFile => |*msg| blk: { - const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; - const fd = os.openC(msg.path.ptr, flags, msg.mode) catch |err| { + const fd = noasync os.openC(msg.path.ptr, flags, msg.mode) catch |err| { msg.result = err; break :blk; }; - defer os.close(fd); - msg.result = os.write(fd, msg.contents); + defer noasync os.close(fd); + msg.result = noasync os.write(fd, msg.contents); }, } switch (node.data.finish) { @@ -838,8 +884,8 @@ pub const Loop = struct { else => unreachable, } }, - .macosx, .freebsd, .netbsd => { - const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait); + .macosx, .freebsd, .netbsd, .dragonfly => { + const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wait); var out_kevs: [1]os.Kevent = undefined; _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable; }, @@ -850,7 +896,7 @@ pub const Loop = struct { const OsData = switch (builtin.os) { .linux => LinuxOsData, - .macosx, .freebsd, .netbsd => KEventData, + .macosx, .freebsd, .netbsd, .dragonfly => KEventData, .windows => struct { io_port: windows.HANDLE, extra_thread_count: usize, @@ -884,34 +930,13 @@ test "std.event.Loop - basic" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; - const allocator = std.heap.direct_allocator; - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); + try loop.initMultiThreaded(); defer loop.deinit(); loop.run(); } -test "std.event.Loop - call" { - // https://github.com/ziglang/zig/issues/1908 - if (builtin.single_threaded) return error.SkipZigTest; - - const allocator = std.heap.direct_allocator; - - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var did_it = false; - var handle = async Loop.call(testEventLoop); - var handle2 = async Loop.call(testEventLoop2, &handle, &did_it); - - loop.run(); - - testing.expect(did_it); -} - async fn testEventLoop() i32 { return 1234; } diff --git a/lib/std/event/net.zig b/lib/std/event/net.zig deleted file mode 100644 index bed665dcd..000000000 --- a/lib/std/event/net.zig +++ /dev/null @@ -1,358 +0,0 @@ -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const testing = std.testing; -const event = std.event; -const mem = std.mem; -const os = std.os; -const Loop = std.event.Loop; -const File = std.fs.File; -const fd_t = os.fd_t; - -pub const Server = struct { - handleRequestFn: async fn (*Server, *const std.net.Address, File) void, - - loop: *Loop, - sockfd: ?i32, - accept_frame: ?anyframe, - listen_address: std.net.Address, - - waiting_for_emfile_node: PromiseNode, - listen_resume_node: event.Loop.ResumeNode, - - const PromiseNode = std.TailQueue(anyframe).Node; - - pub fn init(loop: *Loop) Server { - // TODO can't initialize handler here because we need well defined copy elision - return Server{ - .loop = loop, - .sockfd = null, - .accept_frame = null, - .handleRequestFn = undefined, - .waiting_for_emfile_node = undefined, - .listen_address = undefined, - .listen_resume_node = event.Loop.ResumeNode{ - .id = event.Loop.ResumeNode.Id.Basic, - .handle = undefined, - .overlapped = event.Loop.ResumeNode.overlapped_init, - }, - }; - } - - pub fn listen( - self: *Server, - address: *const std.net.Address, - handleRequestFn: async fn (*Server, *const std.net.Address, File) void, - ) !void { - self.handleRequestFn = handleRequestFn; - - const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); - errdefer os.close(sockfd); - self.sockfd = sockfd; - - try os.bind(sockfd, &address.os_addr); - try os.listen(sockfd, os.SOMAXCONN); - self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd)); - - self.accept_frame = async Server.handler(self); - errdefer await self.accept_frame.?; - - self.listen_resume_node.handle = self.accept_frame.?; - try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); - errdefer self.loop.removeFd(sockfd); - } - - /// Stop listening - pub fn close(self: *Server) void { - self.loop.linuxRemoveFd(self.sockfd.?); - if (self.sockfd) |fd| { - os.close(fd); - self.sockfd = null; - } - } - - pub fn deinit(self: *Server) void { - if (self.accept_frame) |accept_frame| await accept_frame; - if (self.sockfd) |sockfd| os.close(sockfd); - } - - pub async fn handler(self: *Server) void { - while (true) { - var accepted_addr: std.net.Address = undefined; - // TODO just inline the following function here and don't expose it as posixAsyncAccept - if (os.accept4_async(self.sockfd.?, &accepted_addr.os_addr, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC)) |accepted_fd| { - if (accepted_fd == -1) { - // would block - suspend; // we will get resumed by epoll_wait in the event loop - continue; - } - var socket = File.openHandle(accepted_fd); - self.handleRequestFn(self, &accepted_addr, socket); - } else |err| switch (err) { - error.ProcessFdQuotaExceeded => @panic("TODO handle this error"), - error.ConnectionAborted => continue, - - error.FileDescriptorNotASocket => unreachable, - error.OperationNotSupported => unreachable, - - error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => { - @panic("TODO handle this error"); - }, - } - } - } -}; - -pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 { - const sockfd = try os.socket( - os.AF_UNIX, - os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, - 0, - ); - errdefer os.close(sockfd); - - var sock_addr = os.sockaddr_un{ - .family = os.AF_UNIX, - .path = undefined, - }; - - if (path.len > @typeOf(sock_addr.path).len) return error.NameTooLong; - mem.copy(u8, sock_addr.path[0..], path); - const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len); - try os.connect_async(sockfd, &sock_addr, size); - try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); - try os.getsockoptError(sockfd); - - return sockfd; -} - -pub const ReadError = error{ - SystemResources, - Unexpected, - UserResourceLimitReached, - InputOutput, - - FileDescriptorNotRegistered, // TODO remove this possibility - OperationCausesCircularLoop, // TODO remove this possibility - FileDescriptorAlreadyPresentInSet, // TODO remove this possibility - FileDescriptorIncompatibleWithEpoll, // TODO remove this possibility -}; - -/// returns number of bytes read. 0 means EOF. -pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize { - const iov = os.iovec{ - .iov_base = buffer.ptr, - .iov_len = buffer.len, - }; - const iovs: *const [1]os.iovec = &iov; - return readvPosix(loop, fd, iovs, 1); -} - -pub const WriteError = error{}; - -pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteError!void { - const iov = os.iovec_const{ - .iov_base = buffer.ptr, - .iov_len = buffer.len, - }; - const iovs: *const [1]os.iovec_const = &iov; - return writevPosix(loop, fd, iovs, 1); -} - -pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void { - while (true) { - switch (builtin.os) { - .macosx, .linux => { - switch (os.errno(os.system.writev(fd, iov, count))) { - 0 => return, - os.EINTR => continue, - os.ESPIPE => unreachable, - os.EINVAL => unreachable, - os.EFAULT => unreachable, - os.EAGAIN => { - try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT); - continue; - }, - os.EBADF => unreachable, // always a race condition - os.EDESTADDRREQ => unreachable, // connect was never called - os.EDQUOT => unreachable, - os.EFBIG => unreachable, - os.EIO => return error.InputOutput, - os.ENOSPC => unreachable, - os.EPERM => return error.AccessDenied, - os.EPIPE => unreachable, - else => |err| return os.unexpectedErrno(err), - } - }, - else => @compileError("Unsupported OS"), - } - } -} - -/// returns number of bytes read. 0 means EOF. -pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count: usize) !usize { - while (true) { - switch (builtin.os) { - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.macosx => { - const rc = os.system.readv(fd, iov, count); - switch (os.errno(rc)) { - 0 => return rc, - os.EINTR => continue, - os.EINVAL => unreachable, - os.EFAULT => unreachable, - os.EAGAIN => { - try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN); - continue; - }, - os.EBADF => unreachable, // always a race condition - os.EIO => return error.InputOutput, - os.EISDIR => unreachable, - os.ENOBUFS => return error.SystemResources, - os.ENOMEM => return error.SystemResources, - else => |err| return os.unexpectedErrno(err), - } - }, - else => @compileError("Unsupported OS"), - } - } -} - -pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void { - const iovecs = try loop.allocator.alloc(os.iovec_const, data.len); - defer loop.allocator.free(iovecs); - - for (data) |buf, i| { - iovecs[i] = os.iovec_const{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - } - - return writevPosix(loop, fd, iovecs.ptr, data.len); -} - -pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize { - const iovecs = try loop.allocator.alloc(os.iovec, data.len); - defer loop.allocator.free(iovecs); - - for (data) |buf, i| { - iovecs[i] = os.iovec{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - } - - return readvPosix(loop, fd, iovecs.ptr, data.len); -} - -pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File { - var address = _address.*; // TODO https://github.com/ziglang/zig/issues/1592 - - const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); - errdefer os.close(sockfd); - - try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in)); - try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); - try os.getsockoptError(sockfd); - - return File.openHandle(sockfd); -} - -test "listen on a port, send bytes, receive bytes" { - // https://github.com/ziglang/zig/issues/2377 - if (true) return error.SkipZigTest; - - if (builtin.os != builtin.Os.linux) { - // TODO build abstractions for other operating systems - return error.SkipZigTest; - } - - const MyServer = struct { - tcp_server: Server, - - const Self = @This(); - async fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void { - const self = @fieldParentPtr(Self, "tcp_server", tcp_server); - var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 - defer socket.close(); - const next_handler = errorableHandler(self, _addr, socket) catch |err| { - std.debug.panic("unable to handle connection: {}\n", err); - }; - } - async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void { - const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/1592 - var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 - - const stream = &socket.outStream().stream; - try stream.print("hello from server\n"); - } - }; - - const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; - const addr = std.net.Address.initIp4(ip4addr, 0); - - var loop: Loop = undefined; - try loop.initSingleThreaded(std.debug.global_allocator); - var server = MyServer{ .tcp_server = Server.init(&loop) }; - defer server.tcp_server.deinit(); - try server.tcp_server.listen(&addr, MyServer.handler); - - _ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server); - loop.run(); -} - -async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void { - errdefer @panic("test failure"); - - var socket_file = try connect(loop, address); - defer socket_file.close(); - - var buf: [512]u8 = undefined; - const amt_read = try socket_file.read(buf[0..]); - const msg = buf[0..amt_read]; - testing.expect(mem.eql(u8, msg, "hello from server\n")); - server.close(); -} - -pub const OutStream = struct { - fd: fd_t, - stream: Stream, - loop: *Loop, - - pub const Error = WriteError; - pub const Stream = event.io.OutStream(Error); - - pub fn init(loop: *Loop, fd: fd_t) OutStream { - return OutStream{ - .fd = fd, - .loop = loop, - .stream = Stream{ .writeFn = writeFn }, - }; - } - - async fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { - const self = @fieldParentPtr(OutStream, "stream", out_stream); - return write(self.loop, self.fd, bytes); - } -}; - -pub const InStream = struct { - fd: fd_t, - stream: Stream, - loop: *Loop, - - pub const Error = ReadError; - pub const Stream = event.io.InStream(Error); - - pub fn init(loop: *Loop, fd: fd_t) InStream { - return InStream{ - .fd = fd, - .loop = loop, - .stream = Stream{ .readFn = readFn }, - }; - } - - async fn readFn(in_stream: *Stream, bytes: []u8) Error!usize { - const self = @fieldParentPtr(InStream, "stream", in_stream); - return read(self.loop, self.fd, bytes); - } -}; diff --git a/lib/std/event/rwlock.zig b/lib/std/event/rwlock.zig index bf7ea0fa9..f4b13d008 100644 --- a/lib/std/event/rwlock.zig +++ b/lib/std/event/rwlock.zig @@ -11,23 +11,26 @@ const Loop = std.event.Loop; /// Many readers can hold the lock at the same time; however locking for writing is exclusive. /// When a read lock is held, it will not be released until the reader queue is empty. /// When a write lock is held, it will not be released until the writer queue is empty. +/// TODO: make this API also work in blocking I/O mode pub const RwLock = struct { - loop: *Loop, - shared_state: u8, // TODO make this an enum + shared_state: State, writer_queue: Queue, reader_queue: Queue, writer_queue_empty_bit: u8, // TODO make this a bool reader_queue_empty_bit: u8, // TODO make this a bool reader_lock_count: usize, - const State = struct { - const Unlocked = 0; - const WriteLock = 1; - const ReadLock = 2; + const State = enum(u8) { + Unlocked, + WriteLock, + ReadLock, }; const Queue = std.atomic.Queue(anyframe); + const global_event_loop = Loop.instance orelse + @compileError("std.event.RwLock currently only works with event-based I/O"); + pub const HeldRead = struct { lock: *RwLock, @@ -37,8 +40,8 @@ pub const RwLock = struct { return; } - _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst); - if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) { + @atomicStore(u8, &self.lock.reader_queue_empty_bit, 1, .SeqCst); + if (@cmpxchgStrong(State, &self.lock.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) { // Didn't unlock. Someone else's problem. return; } @@ -54,31 +57,30 @@ pub const RwLock = struct { // See if we can leave it locked for writing, and pass the lock to the next writer // in the queue to grab the lock. if (self.lock.writer_queue.get()) |node| { - self.lock.loop.onNextTick(node); + global_event_loop.onNextTick(node); return; } // We need to release the write lock. Check if any readers are waiting to grab the lock. if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, .SeqCst) == 0) { // Switch to a read lock. - _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.ReadLock, .SeqCst); + @atomicStore(State, &self.lock.shared_state, .ReadLock, .SeqCst); while (self.lock.reader_queue.get()) |node| { - self.lock.loop.onNextTick(node); + global_event_loop.onNextTick(node); } return; } - _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst); - _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst); + @atomicStore(u8, &self.lock.writer_queue_empty_bit, 1, .SeqCst); + @atomicStore(State, &self.lock.shared_state, .Unlocked, .SeqCst); self.lock.commonPostUnlock(); } }; - pub fn init(loop: *Loop) RwLock { + pub fn init() RwLock { return RwLock{ - .loop = loop, - .shared_state = State.Unlocked, + .shared_state = .Unlocked, .writer_queue = Queue.init(), .writer_queue_empty_bit = 1, .reader_queue = Queue.init(), @@ -90,7 +92,7 @@ pub const RwLock = struct { /// Must be called when not locked. Not thread safe. /// All calls to acquire() and release() must complete before calling deinit(). pub fn deinit(self: *RwLock) void { - assert(self.shared_state == State.Unlocked); + assert(self.shared_state == .Unlocked); while (self.writer_queue.get()) |node| resume node.data; while (self.reader_queue.get()) |node| resume node.data; } @@ -111,15 +113,15 @@ pub const RwLock = struct { // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1, // some actor will attempt to grab the lock. - _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.reader_queue_empty_bit, 0, .SeqCst); // Here we don't care if we are the one to do the locking or if it was already locked for reading. - const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == State.ReadLock else true; + const have_read_lock = if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == .ReadLock else true; if (have_read_lock) { // Give out all the read locks. if (self.reader_queue.get()) |first_node| { while (self.reader_queue.get()) |node| { - self.loop.onNextTick(node); + global_event_loop.onNextTick(node); } resume first_node.data; } @@ -142,10 +144,10 @@ pub const RwLock = struct { // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1, // some actor will attempt to grab the lock. - _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 0, .SeqCst); + @atomicStore(u8, &self.writer_queue_empty_bit, 0, .SeqCst); // Here we must be the one to acquire the write lock. It cannot already be locked. - if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) == null) { + if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) == null) { // We now have a write lock. if (self.writer_queue.get()) |node| { // Whether this node is us or someone else, we tail resume it. @@ -164,37 +166,37 @@ pub const RwLock = struct { // But if there's a writer_queue item or a reader_queue item, // we are the actor which must loop and attempt to grab the lock again. if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) { - if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) != null) { + if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) != null) { // We did not obtain the lock. Great, the queues are someone else's problem. return; } // If there's an item in the writer queue, give them the lock, and we're done. if (self.writer_queue.get()) |node| { - self.loop.onNextTick(node); + global_event_loop.onNextTick(node); return; } // Release the lock again. - _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst); - _ = @atomicRmw(u8, &self.shared_state, .Xchg, State.Unlocked, .SeqCst); + @atomicStore(u8, &self.writer_queue_empty_bit, 1, .SeqCst); + @atomicStore(State, &self.shared_state, .Unlocked, .SeqCst); continue; } if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) { - if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst) != null) { + if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst) != null) { // We did not obtain the lock. Great, the queues are someone else's problem. return; } // If there are any items in the reader queue, give out all the reader locks, and we're done. if (self.reader_queue.get()) |first_node| { - self.loop.onNextTick(first_node); + global_event_loop.onNextTick(first_node); while (self.reader_queue.get()) |node| { - self.loop.onNextTick(node); + global_event_loop.onNextTick(node); } return; } // Release the lock again. - _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst); - if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) { + @atomicStore(u8, &self.reader_queue_empty_bit, 1, .SeqCst); + if (@cmpxchgStrong(State, &self.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) { // Didn't unlock. Someone else's problem. return; } @@ -212,48 +214,44 @@ test "std.event.RwLock" { // https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; - const allocator = std.heap.direct_allocator; + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var lock = RwLock.init(&loop); + var lock = RwLock.init(); defer lock.deinit(); - const handle = testLock(&loop, &lock); - loop.run(); + const handle = testLock(std.heap.page_allocator, &lock); const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len; testing.expectEqualSlices(i32, expected_result, shared_test_data); } -async fn testLock(loop: *Loop, lock: *RwLock) void { +async fn testLock(allocator: *Allocator, lock: *RwLock) void { var read_nodes: [100]Loop.NextTickNode = undefined; for (read_nodes) |*read_node| { - const frame = loop.allocator.create(@Frame(readRunner)) catch @panic("memory"); + const frame = allocator.create(@Frame(readRunner)) catch @panic("memory"); read_node.data = frame; frame.* = async readRunner(lock); - loop.onNextTick(read_node); + Loop.instance.?.onNextTick(read_node); } var write_nodes: [shared_it_count]Loop.NextTickNode = undefined; for (write_nodes) |*write_node| { - const frame = loop.allocator.create(@Frame(writeRunner)) catch @panic("memory"); + const frame = allocator.create(@Frame(writeRunner)) catch @panic("memory"); write_node.data = frame; frame.* = async writeRunner(lock); - loop.onNextTick(write_node); + Loop.instance.?.onNextTick(write_node); } for (write_nodes) |*write_node| { const casted = @ptrCast(*const @Frame(writeRunner), write_node.data); await casted; - loop.allocator.destroy(casted); + allocator.destroy(casted); } for (read_nodes) |*read_node| { const casted = @ptrCast(*const @Frame(readRunner), read_node.data); await casted; - loop.allocator.destroy(casted); + allocator.destroy(casted); } } diff --git a/lib/std/event/rwlocked.zig b/lib/std/event/rwlocked.zig index 386aa0840..3f4c6ddbf 100644 --- a/lib/std/event/rwlocked.zig +++ b/lib/std/event/rwlocked.zig @@ -1,6 +1,5 @@ const std = @import("../std.zig"); const RwLock = std.event.RwLock; -const Loop = std.event.Loop; /// Thread-safe async/await RW lock that protects one piece of data. /// Functions which are waiting for the lock are suspended, and @@ -30,9 +29,9 @@ pub fn RwLocked(comptime T: type) type { } }; - pub fn init(loop: *Loop, data: T) Self { + pub fn init(data: T) Self { return Self{ - .lock = RwLock.init(loop), + .lock = RwLock.init(), .locked_data = data, }; } @@ -43,14 +42,14 @@ pub fn RwLocked(comptime T: type) type { pub async fn acquireRead(self: *Self) HeldReadLock { return HeldReadLock{ - .held = await (async self.lock.acquireRead() catch unreachable), + .held = self.lock.acquireRead(), .value = &self.locked_data, }; } pub async fn acquireWrite(self: *Self) HeldWriteLock { return HeldWriteLock{ - .held = await (async self.lock.acquireWrite() catch unreachable), + .held = self.lock.acquireWrite(), .value = &self.locked_data, }; } diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig new file mode 100644 index 000000000..7870979ab --- /dev/null +++ b/lib/std/fifo.zig @@ -0,0 +1,441 @@ +// FIFO of fixed size items +// Usually used for e.g. byte buffers + +const std = @import("std"); +const math = std.math; +const mem = std.mem; +const Allocator = mem.Allocator; +const debug = std.debug; +const assert = debug.assert; +const testing = std.testing; + +pub const LinearFifoBufferType = union(enum) { + /// The buffer is internal to the fifo; it is of the specified size. + Static: usize, + + /// The buffer is passed as a slice to the initialiser. + Slice, + + /// The buffer is managed dynamically using a `mem.Allocator`. + Dynamic, +}; + +pub fn LinearFifo( + comptime T: type, + comptime buffer_type: LinearFifoBufferType, +) type { + const autoalign = false; + + const powers_of_two = switch (buffer_type) { + .Static => std.math.isPowerOfTwo(buffer_type.Static), + .Slice => false, // Any size slice could be passed in + .Dynamic => true, // This could be configurable in future + }; + + return struct { + allocator: if (buffer_type == .Dynamic) *Allocator else void, + buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, + head: usize, + count: usize, + + const Self = @This(); + + // Type of Self argument for slice operations. + // If buffer is inline (Static) then we need to ensure we haven't + // returned a slice into a copy on the stack + const SliceSelfArg = if (buffer_type == .Static) *Self else Self; + + pub usingnamespace switch (buffer_type) { + .Static => struct { + pub fn init() Self { + return .{ + .allocator = {}, + .buf = undefined, + .head = 0, + .count = 0, + }; + } + }, + .Slice => struct { + pub fn init(buf: []T) Self { + return .{ + .allocator = {}, + .buf = buf, + .head = 0, + .count = 0, + }; + } + }, + .Dynamic => struct { + pub fn init(allocator: *Allocator) Self { + return .{ + .allocator = allocator, + .buf = &[_]T{}, + .head = 0, + .count = 0, + }; + } + }, + }; + + pub fn deinit(self: Self) void { + if (buffer_type == .Dynamic) self.allocator.free(self.buf); + } + + pub fn realign(self: *Self) void { + if (self.buf.len - self.head >= self.count) { + // this copy overlaps + mem.copy(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); + self.head = 0; + } else { + var tmp: [mem.page_size / 2 / @sizeOf(T)]T = undefined; + + while (self.head != 0) { + const n = math.min(self.head, tmp.len); + const m = self.buf.len - n; + mem.copy(T, tmp[0..n], self.buf[0..n]); + // this middle copy overlaps; the others here don't + mem.copy(T, self.buf[0..m], self.buf[n..][0..m]); + mem.copy(T, self.buf[m..], tmp[0..n]); + self.head -= n; + } + } + { // set unused area to undefined + const unused = @sliceToBytes(self.buf[self.count..]); + @memset(unused.ptr, undefined, unused.len); + } + } + + /// Reduce allocated capacity to `size`. + pub fn shrink(self: *Self, size: usize) void { + assert(size >= self.count); + if (buffer_type == .Dynamic) { + self.realign(); + self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { + error.OutOfMemory => return, // no problem, capacity is still correct then. + }; + } + } + + /// Ensure that the buffer can fit at least `size` items + pub fn ensureCapacity(self: *Self, size: usize) !void { + if (self.buf.len >= size) return; + if (buffer_type == .Dynamic) { + self.realign(); + const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; + self.buf = try self.allocator.realloc(self.buf, new_size); + } else { + return error.OutOfMemory; + } + } + + /// Makes sure at least `size` items are unused + pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { + if (self.writableLength() >= size) return; + + return try self.ensureCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); + } + + /// Returns number of items currently in fifo + pub fn readableLength(self: Self) usize { + return self.count; + } + + /// Returns a writable slice from the 'read' end of the fifo + fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { + if (offset > self.count) return &[_]T{}; + + var start = self.head + offset; + if (start >= self.buf.len) { + start -= self.buf.len; + return self.buf[start .. self.count - offset]; + } else { + const end = math.min(self.head + self.count, self.buf.len); + return self.buf[start..end]; + } + } + + /// Returns a readable slice from `offset` + pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { + return self.readableSliceMut(offset); + } + + /// Discard first `count` bytes of readable data + pub fn discard(self: *Self, count: usize) void { + assert(count <= self.count); + { // set old range to undefined. Note: may be wrapped around + const slice = self.readableSliceMut(0); + if (slice.len >= count) { + const unused = @sliceToBytes(slice[0..count]); + @memset(unused.ptr, undefined, unused.len); + } else { + const unused = @sliceToBytes(slice[0..]); + @memset(unused.ptr, undefined, unused.len); + const unused2 = @sliceToBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); + @memset(unused2.ptr, undefined, unused2.len); + } + } + if (autoalign and self.count == count) { + self.head = 0; + self.count = 0; + } else { + var head = self.head + count; + if (powers_of_two) { + head &= self.buf.len - 1; + } else { + head %= self.buf.len; + } + self.head = head; + self.count -= count; + } + } + + /// Read the next item from the fifo + pub fn readItem(self: *Self) !T { + if (self.count == 0) return error.EndOfStream; + + const c = self.buf[self.head]; + self.discard(1); + return c; + } + + /// Read data from the fifo into `dst`, returns number of bytes copied. + pub fn read(self: *Self, dst: []T) usize { + var dst_left = dst; + + while (dst_left.len > 0) { + const slice = self.readableSlice(0); + if (slice.len == 0) break; + const n = math.min(slice.len, dst_left.len); + mem.copy(T, dst_left, slice[0..n]); + self.discard(n); + dst_left = dst_left[n..]; + } + + return dst.len - dst_left.len; + } + + /// Returns number of bytes available in fifo + pub fn writableLength(self: Self) usize { + return self.buf.len - self.count; + } + + /// Returns the first section of writable buffer + /// Note that this may be of length 0 + pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { + if (offset > self.buf.len) return &[_]T{}; + + const tail = self.head + offset + self.count; + if (tail < self.buf.len) { + return self.buf[tail..]; + } else { + return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; + } + } + + /// Returns a writable buffer of at least `size` bytes, allocating memory as needed. + /// Use `fifo.update` once you've written data to it. + pub fn writeableWithSize(self: *Self, size: usize) ![]T { + try self.ensureUnusedCapacity(size); + + // try to avoid realigning buffer + var slice = self.writableSlice(0); + if (slice.len < size) { + self.realign(); + slice = self.writableSlice(0); + } + return slice; + } + + /// Update the tail location of the buffer (usually follows use of writable/writeableWithSize) + pub fn update(self: *Self, count: usize) void { + assert(self.count + count <= self.buf.len); + self.count += count; + } + + /// Appends the data in `src` to the fifo. + /// You must have ensured there is enough space. + pub fn writeAssumeCapacity(self: *Self, src: []const T) void { + assert(self.writableLength() >= src.len); + + var src_left = src; + while (src_left.len > 0) { + const writable_slice = self.writableSlice(0); + assert(writable_slice.len != 0); + const n = math.min(writable_slice.len, src_left.len); + mem.copy(T, writable_slice, src_left[0..n]); + self.update(n); + src_left = src_left[n..]; + } + } + + /// Write a single item to the fifo + pub fn writeItem(self: *Self, item: T) !void { + try self.ensureUnusedCapacity(1); + + var tail = self.head + self.count; + if (powers_of_two) { + tail &= self.buf.len - 1; + } else { + tail %= self.buf.len; + } + self.buf[tail] = byte; + self.update(1); + } + + /// Appends the data in `src` to the fifo. + /// Allocates more memory as necessary + pub fn write(self: *Self, src: []const T) !void { + try self.ensureUnusedCapacity(src.len); + + return self.writeAssumeCapacity(src); + } + + pub usingnamespace if (T == u8) + struct { + pub fn print(self: *Self, comptime format: []const u8, args: var) !void { + return std.fmt.format(self, error{OutOfMemory}, Self.write, format, args); + } + } + else + struct {}; + + /// Make `count` items available before the current read location + fn rewind(self: *Self, count: usize) void { + assert(self.writableLength() >= count); + + var head = self.head + (self.buf.len - count); + if (powers_of_two) { + head &= self.buf.len - 1; + } else { + head %= self.buf.len; + } + self.head = head; + self.count += count; + } + + /// Place data back into the read stream + pub fn unget(self: *Self, src: []const T) !void { + try self.ensureUnusedCapacity(src.len); + + self.rewind(src.len); + + const slice = self.readableSliceMut(0); + if (src.len < slice.len) { + mem.copy(T, slice, src); + } else { + mem.copy(T, slice, src[0..slice.len]); + const slice2 = self.readableSliceMut(slice.len); + mem.copy(T, slice2, src[slice.len..]); + } + } + + /// Peek at the item at `offset` + pub fn peekItem(self: Self, offset: usize) error{EndOfStream}!T { + if (offset >= self.count) + return error.EndOfStream; + + var index = self.head + offset; + if (powers_of_two) { + index &= self.buf.len - 1; + } else { + index %= self.buf.len; + } + return self.buf[index]; + } + }; +} + +test "LinearFifo(u8, .Dynamic)" { + var fifo = LinearFifo(u8, .Dynamic).init(debug.global_allocator); + defer fifo.deinit(); + + try fifo.write("HELLO"); + testing.expectEqual(@as(usize, 5), fifo.readableLength()); + testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); + + { + var i: usize = 0; + while (i < 5) : (i += 1) { + try fifo.write(&[_]u8{try fifo.peekItem(i)}); + } + testing.expectEqual(@as(usize, 10), fifo.readableLength()); + testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); + } + + { + testing.expectEqual(@as(u8, 'H'), try fifo.readItem()); + testing.expectEqual(@as(u8, 'E'), try fifo.readItem()); + testing.expectEqual(@as(u8, 'L'), try fifo.readItem()); + testing.expectEqual(@as(u8, 'L'), try fifo.readItem()); + testing.expectEqual(@as(u8, 'O'), try fifo.readItem()); + } + testing.expectEqual(@as(usize, 5), fifo.readableLength()); + + { // Writes that wrap around + testing.expectEqual(@as(usize, 11), fifo.writableLength()); + testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); + fifo.writeAssumeCapacity("6 FifoType.init(), + .Slice => FifoType.init(buf[0..]), + .Dynamic => FifoType.init(debug.global_allocator), + }; + defer fifo.deinit(); + + try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); + testing.expectEqual(@as(usize, 5), fifo.readableLength()); + + { + testing.expectEqual(@as(T, 0), try fifo.readItem()); + testing.expectEqual(@as(T, 1), try fifo.readItem()); + testing.expectEqual(@as(T, 1), try fifo.readItem()); + testing.expectEqual(@as(T, 0), try fifo.readItem()); + testing.expectEqual(@as(T, 1), try fifo.readItem()); + } + } + } +} diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 3f3e3fca7..548ef8ccc 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1,8 +1,6 @@ const std = @import("std.zig"); const math = std.math; -const debug = std.debug; -const assert = debug.assert; -const testing = std.testing; +const assert = std.debug.assert; const mem = std.mem; const builtin = @import("builtin"); const errol = @import("fmt/errol.zig"); @@ -36,7 +34,7 @@ fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, fn peekIsAlign(comptime fmt: []const u8) bool { // Should only be called during a state transition to the format segment. - std.debug.assert(fmt[0] == ':'); + comptime assert(fmt[0] == ':'); inline for (([_]u8{ 1, 2 })[0..]) |i| { if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) { @@ -49,14 +47,56 @@ fn peekIsAlign(comptime fmt: []const u8) bool { /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. +/// +/// The format string must be comptime known and may contain placeholders following +/// this format: +/// `{[position][specifier]:[fill][alignment][width].[precision]}` +/// +/// Each word between `[` and `]` is a parameter you have to replace with something: +/// +/// - *position* is the index of the argument that should be inserted +/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) +/// - *fill* is a single character which is used to pad the formatted text +/// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned +/// - *width* is the total width of the field in characters +/// - *precision* specifies how many decimals a formatted number should have +/// +/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when +/// all parameters after the separator are omitted. +/// Only exception is the *fill* parameter. If *fill* is required, one has to specify *alignment* as well, as otherwise +/// the digits after `:` is interpreted as *width*, not *fill*. +/// +/// The *specifier* has several options for types: +/// - `x` and `X`: +/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case +/// - output numeric value in hexadecimal notation +/// - `s`: print a pointer-to-many as a c-string, use zero-termination +/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values. +/// - `e`: output floating point value in scientific notation +/// - `d`: output numeric value in decimal notation +/// - `b`: output integer value in binary notation +/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. +/// - `*`: output the address of the value instead of the value itself. +/// +/// If a formatted user type contains a function of the type +/// ``` +/// fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, comptime Errors: type, output: fn (@TypeOf(context), []const u8) Errors!void) Errors!void +/// ``` +/// with `?` being the type formatted, this function will be called instead of the default implementation. +/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. +/// +/// A user type may be a `struct`, `union` or `enum` type. pub fn format( context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, comptime fmt: []const u8, - args: ..., + args: var, ) Errors!void { const ArgSetType = @IntType(false, 32); + if (@typeInfo(@TypeOf(args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); + } if (args.len > ArgSetType.bit_count) { @compileError("32 arguments max are supported per format call"); } @@ -128,6 +168,10 @@ pub fn format( '}' => { const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); + if (arg_to_print >= args.len) { + @compileError("Too few arguments"); + } + try formatType( args[arg_to_print], fmt[0..0], @@ -279,17 +323,17 @@ pub fn formatType( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, max_depth: usize, ) Errors!void { if (comptime std.mem.eql(u8, fmt, "*")) { - try output(context, @typeName(@typeOf(value).Child)); + try output(context, @typeName(@TypeOf(value).Child)); try output(context, "@"); try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output); return; } - const T = @typeOf(value); + const T = @TypeOf(value); switch (@typeInfo(T)) { .ComptimeInt, .Int, .Float => { return formatValue(value, fmt, options, context, Errors, output); @@ -339,16 +383,16 @@ pub fn formatType( const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { try output(context, "{ ."); - try output(context, @tagName(UnionTagType(value))); + try output(context, @tagName(@as(UnionTagType, value))); try output(context, " = "); inline for (info.fields) |u_field| { - if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { + if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { try formatType(@field(value, u_field.name), "", options, context, Errors, output, max_depth - 1); } } try output(context, " }"); } else { - try format(context, Errors, output, "@{x}", @ptrToInt(&value)); + try format(context, Errors, output, "@{x}", .{@ptrToInt(&value)}); } }, .Struct => { @@ -380,12 +424,12 @@ pub fn formatType( if (info.child == u8) { return formatText(value, fmt, options, context, Errors, output); } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { return formatType(value.*, fmt, options, context, Errors, output, max_depth); }, - else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), + else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, .Many => { if (ptr_info.child == u8) { @@ -394,7 +438,7 @@ pub fn formatType( return formatText(value[0..len], fmt, options, context, Errors, output); } } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, .Slice => { if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { @@ -403,24 +447,30 @@ pub fn formatType( if (ptr_info.child == u8) { return formatText(value, fmt, options, context, Errors, output); } - return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); + return format(context, Errors, output, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); }, .C => { - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, }, .Array => |info| { - if (info.child == u8) { - return formatText(value, fmt, options, context, Errors, output); - } - if (value.len == 0) { - return format(context, Errors, output, "[0]{}", @typeName(T.Child)); - } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); + const Slice = @Type(builtin.TypeInfo{ + .Pointer = .{ + .size = .Slice, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .alignment = @alignOf(info.child), + .child = info.child, + .sentinel = null, + }, + }); + return formatType(@as(Slice, &value), fmt, options, context, Errors, output, max_depth); }, .Fn => { - return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); + return format(context, Errors, output, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); }, + .Type => return output(context, @typeName(T)), else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } @@ -431,7 +481,7 @@ fn formatValue( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (comptime std.mem.eql(u8, fmt, "B")) { return formatBytes(value, options, 1000, context, Errors, output); @@ -439,7 +489,7 @@ fn formatValue( return formatBytes(value, options, 1024, context, Errors, output); } - const T = @typeOf(value); + const T = @TypeOf(value); switch (@typeId(T)) { .Float => return formatFloatValue(value, fmt, options, context, Errors, output), .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), @@ -453,14 +503,14 @@ pub fn formatIntValue( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { comptime var radix = 10; comptime var uppercase = false; - const int_value = if (@typeOf(value) == comptime_int) blk: { + const int_value = if (@TypeOf(value) == comptime_int) blk: { const Int = math.IntFittingRange(value, value); - break :blk Int(value); + break :blk @as(Int, value); } else value; @@ -468,8 +518,8 @@ pub fn formatIntValue( radix = 10; uppercase = false; } else if (comptime std.mem.eql(u8, fmt, "c")) { - if (@typeOf(int_value).bit_count <= 8) { - return formatAsciiChar(u8(int_value), options, context, Errors, output); + if (@TypeOf(int_value).bit_count <= 8) { + return formatAsciiChar(@as(u8, int_value), options, context, Errors, output); } else { @compileError("Cannot print integer that is larger than 8 bits as a ascii"); } @@ -495,7 +545,7 @@ fn formatFloatValue( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { return formatFloatScientific(value, options, context, Errors, output); @@ -512,7 +562,7 @@ pub fn formatText( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (fmt.len == 0) { return output(context, bytes); @@ -533,9 +583,9 @@ pub fn formatAsciiChar( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { - return output(context, (*const [1]u8)(&c)[0..]); + return output(context, @as(*const [1]u8, &c)[0..]); } pub fn formatBuf( @@ -543,7 +593,7 @@ pub fn formatBuf( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { try output(context, buf); @@ -551,7 +601,7 @@ pub fn formatBuf( var leftover_padding = if (width > buf.len) (width - buf.len) else return; const pad_byte: u8 = options.fill; while (leftover_padding > 0) : (leftover_padding -= 1) { - try output(context, (*const [1]u8)(&pad_byte)[0..1]); + try output(context, @as(*const [1]u8, &pad_byte)[0..1]); } } @@ -563,7 +613,7 @@ pub fn formatFloatScientific( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { var x = @floatCast(f64, value); @@ -625,7 +675,7 @@ pub fn formatFloatScientific( try output(context, float_decimal.digits[0..1]); try output(context, "."); if (float_decimal.digits.len > 1) { - const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; + const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len; try output(context, float_decimal.digits[1..num_digits]); } else { @@ -658,9 +708,9 @@ pub fn formatFloatDecimal( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { - var x = f64(value); + var x = @as(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { @@ -804,7 +854,7 @@ pub fn formatBytes( comptime radix: usize, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (value == 0) { return output(context, "0B"); @@ -831,8 +881,8 @@ pub fn formatBytes( } const buf = switch (radix) { - 1000 => [_]u8{ suffix, 'B' }, - 1024 => [_]u8{ suffix, 'i', 'B' }, + 1000 => &[_]u8{ suffix, 'B' }, + 1024 => &[_]u8{ suffix, 'i', 'B' }, else => unreachable, }; return output(context, buf); @@ -845,15 +895,15 @@ pub fn formatInt( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { - const int_value = if (@typeOf(value) == comptime_int) blk: { + const int_value = if (@TypeOf(value) == comptime_int) blk: { const Int = math.IntFittingRange(value, value); - break :blk Int(value); + break :blk @as(Int, value); } else value; - if (@typeOf(int_value).is_signed) { + if (@TypeOf(int_value).is_signed) { return formatIntSigned(int_value, base, uppercase, options, context, Errors, output); } else { return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output); @@ -867,7 +917,7 @@ fn formatIntSigned( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { const new_options = FormatOptions{ .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, @@ -875,17 +925,17 @@ fn formatIntSigned( .fill = options.fill, }; - const uint = @IntType(false, @typeOf(value).bit_count); + const uint = @IntType(false, @TypeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; - try output(context, (*const [1]u8)(&minus_sign)[0..]); + try output(context, @as(*const [1]u8, &minus_sign)[0..]); const new_value = @intCast(uint, -(value + 1)) + 1; return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); } else if (options.width == null or options.width.? == 0) { return formatIntUnsigned(@intCast(uint, value), base, uppercase, options, context, Errors, output); } else { const plus_sign: u8 = '+'; - try output(context, (*const [1]u8)(&plus_sign)[0..]); + try output(context, @as(*const [1]u8, &plus_sign)[0..]); const new_value = @intCast(uint, value); return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); } @@ -898,12 +948,12 @@ fn formatIntUnsigned( options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { assert(base >= 2); - var buf: [math.max(@typeOf(value).bit_count, 1)]u8 = undefined; - const min_int_bits = comptime math.max(@typeOf(value).bit_count, @typeOf(base).bit_count); - const MinInt = @IntType(@typeOf(value).is_signed, min_int_bits); + var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined; + const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count); + const MinInt = @IntType(@TypeOf(value).is_signed, min_int_bits); var a: MinInt = value; var index: usize = buf.len; @@ -923,12 +973,12 @@ fn formatIntUnsigned( const zero_byte: u8 = options.fill; var leftover_padding = padding - index; while (true) { - try output(context, (*const [1]u8)(&zero_byte)[0..]); + try output(context, @as(*const [1]u8, &zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], options.fill); - return output(context, buf); + return output(context, &buf); } else { const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], options.fill); @@ -955,7 +1005,7 @@ fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { if (!T.is_signed) return parseUnsigned(T, buf, radix); - if (buf.len == 0) return T(0); + if (buf.len == 0) return @as(T, 0); if (buf[0] == '-') { return math.negate(try parseUnsigned(T, buf[1..], radix)); } else if (buf[0] == '+') { @@ -966,16 +1016,16 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { } test "parseInt" { - testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); - testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); - testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); - testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); + std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); + std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); + std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); + std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); } -const ParseUnsignedError = error{ +pub const ParseUnsignedError = error{ /// The result cannot fit in the type specified Overflow, @@ -997,30 +1047,30 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned } test "parseUnsigned" { - testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); - testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); - testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); + std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); + std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); + std.testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); - testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); - testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); + std.testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); + std.testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); - testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); + std.testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); - testing.expect((try parseUnsigned(u7, "1", 10)) == 1); - testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); + std.testing.expect((try parseUnsigned(u7, "1", 10)) == 1); + std.testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); - testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); - testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); + std.testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); + std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); - testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); + std.testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); // these numbers should fit even though the radix itself doesn't fit in the destination type - testing.expect((try parseUnsigned(u1, "0", 10)) == 0); - testing.expect((try parseUnsigned(u1, "1", 10)) == 1); - testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); - testing.expect((try parseUnsigned(u1, "001", 16)) == 1); - testing.expect((try parseUnsigned(u2, "3", 16)) == 3); - testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); + std.testing.expect((try parseUnsigned(u1, "0", 10)) == 0); + std.testing.expect((try parseUnsigned(u1, "1", 10)) == 1); + std.testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); + std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); + std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); + std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); } pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; @@ -1045,7 +1095,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { fn digitToChar(digit: u8, uppercase: bool) u8 { return switch (digit) { 0...9 => digit + '0', - 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), + 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10), else => unreachable, }; } @@ -1055,20 +1105,27 @@ const BufPrintContext = struct { }; fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { - if (context.remaining.len < bytes.len) return error.BufferTooSmall; + if (context.remaining.len < bytes.len) { + mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); + return error.BufferTooSmall; + } mem.copy(u8, context.remaining, bytes); context.remaining = context.remaining[bytes.len..]; } -pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { +pub const BufPrintError = error{ + /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. + BufferTooSmall, +}; +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { var context = BufPrintContext{ .remaining = buf }; - try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); + try format(&context, BufPrintError, bufPrintWrite, fmt, args); return buf[0 .. buf.len - context.remaining.len]; } pub const AllocPrintError = error{OutOfMemory}; -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { var size: usize = 0; format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); @@ -1084,19 +1141,19 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { test "bufPrintInt" { var buffer: [100]u8 = undefined; const buf = buffer[0..]; - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, FormatOptions{}), "-101111000110000101001110")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, FormatOptions{}), "-12345678")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, FormatOptions{}), "-bc614e")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, FormatOptions{}), "-BC614E")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, -12345678), 2, false, FormatOptions{}), "-101111000110000101001110")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, -12345678), 10, false, FormatOptions{}), "-12345678")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, -12345678), 16, false, FormatOptions{}), "-bc614e")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, -12345678), 16, true, FormatOptions{}), "-BC614E")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, FormatOptions{}), "12345678")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(u32, 12345678), 10, true, FormatOptions{}), "12345678")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, FormatOptions{ .width = 6 }), " 666")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 1 }), "1234")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(u32, 666), 10, false, FormatOptions{ .width = 6 }), " 666")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 1 }), "1234")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, FormatOptions{ .width = 3 }), "+42")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, FormatOptions{ .width = 3 }), "-42")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, 42), 10, false, FormatOptions{ .width = 3 }), "+42")); + std.testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 }), "-42")); } fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { @@ -1113,53 +1170,53 @@ test "parse u64 digit too big" { test "parse unsigned comptime" { comptime { - testing.expect((try parseUnsigned(usize, "2", 10)) == 2); + std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); } } test "optional" { { const value: ?i32 = 1234; - try testFmt("optional: 1234\n", "optional: {}\n", value); + try testFmt("optional: 1234\n", "optional: {}\n", .{value}); } { const value: ?i32 = null; - try testFmt("optional: null\n", "optional: {}\n", value); + try testFmt("optional: null\n", "optional: {}\n", .{value}); } } test "error" { { const value: anyerror!i32 = 1234; - try testFmt("error union: 1234\n", "error union: {}\n", value); + try testFmt("error union: 1234\n", "error union: {}\n", .{value}); } { const value: anyerror!i32 = error.InvalidChar; - try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); + try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); } } test "int.small" { { const value: u3 = 0b101; - try testFmt("u3: 5\n", "u3: {}\n", value); + try testFmt("u3: 5\n", "u3: {}\n", .{value}); } } test "int.specifier" { { const value: u8 = 'a'; - try testFmt("u8: a\n", "u8: {c}\n", value); + try testFmt("u8: a\n", "u8: {c}\n", .{value}); } { const value: u8 = 0b1100; - try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); } } test "int.padded" { - try testFmt("u8: ' 1'", "u8: '{:4}'", u8(1)); - try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", u8(1)); + try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); + try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); } test "buffer" { @@ -1168,31 +1225,31 @@ test "buffer" { var context = BufPrintContext{ .remaining = buf1[0..] }; try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); var res = buf1[0 .. buf1.len - context.remaining.len]; - testing.expect(mem.eql(u8, res, "1234")); + std.testing.expect(mem.eql(u8, res, "1234")); context = BufPrintContext{ .remaining = buf1[0..] }; try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); res = buf1[0 .. buf1.len - context.remaining.len]; - testing.expect(mem.eql(u8, res, "a")); + std.testing.expect(mem.eql(u8, res, "a")); context = BufPrintContext{ .remaining = buf1[0..] }; try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); res = buf1[0 .. buf1.len - context.remaining.len]; - testing.expect(mem.eql(u8, res, "1100")); + std.testing.expect(mem.eql(u8, res, "1100")); } } test "array" { { - const value: [3]u8 = "abc"; - try testFmt("array: abc\n", "array: {}\n", value); - try testFmt("array: abc\n", "array: {}\n", &value); + const value: [3]u8 = "abc".*; + try testFmt("array: abc\n", "array: {}\n", .{value}); + try testFmt("array: abc\n", "array: {}\n", .{&value}); var buf: [100]u8 = undefined; try testFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), "array: {*}\n", - &value, + .{&value}, ); } } @@ -1200,45 +1257,41 @@ test "array" { test "slice" { { const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", value); + try testFmt("slice: abc\n", "slice: {}\n", .{value}); } { - const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value); + const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; + try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); } - try testFmt("buf: Test \n", "buf: {s:5}\n", "Test"); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); + try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); + try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); } test "pointer" { { - const value = @intToPtr(*i32, 0xdeadbeef); - try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); - try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); + const value = @intToPtr(*align(1) i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); } { const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } { const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } } test "cstr" { - try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); - try testFmt("cstr: Test C \n", "cstr: {s:10}\n", c"Test C"); + try testFmt("cstr: Test C\n", "cstr: {s}\n", .{"Test C"}); + try testFmt("cstr: Test C \n", "cstr: {s:10}\n", .{"Test C"}); } test "filesize" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); - try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", usize(63 * 1024 * 1024)); + try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); + try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); } test "struct" { @@ -1247,8 +1300,8 @@ test "struct" { field: u8, }; const value = Struct{ .field = 42 }; - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value); - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value); + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); } { const Struct = struct { @@ -1256,7 +1309,7 @@ test "struct" { b: u1, }; const value = Struct{ .a = 0, .b = 1 }; - try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); + try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); } } @@ -1266,92 +1319,72 @@ test "enum" { Two, }; const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", value); - try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); } test "float.scientific" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("f32: 1.34000003e+00", "f32: {e}", f32(1.34)); - try testFmt("f32: 1.23400001e+01", "f32: {e}", f32(12.34)); - try testFmt("f64: -1.234e+11", "f64: {e}", f64(-12.34e10)); - try testFmt("f64: 9.99996e-40", "f64: {e}", f64(9.999960e-40)); + try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); + try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); + try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); + try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); } test "float.scientific.precision" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("f64: 1.40971e-42", "f64: {e:.5}", f64(1.409706e-42)); - try testFmt("f64: 1.00000e-09", "f64: {e:.5}", f64(@bitCast(f32, u32(814313563)))); - try testFmt("f64: 7.81250e-03", "f64: {e:.5}", f64(@bitCast(f32, u32(1006632960)))); + try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); + try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); + try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - try testFmt("f64: 1.00001e+05", "f64: {e:.5}", f64(@bitCast(f32, u32(1203982400)))); + try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); } test "float.special" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("f64: nan", "f64: {}", math.nan_f64); + try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan if (builtin.arch != builtin.Arch.arm) { - try testFmt("f64: -nan", "f64: {}", -math.nan_f64); + try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); } - try testFmt("f64: inf", "f64: {}", math.inf_f64); - try testFmt("f64: -inf", "f64: {}", -math.inf_f64); + try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); + try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); } test "float.decimal" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("f64: 152314000000000000000000000000", "f64: {d}", f64(1.52314e+29)); - try testFmt("f32: 1.1", "f32: {d:.1}", f32(1.1234)); - try testFmt("f32: 1234.57", "f32: {d:.2}", f32(1234.567)); + try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); + try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); + try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). // -11.12339... is rounded back up to -11.1234 - try testFmt("f32: -11.1234", "f32: {d:.4}", f32(-11.1234)); - try testFmt("f32: 91.12345", "f32: {d:.5}", f32(91.12345)); - try testFmt("f64: 91.1234567890", "f64: {d:.10}", f64(91.12345678901235)); - try testFmt("f64: 0.00000", "f64: {d:.5}", f64(0.0)); - try testFmt("f64: 6", "f64: {d:.0}", f64(5.700)); - try testFmt("f64: 10.0", "f64: {d:.1}", f64(9.999)); - try testFmt("f64: 1.000", "f64: {d:.3}", f64(1.0)); - try testFmt("f64: 0.00030000", "f64: {d:.8}", f64(0.0003)); - try testFmt("f64: 0.00000", "f64: {d:.5}", f64(1.40130e-45)); - try testFmt("f64: 0.00000", "f64: {d:.5}", f64(9.999960e-40)); + try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); + try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); + try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); + try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); + try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); + try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); + try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); } test "float.libc.sanity" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(916964781)))); - try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(925353389)))); - try testFmt("f64: 0.10000", "f64: {d:.5}", f64(@bitCast(f32, u32(1036831278)))); - try testFmt("f64: 1.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1065353133)))); - try testFmt("f64: 10.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1092616192)))); + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); + try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); + try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); + try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); // libc differences // // This is 0.015625 exactly according to gdb. We thus round down, // however glibc rounds up for some reason. This occurs for all // floats of the form x.yyyy25 on a precision point. - try testFmt("f64: 0.01563", "f64: {d:.5}", f64(@bitCast(f32, u32(1015021568)))); + try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 // also rounds to 630 so I'm inclined to believe libc is not // optimal here. - try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1518338049)))); + try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); } test "custom" { @@ -1366,12 +1399,12 @@ test "custom" { options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y); + return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", self.x, self.y); + return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", .{ self.x, self.y }); } else { @compileError("Unknown format character: '" ++ fmt ++ "'"); } @@ -1383,12 +1416,12 @@ test "custom" { .x = 10.2, .y = 2.22, }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); } test "struct" { @@ -1402,7 +1435,7 @@ test "struct" { .b = error.Unused, }; - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); + try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); } test "union" { @@ -1425,14 +1458,14 @@ test "union" { const uu_inst = UU{ .int = 456 }; const eu_inst = EU{ .float = 321.123 }; - try testFmt("TU{ .int = 123 }", "{}", tu_inst); + try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", uu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); - const eu_result = try bufPrint(buf[0..], "{}", eu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); + const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); } test "enum" { @@ -1444,7 +1477,7 @@ test "enum" { const inst = E.Two; - try testFmt("E.Two", "{}", inst); + try testFmt("E.Two", "{}", .{inst}); } test "struct.self-referential" { @@ -1458,7 +1491,7 @@ test "struct.self-referential" { }; inst.a = &inst; - try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); + try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); } test "struct.zero-size" { @@ -1473,30 +1506,30 @@ test "struct.zero-size" { const a = A{}; const b = B{ .a = a, .c = 0 }; - try testFmt("B{ .a = A{ }, .c = 0 }", "{}", b); + try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); } test "bytes.hex" { const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); + try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); + try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); + try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); + try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); + try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); } -fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { +fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { var buf: [100]u8 = undefined; const result = try bufPrint(buf[0..], template, args); if (mem.eql(u8, result, expected)) return; - std.debug.warn("\n====== expected this output: =========\n"); - std.debug.warn("{}", expected); - std.debug.warn("\n======== instead found this: =========\n"); - std.debug.warn("{}", result); - std.debug.warn("\n======================================\n"); + std.debug.warn("\n====== expected this output: =========\n", .{}); + std.debug.warn("{}", .{expected}); + std.debug.warn("\n======== instead found this: =========\n", .{}); + std.debug.warn("{}", .{result}); + std.debug.warn("\n======================================\n", .{}); return error.TestFailed; } @@ -1519,11 +1552,11 @@ pub fn trim(buf: []const u8) []const u8 { } test "trim" { - testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); - testing.expect(mem.eql(u8, "", trim(" "))); - testing.expect(mem.eql(u8, "", trim(""))); - testing.expect(mem.eql(u8, "abc", trim(" abc"))); - testing.expect(mem.eql(u8, "abc", trim("abc "))); + std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); + std.testing.expect(mem.eql(u8, "", trim(" "))); + std.testing.expect(mem.eql(u8, "", trim(""))); + std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); + std.testing.expect(mem.eql(u8, "abc", trim("abc "))); } pub fn isWhiteSpace(byte: u8) bool { @@ -1549,15 +1582,15 @@ test "hexToBytes" { const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; var pb: [32]u8 = undefined; try hexToBytes(pb[0..], test_hex_str); - try testFmt(test_hex_str, "{X}", pb); + try testFmt(test_hex_str, "{X}", .{pb}); } test "formatIntValue with comptime_int" { const value: comptime_int = 123456789123456789; var buf = try std.Buffer.init(std.debug.global_allocator, ""); - try formatIntValue(value, "", FormatOptions{}, &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append); - assert(mem.eql(u8, buf.toSlice(), "123456789123456789")); + try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append); + std.testing.expect(mem.eql(u8, buf.toSlice(), "123456789123456789")); } test "formatType max_depth" { @@ -1572,10 +1605,10 @@ test "formatType max_depth" { options: FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { if (fmt.len == 0) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y); + return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } @@ -1610,34 +1643,34 @@ test "formatType max_depth" { inst.tu.ptr = &inst.tu; var buf0 = try std.Buffer.init(std.debug.global_allocator, ""); - try formatType(inst, "", FormatOptions{}, &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0); - assert(mem.eql(u8, buf0.toSlice(), "S{ ... }")); + try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0); + std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); var buf1 = try std.Buffer.init(std.debug.global_allocator, ""); - try formatType(inst, "", FormatOptions{}, &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1); - assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1); + std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); var buf2 = try std.Buffer.init(std.debug.global_allocator, ""); - try formatType(inst, "", FormatOptions{}, &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2); - assert(mem.eql(u8, buf2.toSlice(), "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) }")); + try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2); + std.testing.expect(mem.eql(u8, buf2.toSlice(), "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) }")); var buf3 = try std.Buffer.init(std.debug.global_allocator, ""); - try formatType(inst, "", FormatOptions{}, &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3); - assert(mem.eql(u8, buf3.toSlice(), "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) }")); + try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3); + std.testing.expect(mem.eql(u8, buf3.toSlice(), "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) }")); } test "positional" { - try testFmt("2 1 0", "{2} {1} {0}", usize(0), usize(1), usize(2)); - try testFmt("2 1 0", "{2} {1} {}", usize(0), usize(1), usize(2)); - try testFmt("0 0", "{0} {0}", usize(0)); - try testFmt("0 1", "{} {1}", usize(0), usize(1)); - try testFmt("1 0 0 1", "{1} {} {0} {}", usize(0), usize(1)); + try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); + try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); + try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); } test "positional with specifier" { - try testFmt("10.0", "{0d:.1}", f64(9.999)); + try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); } test "positional/alignment/width/precision" { - try testFmt("10.0", "{0d: >3.1}", f64(9.999)); + try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); } diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig index a835195fc..e697b7d42 100644 --- a/lib/std/fmt/errol.zig +++ b/lib/std/fmt/errol.zig @@ -296,7 +296,7 @@ fn hpMul10(hp: *HP) void { /// @buf: The output buffer. /// &return: The exponent. fn errolInt(val: f64, buffer: []u8) FloatDecimal { - const pow19 = u128(1e19); + const pow19 = @as(u128, 1e19); assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); @@ -670,7 +670,7 @@ fn fpeint(from: f64) u128 { const bits = @bitCast(u64, from); assert((bits & ((1 << 52) - 1)) == 0); - return u128(1) << @truncate(u7, (bits >> 52) -% 1023); + return @as(u128, 1) << @truncate(u7, (bits >> 52) -% 1023); } /// Given two different integers with the same length in terms of the number diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 9a35e27c2..2c480852a 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -59,29 +59,29 @@ const Z96 = struct { // d += s inline fn add(d: *Z96, s: Z96) void { - var w = u64(d.d0) + u64(s.d0); + var w = @as(u64, d.d0) + @as(u64, s.d0); d.d0 = @truncate(u32, w); w >>= 32; - w += u64(d.d1) + u64(s.d1); + w += @as(u64, d.d1) + @as(u64, s.d1); d.d1 = @truncate(u32, w); w >>= 32; - w += u64(d.d2) + u64(s.d2); + w += @as(u64, d.d2) + @as(u64, s.d2); d.d2 = @truncate(u32, w); } // d -= s inline fn sub(d: *Z96, s: Z96) void { - var w = u64(d.d0) -% u64(s.d0); + var w = @as(u64, d.d0) -% @as(u64, s.d0); d.d0 = @truncate(u32, w); w >>= 32; - w += u64(d.d1) -% u64(s.d1); + w += @as(u64, d.d1) -% @as(u64, s.d1); d.d1 = @truncate(u32, w); w >>= 32; - w += u64(d.d2) -% u64(s.d2); + w += @as(u64, d.d2) -% @as(u64, s.d2); d.d2 = @truncate(u32, w); } }; @@ -160,7 +160,7 @@ fn convertRepr(comptime T: type, n: FloatRepr) T { break :blk if (n.negative) f64_minus_zero else f64_plus_zero; } else if (s.d2 != 0) { const binexs2 = @intCast(u64, binary_exponent) << 52; - const rr = (u64(s.d2 & ~mask28) << 24) | ((u64(s.d1) + 128) >> 8) | binexs2; + const rr = (@as(u64, s.d2 & ~mask28) << 24) | ((@as(u64, s.d1) + 128) >> 8) | binexs2; break :blk if (n.negative) rr | (1 << 63) else rr; } else { break :blk 0; @@ -375,17 +375,13 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T { return switch (try parseRepr(s, &r)) { ParseResult.Ok => convertRepr(T, r), ParseResult.PlusZero => 0.0, - ParseResult.MinusZero => -T(0.0), + ParseResult.MinusZero => -@as(T, 0.0), ParseResult.PlusInf => std.math.inf(T), ParseResult.MinusInf => -std.math.inf(T), }; } test "fmt.parseFloat" { - if (@import("builtin").arch == .arm) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const testing = std.testing; const expect = testing.expect; const expectEqual = testing.expectEqual; @@ -426,8 +422,8 @@ test "fmt.parseFloat" { expect(approxEq(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); - expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); - expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), T(0.7062146892655368), epsilon)); + expect(approxEq(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon)); + expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon)); } } } diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 6301c8a26..540d5f1a1 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -6,14 +6,13 @@ const base64 = std.base64; const crypto = std.crypto; const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const math = std.math; pub const path = @import("fs/path.zig"); pub const File = @import("fs/file.zig").File; pub const symLink = os.symlink; pub const symLinkC = os.symlinkC; -pub const deleteFile = os.unlink; -pub const deleteFileC = os.unlinkC; pub const rename = os.rename; pub const renameC = os.renameC; pub const renameW = os.renameW; @@ -27,9 +26,9 @@ pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirE /// This represents the maximum size of a UTF-8 encoded file path. /// All file system operations which return a path are guaranteed to /// fit into a UTF-8 encoded array of this length. -/// path being too long if it is this 0long +/// The byte count includes room for a null sentinel byte. pub const MAX_PATH_BYTES = switch (builtin.os) { - .linux, .macosx, .ios, .freebsd, .netbsd => os.PATH_MAX, + .linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX, // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. // If it would require 4 UTF-8 bytes, then there would be a surrogate // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. @@ -38,8 +37,11 @@ pub const MAX_PATH_BYTES = switch (builtin.os) { else => @compileError("Unsupported OS"), }; -// here we replace the standard +/ with -_ so that it can be used in a file name -const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); +/// Base64, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. +pub const base64_encoder = base64.Base64Encoder.init( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + base64.standard_pad_char, +); /// TODO remove the allocator requirement from this API pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { @@ -59,7 +61,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: tmp_path[dirname.len] = path.sep; while (true) { try crypto.randomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); if (symLink(existing_path, tmp_path)) { return rename(tmp_path, new_path); @@ -87,13 +89,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { /// If any of the directories do not exist for dest_path, they are created. /// TODO https://github.com/ziglang/zig/issues/2885 pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { - var src_file = try File.openRead(source_path); + const my_cwd = cwd(); + + var src_file = try my_cwd.openFile(source_path, .{}); defer src_file.close(); const src_stat = try src_file.stat(); check_dest_stat: { const dest_stat = blk: { - var dest_file = File.openRead(dest_path) catch |err| switch (err) { + var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) { error.FileNotFound => break :check_dest_stat, else => |e| return e, }; @@ -156,7 +160,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil /// in the same directory as dest_path. /// Destination file will have the same mode as the source file. pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { - var in_file = try File.openRead(source_path); + var in_file = try cwd().openFile(source_path, .{}); defer in_file.close(); const mode = try in_file.mode(); @@ -179,7 +183,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { /// merged and readily available, /// there is a possibility of power loss or application termination leaving temporary files present pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { - var in_file = try File.openRead(source_path); + var in_file = try cwd().openFile(source_path, .{}); defer in_file.close(); var atomic_file = try AtomicFile.init(dest_path, mode); @@ -205,8 +209,6 @@ pub const AtomicFile = struct { /// dest_path must remain valid for the lifetime of AtomicFile /// call finish to atomically replace dest_path with contents - /// TODO once we have null terminated pointers, use the - /// openWriteNoClobberN function pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { const dirname = path.dirname(dest_path); var rand_buf: [12]u8 = undefined; @@ -222,16 +224,20 @@ pub const AtomicFile = struct { } tmp_path_buf[tmp_path_len] = 0; + const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0]; + + const my_cwd = cwd(); while (true) { try crypto.randomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); + base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); - const file = File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { + const file = my_cwd.createFileC( + tmp_path_slice, + .{ .mode = mode, .exclusive = true }, + ) catch |err| switch (err) { error.PathAlreadyExists => continue, - // TODO zig should figure out that this error set does not include PathAlreadyExists since - // it is handled in the above switch - else => return err, + else => |e| return e, }; return AtomicFile{ @@ -247,7 +253,7 @@ pub const AtomicFile = struct { pub fn deinit(self: *AtomicFile) void { if (!self.finished) { self.file.close(); - deleteFileC(&self.tmp_path_buf) catch {}; + cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; self.finished = true; } } @@ -256,13 +262,13 @@ pub const AtomicFile = struct { assert(!self.finished); self.file.close(); self.finished = true; - if (os.windows.is_the_target) { + if (builtin.os == .windows) { const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); - const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf); + const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); return os.renameW(&tmp_path_w, &dest_path_w); } const dest_path_c = try os.toPosixPath(self.dest_path); - return os.renameC(&self.tmp_path_buf, &dest_path_c); + return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c); } }; @@ -274,12 +280,12 @@ pub fn makeDir(dir_path: []const u8) !void { } /// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string. -pub fn makeDirC(dir_path: [*]const u8) !void { +pub fn makeDirC(dir_path: [*:0]const u8) !void { return os.mkdirC(dir_path, default_new_dir_mode); } /// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string. -pub fn makeDirW(dir_path: [*]const u16) !void { +pub fn makeDirW(dir_path: [*:0]const u16) !void { return os.mkdirW(dir_path, default_new_dir_mode); } @@ -289,7 +295,7 @@ pub fn makeDirW(dir_path: [*]const u16) !void { /// have been modified regardless. /// TODO determine if we can remove the allocator requirement from this function pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { - const resolved_path = try path.resolve(allocator, [_][]const u8{full_path}); + const resolved_path = try path.resolve(allocator, &[_][]const u8{full_path}); defer allocator.free(resolved_path); var end_index: usize = resolved_path.len; @@ -327,149 +333,39 @@ pub fn deleteDir(dir_path: []const u8) !void { } /// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string. -pub fn deleteDirC(dir_path: [*]const u8) !void { +pub fn deleteDirC(dir_path: [*:0]const u8) !void { return os.rmdirC(dir_path); } /// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string. -pub fn deleteDirW(dir_path: [*]const u16) !void { +pub fn deleteDirW(dir_path: [*:0]const u16) !void { return os.rmdirW(dir_path); } -const DeleteTreeError = error{ - OutOfMemory, - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - PathAlreadyExists, - ReadOnlyFileSystem, - NotDir, - FileNotFound, - FileSystem, - FileBusy, - DirNotEmpty, - DeviceBusy, +/// Removes a symlink, file, or directory. +/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the +/// current working directory as the open directory handle. +/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the +/// base directory. +pub fn deleteTree(full_path: []const u8) !void { + if (path.isAbsolute(full_path)) { + const dirname = path.dirname(full_path) orelse return error{ + /// Attempt to remove the root file system path. + /// This error is unreachable if `full_path` is relative. + CannotDeleteRootDirectory, + }.CannotDeleteRootDirectory; - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, + var dir = try cwd().openDirList(dirname); + defer dir.close(); - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - Unexpected, -}; - -/// Whether `full_path` describes a symlink, file, or directory, this function -/// removes it. If it cannot be removed because it is a non-empty directory, -/// this function recursively removes its entries and then tries again. -/// TODO determine if we can remove the allocator requirement -/// https://github.com/ziglang/zig/issues/2886 -pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { - start_over: while (true) { - var got_access_denied = false; - // First, try deleting the item as a file. This way we don't follow sym links. - if (deleteFile(full_path)) { - return; - } else |err| switch (err) { - error.FileNotFound => return, - error.IsDir => {}, - error.AccessDenied => got_access_denied = true, - - error.InvalidUtf8, - error.SymLinkLoop, - error.NameTooLong, - error.SystemResources, - error.ReadOnlyFileSystem, - error.NotDir, - error.FileSystem, - error.FileBusy, - error.BadPathName, - error.Unexpected, - => return err, - } - { - var dir = Dir.open(allocator, full_path) catch |err| switch (err) { - error.NotDir => { - if (got_access_denied) { - return error.AccessDenied; - } - continue :start_over; - }, - - error.OutOfMemory, - error.AccessDenied, - error.FileTooBig, - error.IsDir, - error.SymLinkLoop, - error.ProcessFdQuotaExceeded, - error.NameTooLong, - error.SystemFdQuotaExceeded, - error.NoDevice, - error.FileNotFound, - error.SystemResources, - error.NoSpaceLeft, - error.PathAlreadyExists, - error.Unexpected, - error.InvalidUtf8, - error.BadPathName, - error.DeviceBusy, - => return err, - }; - defer dir.close(); - - var full_entry_buf = std.ArrayList(u8).init(allocator); - defer full_entry_buf.deinit(); - - while (try dir.next()) |entry| { - try full_entry_buf.resize(full_path.len + entry.name.len + 1); - const full_entry_path = full_entry_buf.toSlice(); - mem.copy(u8, full_entry_path, full_path); - full_entry_path[full_path.len] = path.sep; - mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); - - try deleteTree(allocator, full_entry_path); - } - } - return deleteDir(full_path); + return dir.deleteTree(path.basename(full_path)); + } else { + return cwd().deleteTree(full_path); } } -/// TODO: separate this API into the one that opens directory handles to then subsequently open -/// files, and into the one that reads files from an open directory handle. pub const Dir = struct { - handle: Handle, - allocator: *Allocator, - - pub const Handle = switch (builtin.os) { - .macosx, .ios, .freebsd, .netbsd => struct { - fd: i32, - seek: i64, - buf: []u8, - index: usize, - end_index: usize, - }, - .linux => struct { - fd: i32, - buf: []u8, - index: usize, - end_index: usize, - }, - .windows => struct { - handle: os.windows.HANDLE, - find_file_data: os.windows.WIN32_FIND_DATAW, - first: bool, - name_data: [256]u8, - }, - else => @compileError("unimplemented"), - }; + fd: os.fd_t, pub const Entry = struct { name: []const u8, @@ -488,308 +384,1014 @@ pub const Dir = struct { }; }; + const IteratorError = error{AccessDenied} || os.UnexpectedError; + + pub const Iterator = switch (builtin.os) { + .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct { + dir: Dir, + seek: i64, + buf: [8192]u8, // TODO align(@alignOf(os.dirent)), + index: usize, + end_index: usize, + + const Self = @This(); + + pub const Error = IteratorError; + + /// Memory such as file names referenced in this returned entry becomes invalid + /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. + pub fn next(self: *Self) Error!?Entry { + switch (builtin.os) { + .macosx, .ios => return self.nextDarwin(), + .freebsd, .netbsd, .dragonfly => return self.nextBsd(), + else => @compileError("unimplemented"), + } + } + + fn nextDarwin(self: *Self) !?Entry { + start_over: while (true) { + if (self.index >= self.end_index) { + const rc = os.system.__getdirentries64( + self.dir.fd, + &self.buf, + self.buf.len, + &self.seek, + ); + if (rc == 0) return null; + if (rc < 0) { + switch (os.errno(rc)) { + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => unreachable, + else => |err| return os.unexpectedErrno(err), + } + } + self.index = 0; + self.end_index = @intCast(usize, rc); + } + const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]); + const next_index = self.index + darwin_entry.reclen(); + self.index = next_index; + + const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (darwin_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + os.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextBsd(self: *Self) !?Entry { + start_over: while (true) { + if (self.index >= self.end_index) { + const rc = os.system.getdirentries( + self.dir.fd, + self.buf[0..].ptr, + self.buf.len, + &self.seek, + ); + switch (os.errno(rc)) { + 0 => {}, + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => unreachable, + else => |err| return os.unexpectedErrno(err), + } + if (rc == 0) return null; + self.index = 0; + self.end_index = @intCast(usize, rc); + } + const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]); + const next_index = self.index + freebsd_entry.reclen(); + self.index = next_index; + + const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (freebsd_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + os.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + }, + .linux => struct { + dir: Dir, + buf: [8192]u8, // TODO align(@alignOf(os.dirent64)), + index: usize, + end_index: usize, + + const Self = @This(); + + pub const Error = IteratorError; + + /// Memory such as file names referenced in this returned entry becomes invalid + /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. + pub fn next(self: *Self) Error!?Entry { + start_over: while (true) { + if (self.index >= self.end_index) { + const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); + switch (os.linux.getErrno(rc)) { + 0 => {}, + os.EBADF => unreachable, + os.EFAULT => unreachable, + os.ENOTDIR => unreachable, + os.EINVAL => unreachable, + else => |err| return os.unexpectedErrno(err), + } + if (rc == 0) return null; + self.index = 0; + self.end_index = rc; + } + const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]); + const next_index = self.index + linux_entry.reclen(); + self.index = next_index; + + const name = mem.toSlice(u8, @ptrCast([*:0]u8, &linux_entry.d_name)); + + // skip . and .. entries + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (linux_entry.d_type) { + os.DT_BLK => Entry.Kind.BlockDevice, + os.DT_CHR => Entry.Kind.CharacterDevice, + os.DT_DIR => Entry.Kind.Directory, + os.DT_FIFO => Entry.Kind.NamedPipe, + os.DT_LNK => Entry.Kind.SymLink, + os.DT_REG => Entry.Kind.File, + os.DT_SOCK => Entry.Kind.UnixDomainSocket, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + }, + .windows => struct { + dir: Dir, + buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)), + index: usize, + end_index: usize, + first: bool, + name_data: [256]u8, + + const Self = @This(); + + pub const Error = IteratorError; + + pub fn next(self: *Self) Error!?Entry { + start_over: while (true) { + const w = os.windows; + if (self.index >= self.end_index) { + var io: w.IO_STATUS_BLOCK = undefined; + const rc = w.ntdll.NtQueryDirectoryFile( + self.dir.fd, + null, + null, + null, + &io, + &self.buf, + self.buf.len, + .FileBothDirectoryInformation, + w.FALSE, + null, + if (self.first) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE), + ); + self.first = false; + if (io.Information == 0) return null; + self.index = 0; + self.end_index = io.Information; + switch (rc) { + w.STATUS.SUCCESS => {}, + w.STATUS.ACCESS_DENIED => return error.AccessDenied, + else => return w.unexpectedStatus(rc), + } + } + + const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]); + const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr); + if (dir_info.NextEntryOffset != 0) { + self.index += dir_info.NextEntryOffset; + } else { + self.index = self.buf.len; + } + + const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2]; + + if (mem.eql(u16, name_utf16le, &[_]u16{'.'}) or mem.eql(u16, name_utf16le, &[_]u16{ '.', '.' })) + continue; + // Trust that Windows gives us valid UTF-16LE + const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable; + const name_utf8 = self.name_data[0..name_utf8_len]; + const kind = blk: { + const attrs = dir_info.FileAttributes; + if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; + if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; + break :blk Entry.Kind.File; + }; + return Entry{ + .name = name_utf8, + .kind = kind, + }; + } + } + }, + else => @compileError("unimplemented"), + }; + + pub fn iterate(self: Dir) Iterator { + switch (builtin.os) { + .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ + .dir = self, + .seek = 0, + .index = 0, + .end_index = 0, + .buf = undefined, + }, + .linux => return Iterator{ + .dir = self, + .index = 0, + .end_index = 0, + .buf = undefined, + }, + .windows => return Iterator{ + .dir = self, + .index = 0, + .end_index = 0, + .first = true, + .buf = undefined, + .name_data = undefined, + }, + else => @compileError("unimplemented"), + } + } + pub const OpenError = error{ FileNotFound, NotDir, AccessDenied, - FileTooBig, - IsDir, SymLinkLoop, ProcessFdQuotaExceeded, NameTooLong, SystemFdQuotaExceeded, NoDevice, SystemResources, - NoSpaceLeft, - PathAlreadyExists, - OutOfMemory, InvalidUtf8, BadPathName, DeviceBusy, + } || os.UnexpectedError; - Unexpected, - }; + /// Deprecated; call `cwd().openDirList` directly. + pub fn open(dir_path: []const u8) OpenError!Dir { + return cwd().openDirList(dir_path); + } - /// Call close when done. - /// TODO remove the allocator requirement from this API - /// https://github.com/ziglang/zig/issues/2885 - pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { - return Dir{ - .allocator = allocator, - .handle = switch (builtin.os) { - .windows => blk: { - var find_file_data: os.windows.WIN32_FIND_DATAW = undefined; - const handle = try os.windows.FindFirstFile(dir_path, &find_file_data); - break :blk Handle{ - .handle = handle, - .find_file_data = find_file_data, // TODO guaranteed copy elision - .first = true, - .name_data = undefined, - }; - }, - .macosx, .ios, .freebsd, .netbsd => Handle{ - .fd = try os.open(dir_path, os.O_RDONLY | os.O_NONBLOCK | os.O_DIRECTORY | os.O_CLOEXEC, 0), - .seek = 0, - .index = 0, - .end_index = 0, - .buf = [_]u8{}, - }, - .linux => Handle{ - .fd = try os.open(dir_path, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC, 0), - .index = 0, - .end_index = 0, - .buf = [_]u8{}, - }, - else => @compileError("unimplemented"), - }, - }; + /// Deprecated; call `cwd().openDirListC` directly. + pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { + return cwd().openDirListC(dir_path_c); } pub fn close(self: *Dir) void { - if (os.windows.is_the_target) { - return os.windows.FindClose(self.handle.handle); - } - self.allocator.free(self.handle.buf); - os.close(self.handle.fd); + os.close(self.fd); + self.* = undefined; } - /// Memory such as file names referenced in this returned entry becomes invalid - /// with subsequent calls to next, as well as when this `Dir` is deinitialized. - pub fn next(self: *Dir) !?Entry { - switch (builtin.os) { - .linux => return self.nextLinux(), - .macosx, .ios => return self.nextDarwin(), - .windows => return self.nextWindows(), - .freebsd => return self.nextBsd(), - .netbsd => return self.nextBsd(), - else => @compileError("unimplemented"), + /// Opens a file for reading or writing, without attempting to create a new file. + /// Call `File.close` to release the resource. + /// Asserts that the path parameter has no null bytes. + pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.openFileW(&path_w, flags); + } + const path_c = try os.toPosixPath(sub_path); + return self.openFileC(&path_c, flags); + } + + /// Same as `openFile` but the path parameter is null-terminated. + pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { + if (builtin.os == .windows) { + const path_w = try os.windows.cStrToPrefixedFileW(sub_path); + return self.openFileW(&path_w, flags); + } + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) + @as(u32, os.O_RDWR) + else if (flags.write) + @as(u32, os.O_WRONLY) + else + @as(u32, os.O_RDONLY); + const fd = try os.openatC(self.fd, sub_path, os_flags, 0); + return File{ .handle = fd }; + } + + /// Same as `openFile` but Windows-only and the path parameter is + /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. + pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { + const w = os.windows; + const access_mask = w.SYNCHRONIZE | + (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | + (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); + return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); + } + + /// Creates, opens, or overwrites a file with write access. + /// Call `File.close` on the result when done. + /// Asserts that the path parameter has no null bytes. + pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.createFileW(&path_w, flags); + } + const path_c = try os.toPosixPath(sub_path); + return self.createFileC(&path_c, flags); + } + + /// Same as `createFile` but the path parameter is null-terminated. + pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { + if (builtin.os == .windows) { + const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); + return self.createFileW(&path_w, flags); + } + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | + (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | + (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | + (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); + const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode); + return File{ .handle = fd }; + } + + /// Same as `createFile` but Windows-only and the path parameter is + /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. + pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { + const w = os.windows; + const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | + (if (flags.read) @as(u32, w.GENERIC_READ) else 0); + const creation = if (flags.exclusive) + @as(u32, w.FILE_CREATE) + else if (flags.truncate) + @as(u32, w.FILE_OVERWRITE_IF) + else + @as(u32, w.FILE_OPEN_IF); + return self.openFileWindows(sub_path_w, access_mask, creation); + } + + /// Deprecated; call `openFile` directly. + pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { + return self.openFile(sub_path, .{}); + } + + /// Deprecated; call `openFileC` directly. + pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File { + return self.openFileC(sub_path, .{}); + } + + /// Deprecated; call `openFileW` directly. + pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File { + return self.openFileW(sub_path, .{}); + } + + pub fn openFileWindows( + self: Dir, + sub_path_w: [*:0]const u16, + access_mask: os.windows.ACCESS_MASK, + creation: os.windows.ULONG, + ) File.OpenError!File { + const w = os.windows; + + var result = File{ .handle = undefined }; + + const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { + error.Overflow => return error.NameTooLong, + }; + var nt_name = w.UNICODE_STRING{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), + }; + var attr = w.OBJECT_ATTRIBUTES{ + .Length = @sizeOf(w.OBJECT_ATTRIBUTES), + .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }; + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + return error.IsDir; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + return error.IsDir; + } + var io: w.IO_STATUS_BLOCK = undefined; + const rc = w.ntdll.NtCreateFile( + &result.handle, + access_mask, + &attr, + &io, + null, + w.FILE_ATTRIBUTE_NORMAL, + w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, + creation, + w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, + null, + 0, + ); + switch (rc) { + w.STATUS.SUCCESS => return result, + w.STATUS.OBJECT_NAME_INVALID => unreachable, + w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + w.STATUS.INVALID_PARAMETER => unreachable, + w.STATUS.SHARING_VIOLATION => return error.SharingViolation, + w.STATUS.ACCESS_DENIED => return error.AccessDenied, + w.STATUS.PIPE_BUSY => return error.PipeBusy, + w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable, + w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists, + else => return w.unexpectedStatus(rc), } } - pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File { - const path_c = try os.toPosixPath(file_path); - return self.openReadC(&path_c); + /// Deprecated; call `openDirList` directly. + pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { + return self.openDirList(sub_path); } - pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File { - const flags = os.O_LARGEFILE | os.O_RDONLY; - const fd = try os.openatC(self.handle.fd, file_path, flags, 0); - return File.openHandle(fd); + /// Deprecated; call `openDirListC` directly. + pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { + return self.openDirListC(sub_path_c); } - fn nextDarwin(self: *Dir) !?Entry { + /// Opens a directory at the given path with the ability to access subpaths + /// of the result. Calling `iterate` on the result is illegal behavior; to + /// list the contents of a directory, open it with `openDirList`. + /// + /// Call `close` on the result when done. + /// + /// Asserts that the path parameter has no null bytes. + pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.openDirTraverseW(&sub_path_w); + } + + const sub_path_c = try os.toPosixPath(sub_path); + return self.openDirTraverseC(&sub_path_c); + } + + /// Opens a directory at the given path with the ability to access subpaths and list contents + /// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the + /// same and may be more efficient. + /// + /// Call `close` on the result when done. + /// + /// Asserts that the path parameter has no null bytes. + pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.openDirListW(&sub_path_w); + } + + const sub_path_c = try os.toPosixPath(sub_path); + return self.openDirListC(&sub_path_c); + } + + /// Same as `openDirTraverse` except the parameter is null-terminated. + pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { + if (builtin.os == .windows) { + const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); + return self.openDirTraverseW(&sub_path_w); + } else { + const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; + return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH); + } + } + + /// Same as `openDirList` except the parameter is null-terminated. + pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { + if (builtin.os == .windows) { + const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); + return self.openDirListW(&sub_path_w); + } else { + return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC); + } + } + + fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { + const fd = os.openatC(self.fd, sub_path_c, flags | os.O_DIRECTORY, 0) catch |err| switch (err) { + error.FileTooBig => unreachable, // can't happen for directories + error.IsDir => unreachable, // we're providing O_DIRECTORY + error.NoSpaceLeft => unreachable, // not providing O_CREAT + error.PathAlreadyExists => unreachable, // not providing O_CREAT + else => |e| return e, + }; + return Dir{ .fd = fd }; + } + + /// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed. + /// This function is Windows-only. + pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { + const w = os.windows; + + return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE); + } + + /// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed. + /// This function is Windows-only. + pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { + const w = os.windows; + + return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY); + } + + fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { + const w = os.windows; + + var result = Dir{ + .fd = undefined, + }; + + const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2); + var nt_name = w.UNICODE_STRING{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), + }; + var attr = w.OBJECT_ATTRIBUTES{ + .Length = @sizeOf(w.OBJECT_ATTRIBUTES), + .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }; + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + // Windows does not recognize this, but it does work with empty string. + nt_name.Length = 0; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + // If you're looking to contribute to zig and fix this, see here for an example of how to + // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c + @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows"); + } + var io: w.IO_STATUS_BLOCK = undefined; + const rc = w.ntdll.NtCreateFile( + &result.fd, + access_mask, + &attr, + &io, + null, + 0, + w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, + w.FILE_OPEN, + w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT, + null, + 0, + ); + switch (rc) { + w.STATUS.SUCCESS => return result, + w.STATUS.OBJECT_NAME_INVALID => unreachable, + w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + w.STATUS.INVALID_PARAMETER => unreachable, + else => return w.unexpectedStatus(rc), + } + } + + pub const DeleteFileError = os.UnlinkError; + + /// Delete a file name and possibly the file it refers to, based on an open directory handle. + /// Asserts that the path parameter has no null bytes. + pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { + os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; + } + + /// Same as `deleteFile` except the parameter is null-terminated. + pub fn deleteFileC(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void { + os.unlinkatC(self.fd, sub_path_c, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; + } + + /// Same as `deleteFile` except the parameter is WTF-16 encoded. + pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void { + os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; + } + + pub const DeleteDirError = error{ + DirNotEmpty, + FileNotFound, + AccessDenied, + FileBusy, + FileSystem, + SymLinkLoop, + NameTooLong, + NotDir, + SystemResources, + ReadOnlyFileSystem, + InvalidUtf8, + BadPathName, + Unexpected, + }; + + /// Returns `error.DirNotEmpty` if the directory is not empty. + /// To delete a directory recursively, see `deleteTree`. + /// Asserts that the path parameter has no null bytes. + pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.deleteDirW(&sub_path_w); + } + const sub_path_c = try os.toPosixPath(sub_path); + return self.deleteDirC(&sub_path_c); + } + + /// Same as `deleteDir` except the parameter is null-terminated. + pub fn deleteDirC(self: Dir, sub_path_c: [*:0]const u8) DeleteDirError!void { + os.unlinkatC(self.fd, sub_path_c, os.AT_REMOVEDIR) catch |err| switch (err) { + error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR + else => |e| return e, + }; + } + + /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed. + /// This function is Windows-only. + pub fn deleteDirW(self: Dir, sub_path_w: [*:0]const u16) DeleteDirError!void { + os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) { + error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR + else => |e| return e, + }; + } + + /// Read value of a symbolic link. + /// The return value is a slice of `buffer`, from index `0`. + /// Asserts that the path parameter has no null bytes. + pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); + const sub_path_c = try os.toPosixPath(sub_path); + return self.readLinkC(&sub_path_c, buffer); + } + + /// Same as `readLink`, except the `pathname` parameter is null-terminated. + pub fn readLinkC(self: Dir, sub_path_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { + return os.readlinkatC(self.fd, sub_path_c, buffer); + } + + /// On success, caller owns returned buffer. + /// If the file is larger than `max_bytes`, returns `error.FileTooBig`. + pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 { + return self.readFileAllocAligned(allocator, file_path, max_bytes, @alignOf(u8)); + } + + /// On success, caller owns returned buffer. + /// If the file is larger than `max_bytes`, returns `error.FileTooBig`. + pub fn readFileAllocAligned( + self: Dir, + allocator: *mem.Allocator, + file_path: []const u8, + max_bytes: usize, + comptime A: u29, + ) ![]align(A) u8 { + var file = try self.openRead(file_path); + defer file.close(); + + const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize); + if (size > max_bytes) return error.FileTooBig; + + const buf = try allocator.alignedAlloc(u8, A, size); + errdefer allocator.free(buf); + + try file.inStream().stream.readNoEof(buf); + return buf; + } + + pub const DeleteTreeError = error{ + AccessDenied, + FileTooBig, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + ReadOnlyFileSystem, + FileSystem, + FileBusy, + DeviceBusy, + + /// One of the path components was not a directory. + /// This error is unreachable if `sub_path` does not contain a path separator. + NotDir, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + } || os.UnexpectedError; + + /// Whether `full_path` describes a symlink, file, or directory, this function + /// removes it. If it cannot be removed because it is a non-empty directory, + /// this function recursively removes its entries and then tries again. + /// This operation is not atomic on most file systems. + pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, mem.page_size); - } + var got_access_denied = false; + // First, try deleting the item as a file. This way we don't follow sym links. + if (self.deleteFile(sub_path)) { + return; + } else |err| switch (err) { + error.FileNotFound => return, + error.IsDir => {}, + error.AccessDenied => got_access_denied = true, - while (true) { - const rc = os.system.__getdirentries64( - self.handle.fd, - self.handle.buf.ptr, - self.handle.buf.len, - &self.handle.seek, - ); - if (rc == 0) return null; - if (rc < 0) { - switch (os.errno(rc)) { - os.EBADF => unreachable, - os.EFAULT => unreachable, - os.ENOTDIR => unreachable, - os.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => |err| return os.unexpectedErrno(err), - } + error.InvalidUtf8, + error.SymLinkLoop, + error.NameTooLong, + error.SystemResources, + error.ReadOnlyFileSystem, + error.NotDir, + error.FileSystem, + error.FileBusy, + error.BadPathName, + error.Unexpected, + => |e| return e, + } + var dir = self.openDirList(sub_path) catch |err| switch (err) { + error.NotDir => { + if (got_access_denied) { + return error.AccessDenied; } - self.handle.index = 0; - self.handle.end_index = @intCast(usize, rc); - break; - } - } - const darwin_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + darwin_entry.d_reclen; - self.handle.index = next_index; + continue :start_over; + }, + error.FileNotFound => { + // That's fine, we were trying to remove this directory anyway. + continue :start_over; + }, - const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (darwin_entry.d_type) { - os.DT_BLK => Entry.Kind.BlockDevice, - os.DT_CHR => Entry.Kind.CharacterDevice, - os.DT_DIR => Entry.Kind.Directory, - os.DT_FIFO => Entry.Kind.NamedPipe, - os.DT_LNK => Entry.Kind.SymLink, - os.DT_REG => Entry.Kind.File, - os.DT_SOCK => Entry.Kind.UnixDomainSocket, - os.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, + error.AccessDenied, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.NameTooLong, + error.SystemFdQuotaExceeded, + error.NoDevice, + error.SystemResources, + error.Unexpected, + error.InvalidUtf8, + error.BadPathName, + error.DeviceBusy, + => |e| return e, }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } + var cleanup_dir_parent: ?Dir = null; + defer if (cleanup_dir_parent) |*d| d.close(); - fn nextWindows(self: *Dir) !?Entry { - while (true) { - if (self.handle.first) { - self.handle.first = false; - } else { - if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data)) - return null; - } - const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); - if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' })) - continue; - // Trust that Windows gives us valid UTF-16LE - const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; - const name_utf8 = self.handle.name_data[0..name_utf8_len]; - const kind = blk: { - const attrs = self.handle.find_file_data.dwFileAttributes; - if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; - if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; - break :blk Entry.Kind.File; - }; - return Entry{ - .name = name_utf8, - .kind = kind, - }; - } - } + var cleanup_dir = true; + defer if (cleanup_dir) dir.close(); - fn nextLinux(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, mem.page_size); - } + var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; + var dir_name: []const u8 = sub_path; + var parent_dir = self; - while (true) { - const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); - switch (os.linux.getErrno(rc)) { - 0 => {}, - os.EBADF => unreachable, - os.EFAULT => unreachable, - os.ENOTDIR => unreachable, - os.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; + // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. + // Go through each entry and if it is not a directory, delete it. If it is a directory, + // open it, and close the original directory. Repeat. Then start the entire operation over. + + scan_dir: while (true) { + var dir_it = dir.iterate(); + while (try dir_it.next()) |entry| { + if (dir.deleteFile(entry.name)) { + continue; + } else |err| switch (err) { + error.FileNotFound => continue, + + // Impossible because we do not pass any path separators. + error.NotDir => unreachable, + + error.IsDir => {}, + error.AccessDenied => got_access_denied = true, + + error.InvalidUtf8, + error.SymLinkLoop, + error.NameTooLong, + error.SystemResources, + error.ReadOnlyFileSystem, + error.FileSystem, + error.FileBusy, + error.BadPathName, + error.Unexpected, + => |e| return e, + } + + const new_dir = dir.openDirList(entry.name) catch |err| switch (err) { + error.NotDir => { + if (got_access_denied) { + return error.AccessDenied; + } + continue :scan_dir; }, - else => |err| return os.unexpectedErrno(err), - } - if (rc == 0) return null; - self.handle.index = 0; - self.handle.end_index = rc; - break; + error.FileNotFound => { + // That's fine, we were trying to remove this directory anyway. + continue :scan_dir; + }, + + error.AccessDenied, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.NameTooLong, + error.SystemFdQuotaExceeded, + error.NoDevice, + error.SystemResources, + error.Unexpected, + error.InvalidUtf8, + error.BadPathName, + error.DeviceBusy, + => |e| return e, + }; + if (cleanup_dir_parent) |*d| d.close(); + cleanup_dir_parent = dir; + dir = new_dir; + mem.copy(u8, &dir_name_buf, entry.name); + dir_name = dir_name_buf[0..entry.name.len]; + continue :scan_dir; + } + // Reached the end of the directory entries, which means we successfully deleted all of them. + // Now to remove the directory itself. + dir.close(); + cleanup_dir = false; + + if (cleanup_dir_parent) |d| { + d.deleteDir(dir_name) catch |err| switch (err) { + // These two things can happen due to file system race conditions. + error.FileNotFound, error.DirNotEmpty => continue :start_over, + else => |e| return e, + }; + continue :start_over; + } else { + self.deleteDir(sub_path) catch |err| switch (err) { + error.FileNotFound => return, + error.DirNotEmpty => continue :start_over, + else => |e| return e, + }; + return; } } - const linux_entry = @ptrCast(*align(1) os.dirent64, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + linux_entry.d_reclen; - self.handle.index = next_index; - - const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name)); - - // skip . and .. entries - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (linux_entry.d_type) { - os.DT_BLK => Entry.Kind.BlockDevice, - os.DT_CHR => Entry.Kind.CharacterDevice, - os.DT_DIR => Entry.Kind.Directory, - os.DT_FIFO => Entry.Kind.NamedPipe, - os.DT_LNK => Entry.Kind.SymLink, - os.DT_REG => Entry.Kind.File, - os.DT_SOCK => Entry.Kind.UnixDomainSocket, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; } } - fn nextBsd(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, mem.page_size); - } - - while (true) { - const rc = os.system.getdirentries( - self.handle.fd, - self.handle.buf.ptr, - self.handle.buf.len, - &self.handle.seek, - ); - switch (os.errno(rc)) { - 0 => {}, - os.EBADF => unreachable, - os.EFAULT => unreachable, - os.ENOTDIR => unreachable, - os.EINVAL => { - self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => |err| return os.unexpectedErrno(err), - } - if (rc == 0) return null; - self.handle.index = 0; - self.handle.end_index = @intCast(usize, rc); - break; - } - } - const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + freebsd_entry.d_reclen; - self.handle.index = next_index; - - const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (freebsd_entry.d_type) { - os.DT_BLK => Entry.Kind.BlockDevice, - os.DT_CHR => Entry.Kind.CharacterDevice, - os.DT_DIR => Entry.Kind.Directory, - os.DT_FIFO => Entry.Kind.NamedPipe, - os.DT_LNK => Entry.Kind.SymLink, - os.DT_REG => Entry.Kind.File, - os.DT_SOCK => Entry.Kind.UnixDomainSocket, - os.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } + /// Writes content to the file system, creating a new file if it does not exist, truncating + /// if it already exists. + pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void { + var file = try self.createFile(sub_path, .{}); + defer file.close(); + try file.write(data); } }; +/// Returns an handle to the current working directory that is open for traversal. +/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. +/// On POSIX targets, this function is comptime-callable. +pub fn cwd() Dir { + if (builtin.os == .windows) { + return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; + } else { + return Dir{ .fd = os.AT_FDCWD }; + } +} + +/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path. +/// Call `File.close` to release the resource. +/// Asserts that the path is absolute. See `Dir.openFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function +/// that accepts a null-terminated path. +pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsolute(absolute_path)); + return cwd().openFile(absolute_path, flags); +} + +/// Same as `openFileAbsolute` but the path parameter is null-terminated. +pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().openFileC(absolute_path_c, flags); +} + +/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. +pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().openFileW(absolute_path_w, flags); +} + +/// Creates, opens, or overwrites a file with write access, based on an absolute path. +/// Call `File.close` to release the resource. +/// Asserts that the path is absolute. See `Dir.createFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function +/// that accepts a null-terminated path. +pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsolute(absolute_path)); + return cwd().createFile(absolute_path, flags); +} + +/// Same as `createFileAbsolute` but the path parameter is null-terminated. +pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().createFileC(absolute_path_c, flags); +} + +/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded. +pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().createFileW(absolute_path_w, flags); +} + +/// Delete a file name and possibly the file it refers to, based on an absolute path. +/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. +pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void { + assert(path.isAbsolute(absolute_path)); + return cwd().deleteFile(absolute_path); +} + +/// Same as `deleteFileAbsolute` except the parameter is null-terminated. +pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().deleteFileC(absolute_path_c); +} + +/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded. +pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().deleteFileW(absolute_path_w); +} + pub const Walker = struct { stack: std.ArrayList(StackItem), name_buffer: std.Buffer, pub const Entry = struct { - path: []const u8, + /// The containing directory. This can be used to operate directly on `basename` + /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. + /// The directory remains open until `next` or `deinit` is called. + dir: Dir, basename: []const u8, + + path: []const u8, kind: Dir.Entry.Kind, }; const StackItem = struct { - dir_it: Dir, + dir_it: Dir.Iterator, dirname_len: usize, }; @@ -807,29 +1409,32 @@ pub const Walker = struct { try self.name_buffer.appendByte(path.sep); try self.name_buffer.append(base.name); if (base.kind == .Directory) { - // TODO https://github.com/ziglang/zig/issues/2888 - var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst()); + var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) { + error.NameTooLong => unreachable, // no path sep in base.name + else => |e| return e, + }; { errdefer new_dir.close(); try self.stack.append(StackItem{ - .dir_it = new_dir, + .dir_it = new_dir.iterate(), .dirname_len = self.name_buffer.len(), }); } } return Entry{ + .dir = top.dir_it.dir, .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..], .path = self.name_buffer.toSliceConst(), .kind = base.kind, }; } else { - self.stack.pop().dir_it.close(); + self.stack.pop().dir_it.dir.close(); } } } pub fn deinit(self: *Walker) void { - while (self.stack.popOrNull()) |*item| item.dir_it.close(); + while (self.stack.popOrNull()) |*item| item.dir_it.dir.close(); self.stack.deinit(); self.name_buffer.deinit(); } @@ -838,12 +1443,12 @@ pub const Walker = struct { /// Recursively iterates over a directory. /// Must call `Walker.deinit` when done. /// `dir_path` must not end in a path separator. -/// TODO: https://github.com/ziglang/zig/issues/2888 +/// The order of returned file system entries is undefined. pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { assert(!mem.endsWith(u8, dir_path, path.sep_str)); - var dir_it = try Dir.open(allocator, dir_path); - errdefer dir_it.close(); + var dir = try cwd().openDirList(dir_path); + errdefer dir.close(); var name_buffer = try std.Buffer.init(allocator, dir_path); errdefer name_buffer.deinit(); @@ -854,7 +1459,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { }; try walker.stack.append(Walker.StackItem{ - .dir_it = dir_it, + .dir_it = dir.iterate(), .dirname_len = dir_path.len, }); @@ -863,37 +1468,35 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { /// Read value of a symbolic link. /// The return value is a slice of buffer, from index `0`. -/// TODO https://github.com/ziglang/zig/issues/2888 -pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { +pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { return os.readlink(pathname, buffer); } -/// Same as `readLink`, except the `pathname` parameter is null-terminated. -/// TODO https://github.com/ziglang/zig/issues/2888 -pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { - return os.readlinkC(pathname, buffer); +/// Same as `readLink`, except the parameter is null-terminated. +pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { + return os.readlinkC(pathname_c, buffer); } pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; pub fn openSelfExe() OpenSelfExeError!File { - if (os.linux.is_the_target) { - return File.openReadC(c"/proc/self/exe"); + if (builtin.os == .linux) { + return openFileAbsoluteC("/proc/self/exe", .{}); } - if (os.windows.is_the_target) { - var buf: [os.windows.PATH_MAX_WIDE]u16 = undefined; - const wide_slice = try selfExePathW(&buf); - return File.openReadW(wide_slice.ptr); + if (builtin.os == .windows) { + const wide_slice = selfExePathW(); + const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); + return cwd().openReadW(&prefixed_path_w); } var buf: [MAX_PATH_BYTES]u8 = undefined; const self_exe_path = try selfExePath(&buf); buf[self_exe_path.len] = 0; - return File.openReadC(self_exe_path.ptr); + return openFileAbsoluteC(self_exe_path[0..self_exe_path.len :0].ptr, .{}); } test "openSelfExe" { switch (builtin.os) { - .linux, .macosx, .ios, .windows, .freebsd => (try openSelfExe()).close(), + .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(), else => return error.SkipZigTest, // Unsupported OS. } } @@ -911,31 +1514,30 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. /// TODO make the return type of this a null terminated pointer pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { var u32_len: u32 = out_buffer.len; const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); if (rc != 0) return error.NameTooLong; - return mem.toSlice(u8, out_buffer); + return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer)); } switch (builtin.os) { - .linux => return os.readlinkC(c"/proc/self/exe", out_buffer), - .freebsd => { + .linux => return os.readlinkC("/proc/self/exe", out_buffer), + .freebsd, .dragonfly => { var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; var out_len: usize = out_buffer.len; try os.sysctl(&mib, out_buffer, &out_len, null, 0); // TODO could this slice from 0 to out_len instead? - return mem.toSlice(u8, out_buffer); + return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer)); }, .netbsd => { var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME }; var out_len: usize = out_buffer.len; try os.sysctl(&mib, out_buffer, &out_len, null, 0); // TODO could this slice from 0 to out_len instead? - return mem.toSlice(u8, out_buffer); + return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer)); }, .windows => { - var utf16le_buf: [os.windows.PATH_MAX_WIDE]u16 = undefined; - const utf16le_slice = try selfExePathW(&utf16le_buf); + const utf16le_slice = selfExePathW(); // Trust that Windows gives us valid UTF-16LE. const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; return out_buffer[0..end_index]; @@ -944,9 +1546,10 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { } } -/// Same as `selfExePath` except the result is UTF16LE-encoded. -pub fn selfExePathW(out_buffer: *[os.windows.PATH_MAX_WIDE]u16) SelfExePathError![]u16 { - return os.windows.GetModuleFileNameW(null, out_buffer, out_buffer.len); +/// The result is UTF16LE-encoded. +pub fn selfExePathW() [:0]const u16 { + const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; + return mem.toSliceConst(u16, @ptrCast([*:0]const u16, image_path_name.Buffer)); } /// `selfExeDirPath` except allocates the result on the heap. @@ -959,12 +1562,12 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { /// Get the directory path that contains the current executable. /// Returned value is a slice of out_buffer. pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { - if (os.linux.is_the_target) { + if (builtin.os == .linux) { // If the currently executing binary has been deleted, // the file path looks something like `/a/b/c/exe (deleted)` // This path cannot be opened, but it's valid for determining the directory // the executable was in when it was run. - const full_exe_path = try os.readlinkC(c"/proc/self/exe", out_buffer); + const full_exe_path = try os.readlinkC("/proc/self/exe", out_buffer); // Assume that /proc/self/exe has an absolute path, and therefore dirname // will not return null. return path.dirname(full_exe_path).?; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 5ecad0102..c7265a892 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -25,116 +25,87 @@ pub const File = struct { pub const OpenError = windows.CreateFileError || os.OpenError; - /// Call close to clean up. + /// TODO https://github.com/ziglang/zig/issues/3802 + pub const OpenFlags = struct { + read: bool = true, + write: bool = false, + }; + + /// TODO https://github.com/ziglang/zig/issues/3802 + pub const CreateFlags = struct { + /// Whether the file will be created with read access. + read: bool = false, + + /// If the file already exists, and is a regular file, and the access + /// mode allows writing, it will be truncated to length 0. + truncate: bool = true, + + /// Ensures that this open call creates the file, otherwise causes + /// `error.FileAlreadyExists` to be returned. + exclusive: bool = false, + + /// For POSIX systems this is the file system mode the file will + /// be created with. + mode: Mode = default_mode, + }; + + /// Deprecated; call `std.fs.Dir.openFile` directly. pub fn openRead(path: []const u8) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.sliceToPrefixedFileW(path); - return openReadW(&path_w); - } - const path_c = try os.toPosixPath(path); - return openReadC(&path_c); + return std.fs.cwd().openFile(path, .{}); } - /// `openRead` except with a null terminated path - pub fn openReadC(path: [*]const u8) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.cStrToPrefixedFileW(path); - return openReadW(&path_w); - } - const flags = os.O_LARGEFILE | os.O_RDONLY; - const fd = try os.openC(path, flags, 0); - return openHandle(fd); + /// Deprecated; call `std.fs.Dir.openFileC` directly. + pub fn openReadC(path_c: [*:0]const u8) OpenError!File { + return std.fs.cwd().openFileC(path_c, .{}); } - /// `openRead` except with a null terminated UTF16LE encoded path - pub fn openReadW(path_w: [*]const u16) OpenError!File { - const handle = try windows.CreateFileW( - path_w, - windows.GENERIC_READ, - windows.FILE_SHARE_READ, - null, - windows.OPEN_EXISTING, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - return openHandle(handle); + /// Deprecated; call `std.fs.Dir.openFileW` directly. + pub fn openReadW(path_w: [*:0]const u16) OpenError!File { + return std.fs.cwd().openFileW(path_w, .{}); } - /// Calls `openWriteMode` with `default_mode` for the mode. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWrite(path: []const u8) OpenError!File { - return openWriteMode(path, default_mode); + return std.fs.cwd().createFile(path, .{}); } - /// If the path does not exist it will be created. - /// If a file already exists in the destination it will be truncated. - /// Call close to clean up. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.sliceToPrefixedFileW(path); - return openWriteModeW(&path_w, file_mode); - } - const path_c = try os.toPosixPath(path); - return openWriteModeC(&path_c, file_mode); + return std.fs.cwd().createFile(path, .{ .mode = file_mode }); } - /// Same as `openWriteMode` except `path` is null-terminated. - pub fn openWriteModeC(path: [*]const u8, file_mode: Mode) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.cStrToPrefixedFileW(path); - return openWriteModeW(&path_w, file_mode); - } - const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; - const fd = try os.openC(path, flags, file_mode); - return openHandle(fd); + /// Deprecated; call `std.fs.Dir.createFileC` directly. + pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { + return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode }); } - /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded - pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File { - const handle = try windows.CreateFileW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - null, - windows.CREATE_ALWAYS, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - return openHandle(handle); + /// Deprecated; call `std.fs.Dir.createFileW` directly. + pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { + return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode }); } - /// If the path does not exist it will be created. - /// If a file already exists in the destination this returns OpenError.PathAlreadyExists - /// Call close to clean up. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.sliceToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } - const path_c = try os.toPosixPath(path); - return openWriteNoClobberC(&path_c, file_mode); + return std.fs.cwd().createFile(path, .{ + .mode = file_mode, + .exclusive = true, + }); } - pub fn openWriteNoClobberC(path: [*]const u8, file_mode: Mode) OpenError!File { - if (windows.is_the_target) { - const path_w = try windows.cStrToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } - const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL; - const fd = try os.openC(path, flags, file_mode); - return openHandle(fd); + /// Deprecated; call `std.fs.Dir.createFileC` directly. + pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { + return std.fs.cwd().createFileC(path_c, .{ + .mode = file_mode, + .exclusive = true, + }); } - pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File { - const handle = try windows.CreateFileW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - null, - windows.CREATE_NEW, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - return openHandle(handle); + /// Deprecated; call `std.fs.Dir.createFileW` directly. + pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { + return std.fs.cwd().createFileW(path_w, .{ + .mode = file_mode, + .exclusive = true, + }); } pub fn openHandle(handle: os.fd_t) File { @@ -146,17 +117,20 @@ pub const File = struct { /// In general it is recommended to avoid this function. For example, /// instead of testing if a file exists and then opening it, just /// open it and handle the error for file not found. + /// TODO: deprecate this and move it to `std.fs.Dir`. pub fn access(path: []const u8) !void { return os.access(path, os.F_OK); } /// Same as `access` except the parameter is null-terminated. - pub fn accessC(path: [*]const u8) !void { + /// TODO: deprecate this and move it to `std.fs.Dir`. + pub fn accessC(path: [*:0]const u8) !void { return os.accessC(path, os.F_OK); } /// Same as `access` except the parameter is null-terminated UTF16LE-encoded. - pub fn accessW(path: [*]const u16) !void { + /// TODO: deprecate this and move it to `std.fs.Dir`. + pub fn accessW(path: [*:0]const u16) !void { return os.accessW(path, os.F_OK); } @@ -174,10 +148,20 @@ pub const File = struct { /// Test whether ANSI escape codes will be treated as such. pub fn supportsAnsiEscapeCodes(self: File) bool { - if (windows.is_the_target) { + if (builtin.os == .windows) { return os.isCygwinPty(self.handle); } - return self.isTty(); + if (self.isTty()) { + if (self.handle == os.STDOUT_FILENO or self.handle == os.STDERR_FILENO) { + // Use getenvC to workaround https://github.com/ziglang/zig/issues/3511 + if (os.getenvC("TERM")) |term| { + if (std.mem.eql(u8, term, "dumb")) + return false; + } + } + return true; + } + return false; } pub const SeekError = os.SeekError; @@ -204,7 +188,7 @@ pub const File = struct { } pub fn getEndPos(self: File) GetPosError!u64 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.GetFileSizeEx(self.handle); } return (try self.stat()).size; @@ -213,7 +197,7 @@ pub const File = struct { pub const ModeError = os.FStatError; pub fn mode(self: File) ModeError!Mode { - if (windows.is_the_target) { + if (builtin.os == .windows) { return {}; } return (try self.stat()).mode; @@ -236,13 +220,15 @@ pub const File = struct { pub const StatError = os.FStatError; pub fn stat(self: File) StatError!Stat { - if (windows.is_the_target) { + if (builtin.os == .windows) { var io_status_block: windows.IO_STATUS_BLOCK = undefined; var info: windows.FILE_ALL_INFORMATION = undefined; const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation); switch (rc) { windows.STATUS.SUCCESS => {}, windows.STATUS.BUFFER_OVERFLOW => {}, + windows.STATUS.INVALID_PARAMETER => unreachable, + windows.STATUS.ACCESS_DENIED => return error.AccessDenied, else => return windows.unexpectedStatus(rc), } return Stat{ @@ -261,30 +247,38 @@ pub const File = struct { return Stat{ .size = @bitCast(u64, st.size), .mode = st.mode, - .atime = i64(atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, - .mtime = i64(mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, - .ctime = i64(ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, + .atime = @as(i64, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, + .mtime = @as(i64, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, + .ctime = @as(i64, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, }; } pub const UpdateTimesError = os.FutimensError || windows.SetFileTimeError; - /// `atime`: access timestamp in nanoseconds - /// `mtime`: last modification timestamp in nanoseconds - pub fn updateTimes(self: File, atime: i64, mtime: i64) UpdateTimesError!void { - if (windows.is_the_target) { + /// The underlying file system may have a different granularity than nanoseconds, + /// and therefore this function cannot guarantee any precision will be stored. + /// Further, the maximum value is limited by the system ABI. When a value is provided + /// that exceeds this range, the value is clamped to the maximum. + pub fn updateTimes( + self: File, + /// access timestamp in nanoseconds + atime: i64, + /// last modification timestamp in nanoseconds + mtime: i64, + ) UpdateTimesError!void { + if (builtin.os == .windows) { const atime_ft = windows.nanoSecondsToFileTime(atime); const mtime_ft = windows.nanoSecondsToFileTime(mtime); return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft); } const times = [2]os.timespec{ os.timespec{ - .tv_sec = @divFloor(atime, std.time.ns_per_s), - .tv_nsec = @mod(atime, std.time.ns_per_s), + .tv_sec = math.cast(isize, @divFloor(atime, std.time.ns_per_s)) catch maxInt(isize), + .tv_nsec = math.cast(isize, @mod(atime, std.time.ns_per_s)) catch maxInt(isize), }, os.timespec{ - .tv_sec = @divFloor(mtime, std.time.ns_per_s), - .tv_nsec = @mod(mtime, std.time.ns_per_s), + .tv_sec = math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) catch maxInt(isize), + .tv_nsec = math.cast(isize, @mod(mtime, std.time.ns_per_s)) catch maxInt(isize), }, }; try os.futimens(self.handle, ×); diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index cdab7703f..cb26e335d 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -15,7 +15,7 @@ pub const GetAppDataDirError = error{ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { switch (builtin.os) { .windows => { - var dir_path_ptr: [*]u16 = undefined; + var dir_path_ptr: [*:0]u16 = undefined; switch (os.windows.shell32.SHGetKnownFolderPath( &os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE, @@ -24,14 +24,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD )) { os.windows.S_OK => { defer os.windows.ole32.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); - const global_dir = unicode.utf16leToUtf8Alloc(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { + const global_dir = unicode.utf16leToUtf8Alloc(allocator, mem.toSliceConst(u16, dir_path_ptr)) catch |err| switch (err) { error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, error.DanglingSurrogateHalf => return error.AppDataDirUnavailable, error.OutOfMemory => return error.OutOfMemory, }; defer allocator.free(global_dir); - return fs.path.join(allocator, [_][]const u8{ global_dir, appname }); + return fs.path.join(allocator, &[_][]const u8{ global_dir, appname }); }, os.windows.E_OUTOFMEMORY => return error.OutOfMemory, else => return error.AppDataDirUnavailable, @@ -42,25 +42,19 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return fs.path.join(allocator, [_][]const u8{ home_dir, "Library", "Application Support", appname }); + return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname }); }, - .linux, .freebsd, .netbsd => { + .linux, .freebsd, .netbsd, .dragonfly => { const home_dir = os.getenv("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return fs.path.join(allocator, [_][]const u8{ home_dir, ".local", "share", appname }); + return fs.path.join(allocator, &[_][]const u8{ home_dir, ".local", "share", appname }); }, else => @compileError("Unsupported OS"), } } -fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { - var index: usize = 0; - while (ptr[index] != 0) : (index += 1) {} - return ptr[0..index]; -} - test "getAppDataDir" { var buf: [512]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 2bb23f04c..b3ad3e9f7 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -13,16 +13,18 @@ const process = std.process; pub const sep_windows = '\\'; pub const sep_posix = '/'; -pub const sep = if (windows.is_the_target) sep_windows else sep_posix; +pub const sep = if (builtin.os == .windows) sep_windows else sep_posix; -pub const sep_str = [1]u8{sep}; +pub const sep_str_windows = "\\"; +pub const sep_str_posix = "/"; +pub const sep_str = if (builtin.os == .windows) sep_str_windows else sep_str_posix; pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; -pub const delimiter = if (windows.is_the_target) delimiter_windows else delimiter_posix; +pub const delimiter = if (builtin.os == .windows) delimiter_windows else delimiter_posix; pub fn isSep(byte: u8) bool { - if (windows.is_the_target) { + if (builtin.os == .windows) { return byte == '/' or byte == '\\'; } else { return byte == '/'; @@ -32,7 +34,7 @@ pub fn isSep(byte: u8) bool { /// This is different from mem.join in that the separator will not be repeated if /// it is found at the end or beginning of a pair of consecutive paths. fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 { - if (paths.len == 0) return (([*]u8)(undefined))[0..0]; + if (paths.len == 0) return &[0]u8{}; const total_len = blk: { var sum: usize = paths[0].len; @@ -72,7 +74,7 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u return buf; } -pub const join = if (windows.is_the_target) joinWindows else joinPosix; +pub const join = if (builtin.os == .windows) joinWindows else joinPosix; /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. @@ -101,41 +103,68 @@ fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { } test "join" { - testJoinWindows([_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); - testJoinWindows([_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); - testJoinWindows([_][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); + testJoinWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); + testJoinWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); + testJoinWindows(&[_][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); - testJoinWindows([_][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c"); - testJoinWindows([_][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c"); + testJoinWindows(&[_][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c"); + testJoinWindows(&[_][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c"); testJoinWindows( - [_][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, + &[_][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", ); - testJoinPosix([_][]const u8{ "/a/b", "c" }, "/a/b/c"); - testJoinPosix([_][]const u8{ "/a/b/", "c" }, "/a/b/c"); + testJoinPosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); + testJoinPosix(&[_][]const u8{ "/a/b/", "c" }, "/a/b/c"); - testJoinPosix([_][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); - testJoinPosix([_][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); + testJoinPosix(&[_][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); + testJoinPosix(&[_][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); testJoinPosix( - [_][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, + &[_][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, "/home/andy/dev/zig/build/lib/zig/std/io.zig", ); - testJoinPosix([_][]const u8{ "a", "/c" }, "a/c"); - testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c"); + testJoinPosix(&[_][]const u8{ "a", "/c" }, "a/c"); + testJoinPosix(&[_][]const u8{ "a/", "/c" }, "a/c"); +} + +pub fn isAbsoluteC(path_c: [*:0]const u8) bool { + if (builtin.os == .windows) { + return isAbsoluteWindowsC(path_c); + } else { + return isAbsolutePosixC(path_c); + } } pub fn isAbsolute(path: []const u8) bool { - if (windows.is_the_target) { + if (builtin.os == .windows) { return isAbsoluteWindows(path); } else { return isAbsolutePosix(path); } } +pub fn isAbsoluteW(path_w: [*:0]const u16) bool { + if (path_w[0] == '/') + return true; + + if (path_w[0] == '\\') { + return true; + } + if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) { + return false; + } + if (path_w[1] == ':') { + if (path_w[2] == '/') + return true; + if (path_w[2] == '\\') + return true; + } + return false; +} + pub fn isAbsoluteWindows(path: []const u8) bool { if (path[0] == '/') return true; @@ -155,10 +184,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool { return false; } +pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool { + if (path_c[0] == '/') + return true; + + if (path_c[0] == '\\') { + return true; + } + if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) { + return false; + } + if (path_c[1] == ':') { + if (path_c[2] == '/') + return true; + if (path_c[2] == '\\') + return true; + } + return false; +} + pub fn isAbsolutePosix(path: []const u8) bool { return path[0] == sep_posix; } +pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool { + return path_c[0] == sep_posix; +} + test "isAbsoluteWindows" { testIsAbsoluteWindows("/", true); testIsAbsoluteWindows("//", true); @@ -227,41 +279,21 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { } const relative_path = WindowsPath{ .kind = WindowsPath.Kind.None, - .disk_designator = [_]u8{}, + .disk_designator = &[_]u8{}, .is_abs = false, }; if (path.len < "//a/b".len) { return relative_path; } - // TODO when I combined these together with `inline for` the compiler crashed - { - const this_sep = '/'; + inline for ("/\\") |this_sep| { const two_sep = [_]u8{ this_sep, this_sep }; - if (mem.startsWith(u8, path, two_sep)) { + if (mem.startsWith(u8, path, &two_sep)) { if (path[2] == this_sep) { return relative_path; } - var it = mem.tokenize(path, [_]u8{this_sep}); - _ = (it.next() orelse return relative_path); - _ = (it.next() orelse return relative_path); - return WindowsPath{ - .is_abs = isAbsoluteWindows(path), - .kind = WindowsPath.Kind.NetworkShare, - .disk_designator = path[0..it.index], - }; - } - } - { - const this_sep = '\\'; - const two_sep = [_]u8{ this_sep, this_sep }; - if (mem.startsWith(u8, path, two_sep)) { - if (path[2] == this_sep) { - return relative_path; - } - - var it = mem.tokenize(path, [_]u8{this_sep}); + var it = mem.tokenize(path, &[_]u8{this_sep}); _ = (it.next() orelse return relative_path); _ = (it.next() orelse return relative_path); return WindowsPath{ @@ -308,7 +340,7 @@ test "windowsParsePath" { } pub fn diskDesignator(path: []const u8) []const u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return diskDesignatorWindows(path); } else { return ""; @@ -323,8 +355,8 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool { const sep1 = ns1[0]; const sep2 = ns2[0]; - var it1 = mem.tokenize(ns1, [_]u8{sep1}); - var it2 = mem.tokenize(ns2, [_]u8{sep2}); + var it1 = mem.tokenize(ns1, &[_]u8{sep1}); + var it2 = mem.tokenize(ns2, &[_]u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. return asciiEqlIgnoreCase(it1.next().?, it2.next().?); @@ -344,8 +376,8 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 const sep1 = p1[0]; const sep2 = p2[0]; - var it1 = mem.tokenize(p1, [_]u8{sep1}); - var it2 = mem.tokenize(p2, [_]u8{sep2}); + var it1 = mem.tokenize(p1, &[_]u8{sep1}); + var it2 = mem.tokenize(p2, &[_]u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?); @@ -373,7 +405,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return resolveWindows(allocator, paths); } else { return resolvePosix(allocator, paths); @@ -390,12 +422,12 @@ pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd + assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd return process.getCwdAlloc(allocator); } // determine which disk designator we will result with, if any - var result_drive_buf = "_:"; + var result_drive_buf = "_:".*; var result_disk_designator: []const u8 = ""; var have_drive_kind = WindowsPath.Kind.None; var have_abs_path = false; @@ -485,7 +517,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { result_disk_designator = result[0..result_index]; }, WindowsPath.Kind.None => { - assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd + assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); const parsed_cwd = windowsParsePath(cwd); @@ -500,7 +532,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, } } else { - assert(windows.is_the_target); // resolveWindows called on non windows can't use getCwd + assert(builtin.os == .windows); // resolveWindows called on non windows can't use getCwd // TODO call get cwd for the result_disk_designator instead of the global one const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); @@ -571,7 +603,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { - assert(!windows.is_the_target); // resolvePosix called on windows can't use getCwd + assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd return process.getCwdAlloc(allocator); } @@ -593,7 +625,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (have_abs) { result = try allocator.alloc(u8, max_size); } else { - assert(!windows.is_the_target); // resolvePosix called on windows can't use getCwd + assert(builtin.os != .windows); // resolvePosix called on windows can't use getCwd const cwd = try process.getCwdAlloc(allocator); defer allocator.free(cwd); result = try allocator.alloc(u8, max_size + cwd.len + 1); @@ -634,14 +666,14 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { test "resolve" { const cwd = try process.getCwdAlloc(debug.global_allocator); - if (windows.is_the_target) { + if (builtin.os == .windows) { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { cwd[0] = asciiUpper(cwd[0]); } - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{"."}), cwd)); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{"."}), cwd)); } else { - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "a/b/c/", "../../.." }), cwd)); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{"."}), cwd)); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }), cwd)); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{"."}), cwd)); } } @@ -650,12 +682,12 @@ test "resolveWindows" { // TODO https://github.com/ziglang/zig/issues/3288 return error.SkipZigTest; } - if (windows.is_the_target) { + if (builtin.os == .windows) { const cwd = try process.getCwdAlloc(debug.global_allocator); const parsed_cwd = windowsParsePath(cwd); { - const result = testResolveWindows([_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); - const expected = try join(debug.global_allocator, [_][]const u8{ + const result = testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); + const expected = try join(debug.global_allocator, &[_][]const u8{ parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig", }); @@ -665,8 +697,8 @@ test "resolveWindows" { testing.expect(mem.eql(u8, result, expected)); } { - const result = testResolveWindows([_][]const u8{ "usr/local", "lib\\zig" }); - const expected = try join(debug.global_allocator, [_][]const u8{ + const result = testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }); + const expected = try join(debug.global_allocator, &[_][]const u8{ cwd, "usr\\local\\lib\\zig", }); @@ -677,32 +709,32 @@ test "resolveWindows" { } } - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }), "C:\\hi\\ok")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }), "C:\\blah\\a")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/blah\\blah", "d:/games", "C:../a" }), "C:\\blah\\a")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/ignore", "d:\\a/b\\c/d", "\\e.exe" }), "D:\\e.exe")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/ignore", "c:/some/file" }), "C:\\some\\file")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "d:/ignore", "d:some/dir//" }), "D:\\ignore\\some\\dir")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "//server/share", "..", "relative\\" }), "\\\\server\\share\\relative")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/", "//" }), "C:\\")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/", "//dir" }), "C:\\dir")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/", "//server/share" }), "\\\\server\\share\\")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/", "//server//share" }), "\\\\server\\share\\")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "c:/", "///some//dir" }), "C:\\some\\dir")); - testing.expect(mem.eql(u8, testResolveWindows([_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }), "C:\\foo\\tmp.3\\cycles\\root.js")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }), "C:\\hi\\ok")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }), "C:\\blah\\a")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "C:../a" }), "C:\\blah\\a")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/ignore", "d:\\a/b\\c/d", "\\e.exe" }), "D:\\e.exe")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/ignore", "c:/some/file" }), "C:\\some\\file")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "d:/ignore", "d:some/dir//" }), "D:\\ignore\\some\\dir")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "//server/share", "..", "relative\\" }), "\\\\server\\share\\relative")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/", "//" }), "C:\\")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/", "//dir" }), "C:\\dir")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/", "//server/share" }), "\\\\server\\share\\")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }), "\\\\server\\share\\")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }), "C:\\some\\dir")); + testing.expect(mem.eql(u8, testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }), "C:\\foo\\tmp.3\\cycles\\root.js")); } test "resolvePosix" { - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/a/b", "c" }), "/a/b/c")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/a/b/c", "..", "../" }), "/a")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/", "..", ".." }), "/")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{"/a/b/c/"}), "/a/b/c")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/a/b", "c" }), "/a/b/c")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }), "/a")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/", "..", ".." }), "/")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{"/a/b/c/"}), "/a/b/c")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/var/lib", "../", "file/" }), "/var/file")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/var/lib", "/../", "file/" }), "/file")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/some/dir", ".", "/absolute/" }), "/absolute")); - testing.expect(mem.eql(u8, testResolvePosix([_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }), "/foo/tmp.3/cycles/root.js")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/var/lib", "../", "file/" }), "/var/file")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }), "/file")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }), "/absolute")); + testing.expect(mem.eql(u8, testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }), "/foo/tmp.3/cycles/root.js")); } fn testResolveWindows(paths: []const []const u8) []u8 { @@ -716,7 +748,7 @@ fn testResolvePosix(paths: []const []const u8) []u8 { /// If the path is a file in the current directory (no directory component) /// then returns null pub fn dirname(path: []const u8) ?[]const u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return dirnameWindows(path); } else { return dirnamePosix(path); @@ -848,7 +880,7 @@ fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void { } pub fn basename(path: []const u8) []const u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return basenameWindows(path); } else { return basenamePosix(path); @@ -857,12 +889,12 @@ pub fn basename(path: []const u8) []const u8 { pub fn basenamePosix(path: []const u8) []const u8 { if (path.len == 0) - return [_]u8{}; + return &[_]u8{}; var end_index: usize = path.len - 1; while (path[end_index] == '/') { if (end_index == 0) - return [_]u8{}; + return &[_]u8{}; end_index -= 1; } var start_index: usize = end_index; @@ -878,19 +910,19 @@ pub fn basenamePosix(path: []const u8) []const u8 { pub fn basenameWindows(path: []const u8) []const u8 { if (path.len == 0) - return [_]u8{}; + return &[_]u8{}; var end_index: usize = path.len - 1; while (true) { const byte = path[end_index]; if (byte == '/' or byte == '\\') { if (end_index == 0) - return [_]u8{}; + return &[_]u8{}; end_index -= 1; continue; } if (byte == ':' and end_index == 1) { - return [_]u8{}; + return &[_]u8{}; } break; } @@ -964,7 +996,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return relativeWindows(allocator, from, to); } else { return relativePosix(allocator, from, to); @@ -972,11 +1004,11 @@ pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { } pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { - const resolved_from = try resolveWindows(allocator, [_][]const u8{from}); + const resolved_from = try resolveWindows(allocator, &[_][]const u8{from}); defer allocator.free(resolved_from); var clean_up_resolved_to = true; - const resolved_to = try resolveWindows(allocator, [_][]const u8{to}); + const resolved_to = try resolveWindows(allocator, &[_][]const u8{to}); defer if (clean_up_resolved_to) allocator.free(resolved_to); const parsed_from = windowsParsePath(resolved_from); @@ -1045,10 +1077,10 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) } pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { - const resolved_from = try resolvePosix(allocator, [_][]const u8{from}); + const resolved_from = try resolvePosix(allocator, &[_][]const u8{from}); defer allocator.free(resolved_from); - const resolved_to = try resolvePosix(allocator, [_][]const u8{to}); + const resolved_to = try resolvePosix(allocator, &[_][]const u8{to}); defer allocator.free(resolved_to); var from_it = mem.tokenize(resolved_from, "/"); diff --git a/lib/std/hash.zig b/lib/std/hash.zig index ab3a0ea8f..c51353b32 100644 --- a/lib/std/hash.zig +++ b/lib/std/hash.zig @@ -3,6 +3,8 @@ pub const Adler32 = adler.Adler32; const auto_hash = @import("hash/auto_hash.zig"); pub const autoHash = auto_hash.autoHash; +pub const autoHashStrat = auto_hash.hash; +pub const Strategy = auto_hash.HashStrategy; // pub for polynomials + generic crc32 construction pub const crc = @import("hash/crc.zig"); diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 3a4abcbcd..e60a1c105 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -22,7 +22,7 @@ pub const HashStrategy = enum { /// Helper function to hash a pointer and mutate the strategy if needed. pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { - const info = @typeInfo(@typeOf(key)); + const info = @typeInfo(@TypeOf(key)); switch (info.Pointer.size) { builtin.TypeInfo.Pointer.Size.One => switch (strat) { @@ -74,12 +74,11 @@ pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void { /// Provides generic hashing for any eligible type. /// Strategy is provided to determine if pointers should be followed or not. pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { - const Key = @typeOf(key); + const Key = @TypeOf(key); switch (@typeInfo(Key)) { .NoReturn, .Opaque, .Undefined, - .ArgTuple, .Void, .Null, .BoundFn, @@ -92,7 +91,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { // Help the optimizer see that hashing an int is easy by inlining! // TODO Check if the situation is better after #561 is resolved. - .Int => @inlineCall(hasher.update, std.mem.asBytes(&key)), + .Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}), .Float => |info| hash(hasher, @bitCast(@IntType(false, info.bits), key), strat), @@ -101,7 +100,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { .ErrorSet => hash(hasher, @errorToInt(key), strat), .AnyFrame, .Fn => hash(hasher, @ptrToInt(key), strat), - .Pointer => @inlineCall(hashPointer, hasher, key, strat), + .Pointer => @call(.{ .modifier = .always_inline }, hashPointer, .{ hasher, key, strat }), .Optional => if (key) |k| hash(hasher, k, strat), @@ -165,7 +164,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { /// Only hashes `key` itself, pointers are not followed. /// Slices are rejected to avoid ambiguity on the user's intention. pub fn autoHash(hasher: var, key: var) void { - const Key = @typeOf(key); + const Key = @TypeOf(key); if (comptime meta.trait.isSlice(Key)) { comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated const extra_help = if (Key == []const u8) @@ -235,8 +234,8 @@ test "hash pointer" { test "hash slice shallow" { // Allocate one array dynamically so that we're assured it is not merged // with the other by the optimization passes. - const array1 = try std.heap.direct_allocator.create([6]u32); - defer std.heap.direct_allocator.destroy(array1); + const array1 = try std.heap.page_allocator.create([6]u32); + defer std.heap.page_allocator.destroy(array1); array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 }; const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 }; const a = array1[0..]; @@ -251,8 +250,8 @@ test "hash slice shallow" { test "hash slice deep" { // Allocate one array dynamically so that we're assured it is not merged // with the other by the optimization passes. - const array1 = try std.heap.direct_allocator.create([6]u32); - defer std.heap.direct_allocator.destroy(array1); + const array1 = try std.heap.page_allocator.create([6]u32); + defer std.heap.page_allocator.destroy(array1); array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 }; const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 }; const a = array1[0..]; @@ -279,7 +278,7 @@ test "hash struct deep" { } }; - const allocator = std.heap.direct_allocator; + const allocator = std.heap.page_allocator; const foo = try Foo.init(allocator, 123, 1.0, true); const bar = try Foo.init(allocator, 123, 1.0, true); const baz = try Foo.init(allocator, 123, 1.0, false); @@ -306,7 +305,7 @@ test "hash struct deep" { test "testHash optional" { const a: ?u32 = 123; const b: ?u32 = null; - testing.expectEqual(testHash(a), testHash(u32(123))); + testing.expectEqual(testHash(a), testHash(@as(u32, 123))); testing.expect(testHash(a) != testHash(b)); testing.expectEqual(testHash(b), 0); } @@ -315,9 +314,9 @@ test "testHash array" { const a = [_]u32{ 1, 2, 3 }; const h = testHash(a); var hasher = Wyhash.init(0); - autoHash(&hasher, u32(1)); - autoHash(&hasher, u32(2)); - autoHash(&hasher, u32(3)); + autoHash(&hasher, @as(u32, 1)); + autoHash(&hasher, @as(u32, 2)); + autoHash(&hasher, @as(u32, 3)); testing.expectEqual(h, hasher.final()); } @@ -330,9 +329,9 @@ test "testHash struct" { const f = Foo{}; const h = testHash(f); var hasher = Wyhash.init(0); - autoHash(&hasher, u32(1)); - autoHash(&hasher, u32(2)); - autoHash(&hasher, u32(3)); + autoHash(&hasher, @as(u32, 1)); + autoHash(&hasher, @as(u32, 2)); + autoHash(&hasher, @as(u32, 3)); testing.expectEqual(h, hasher.final()); } diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index f15b81d7c..ce9ed75b5 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -164,24 +164,15 @@ fn usage() void { \\ --iterative-only \\ --help \\ - ); + , .{}); } fn mode(comptime x: comptime_int) comptime_int { return if (builtin.mode == builtin.Mode.Debug) x / 64 else x; } -// TODO(#1358): Replace with builtin formatted padding when available. -fn printPad(stdout: var, s: []const u8) !void { - var i: usize = 0; - while (i < 12 - s.len) : (i += 1) { - try stdout.print(" "); - } - try stdout.print("{}", s); -} - pub fn main() !void { - var stdout_file = try std.io.getStdOut(); + var stdout_file = std.io.getStdOut(); var stdout_out_stream = stdout_file.outStream(); const stdout = &stdout_out_stream.stream; @@ -198,7 +189,7 @@ pub fn main() !void { var i: usize = 1; while (i < args.len) : (i += 1) { if (std.mem.eql(u8, args[i], "--mode")) { - try stdout.print("{}\n", builtin.mode); + try stdout.print("{}\n", .{builtin.mode}); return; } else if (std.mem.eql(u8, args[i], "--seed")) { i += 1; @@ -235,7 +226,7 @@ pub fn main() !void { key_size = try std.fmt.parseUnsigned(usize, args[i], 10); if (key_size > block_size) { - try stdout.print("key_size cannot exceed block size of {}\n", block_size); + try stdout.print("key_size cannot exceed block size of {}\n", .{block_size}); std.os.exit(1); } } else if (std.mem.eql(u8, args[i], "--iterative-only")) { @@ -252,20 +243,20 @@ pub fn main() !void { inline for (hashes) |H| { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { if (!test_iterative_only or H.has_iterative_api) { - try stdout.print("{}\n", H.name); + try stdout.print("{}\n", .{H.name}); // Always reseed prior to every call so we are hashing the same buffer contents. // This allows easier comparison between different implementations. if (H.has_iterative_api) { prng.seed(seed); const result = try benchmarkHash(H, count); - try stdout.print(" iterative: {:4} MiB/s [{x:0<16}]\n", result.throughput / (1 * MiB), result.hash); + try stdout.print(" iterative: {:4} MiB/s [{x:0<16}]\n", .{result.throughput / (1 * MiB), result.hash}); } if (!test_iterative_only) { prng.seed(seed); const result_small = try benchmarkHashSmallKeys(H, key_size, count); - try stdout.print(" small keys: {:4} MiB/s [{x:0<16}]\n", result_small.throughput / (1 * MiB), result_small.hash); + try stdout.print(" small keys: {:4} MiB/s [{x:0<16}]\n", .{result_small.throughput / (1 * MiB), result_small.hash}); } } } diff --git a/lib/std/hash/cityhash.zig b/lib/std/hash/cityhash.zig index 43e5b7a38..18b3abe16 100644 --- a/lib/std/hash/cityhash.zig +++ b/lib/std/hash/cityhash.zig @@ -197,7 +197,7 @@ pub const CityHash64 = struct { } fn hashLen16(u: u64, v: u64) u64 { - return @inlineCall(hash128To64, u, v); + return @call(.{ .modifier = .always_inline }, hash128To64, .{ u, v }); } fn hashLen16Mul(low: u64, high: u64, mul: u64) u64 { @@ -210,11 +210,11 @@ pub const CityHash64 = struct { } fn hash128To64(low: u64, high: u64) u64 { - return @inlineCall(hashLen16Mul, low, high, 0x9ddfea08eb382d69); + return @call(.{ .modifier = .always_inline }, hashLen16Mul, .{ low, high, 0x9ddfea08eb382d69 }); } fn hashLen0To16(str: []const u8) u64 { - const len: u64 = u64(str.len); + const len: u64 = @as(u64, str.len); if (len >= 8) { const mul: u64 = k2 +% len *% 2; const a: u64 = fetch64(str.ptr) +% k2; @@ -240,7 +240,7 @@ pub const CityHash64 = struct { } fn hashLen17To32(str: []const u8) u64 { - const len: u64 = u64(str.len); + const len: u64 = @as(u64, str.len); const mul: u64 = k2 +% len *% 2; const a: u64 = fetch64(str.ptr) *% k1; const b: u64 = fetch64(str.ptr + 8); @@ -251,7 +251,7 @@ pub const CityHash64 = struct { } fn hashLen33To64(str: []const u8) u64 { - const len: u64 = u64(str.len); + const len: u64 = @as(u64, str.len); const mul: u64 = k2 +% len *% 2; const a: u64 = fetch64(str.ptr) *% k2; const b: u64 = fetch64(str.ptr + 8); @@ -291,7 +291,14 @@ pub const CityHash64 = struct { } fn weakHashLen32WithSeeds(ptr: [*]const u8, a: u64, b: u64) WeakPair { - return @inlineCall(weakHashLen32WithSeedsHelper, fetch64(ptr), fetch64(ptr + 8), fetch64(ptr + 16), fetch64(ptr + 24), a, b); + return @call(.{ .modifier = .always_inline }, weakHashLen32WithSeedsHelper, .{ + fetch64(ptr), + fetch64(ptr + 8), + fetch64(ptr + 16), + fetch64(ptr + 24), + a, + b, + }); } pub fn hash(str: []const u8) u64 { @@ -305,7 +312,7 @@ pub const CityHash64 = struct { return hashLen33To64(str); } - var len: u64 = u64(str.len); + var len: u64 = @as(u64, str.len); var x: u64 = fetch64(str.ptr + str.len - 40); var y: u64 = fetch64(str.ptr + str.len - 16) +% fetch64(str.ptr + str.len - 56); @@ -339,7 +346,7 @@ pub const CityHash64 = struct { } pub fn hashWithSeed(str: []const u8, seed: u64) u64 { - return @inlineCall(Self.hashWithSeeds, str, k2, seed); + return @call(.{ .modifier = .always_inline }, Self.hashWithSeeds, .{ str, k2, seed }); } pub fn hashWithSeeds(str: []const u8, seed0: u64, seed1: u64) u64 { @@ -353,9 +360,9 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { var hashes: [hashbytes * 256]u8 = undefined; var final: [hashbytes]u8 = undefined; - @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@typeOf(key))); - @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@typeOf(hashes))); - @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@typeOf(final))); + @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key))); + @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes))); + @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final))); var i: u32 = 0; while (i < 256) : (i += 1) { @@ -363,11 +370,11 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { var h = hash_fn(key[0..i], 256 - i); if (builtin.endian == builtin.Endian.Big) - h = @byteSwap(@typeOf(h), h); + h = @byteSwap(@TypeOf(h), h); @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes); } - return @truncate(u32, hash_fn(hashes, 0)); + return @truncate(u32, hash_fn(&hashes, 0)); } fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 { diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index cdcaf5561..6176ded81 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -65,10 +65,10 @@ pub fn Crc32WithPoly(comptime poly: u32) type { const p = input[i .. i + 8]; // Unrolling this way gives ~50Mb/s increase - self.crc ^= (u32(p[0]) << 0); - self.crc ^= (u32(p[1]) << 8); - self.crc ^= (u32(p[2]) << 16); - self.crc ^= (u32(p[3]) << 24); + self.crc ^= (@as(u32, p[0]) << 0); + self.crc ^= (@as(u32, p[1]) << 8); + self.crc ^= (@as(u32, p[2]) << 16); + self.crc ^= (@as(u32, p[3]) << 24); self.crc = lookup_tables[0][p[7]] ^ diff --git a/lib/std/hash/murmur.zig b/lib/std/hash/murmur.zig index a0c8f9133..f6c0ccafb 100644 --- a/lib/std/hash/murmur.zig +++ b/lib/std/hash/murmur.zig @@ -8,7 +8,7 @@ pub const Murmur2_32 = struct { const Self = @This(); pub fn hash(str: []const u8) u32 { - return @inlineCall(Self.hashWithSeed, str, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashWithSeed, .{ str, default_seed }); } pub fn hashWithSeed(str: []const u8, seed: u32) u32 { @@ -44,7 +44,7 @@ pub const Murmur2_32 = struct { } pub fn hashUint32(v: u32) u32 { - return @inlineCall(Self.hashUint32WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint32WithSeed, .{ v, default_seed }); } pub fn hashUint32WithSeed(v: u32, seed: u32) u32 { @@ -64,7 +64,7 @@ pub const Murmur2_32 = struct { } pub fn hashUint64(v: u64) u32 { - return @inlineCall(Self.hashUint64WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint64WithSeed, .{ v, default_seed }); } pub fn hashUint64WithSeed(v: u64, seed: u32) u32 { @@ -93,12 +93,12 @@ pub const Murmur2_64 = struct { const Self = @This(); pub fn hash(str: []const u8) u64 { - return @inlineCall(Self.hashWithSeed, str, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashWithSeed, .{ str, default_seed }); } pub fn hashWithSeed(str: []const u8, seed: u64) u64 { const m: u64 = 0xc6a4a7935bd1e995; - const len = u64(str.len); + const len = @as(u64, str.len); var h1: u64 = seed ^ (len *% m); for (@ptrCast([*]allowzero align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| { var k1: u64 = v; @@ -127,7 +127,7 @@ pub const Murmur2_64 = struct { } pub fn hashUint32(v: u32) u64 { - return @inlineCall(Self.hashUint32WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint32WithSeed, .{ v, default_seed }); } pub fn hashUint32WithSeed(v: u32, seed: u32) u64 { @@ -144,7 +144,7 @@ pub const Murmur2_64 = struct { } pub fn hashUint64(v: u64) u64 { - return @inlineCall(Self.hashUint64WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint64WithSeed, .{ v, default_seed }); } pub fn hashUint64WithSeed(v: u64, seed: u32) u64 { @@ -172,7 +172,7 @@ pub const Murmur3_32 = struct { } pub fn hash(str: []const u8) u32 { - return @inlineCall(Self.hashWithSeed, str, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashWithSeed, .{ str, default_seed }); } pub fn hashWithSeed(str: []const u8, seed: u32) u32 { @@ -220,7 +220,7 @@ pub const Murmur3_32 = struct { } pub fn hashUint32(v: u32) u32 { - return @inlineCall(Self.hashUint32WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint32WithSeed, .{ v, default_seed }); } pub fn hashUint32WithSeed(v: u32, seed: u32) u32 { @@ -246,7 +246,7 @@ pub const Murmur3_32 = struct { } pub fn hashUint64(v: u64) u32 { - return @inlineCall(Self.hashUint64WithSeed, v, default_seed); + return @call(.{ .modifier = .always_inline }, Self.hashUint64WithSeed, .{ v, default_seed }); } pub fn hashUint64WithSeed(v: u64, seed: u32) u32 { @@ -285,9 +285,9 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { var hashes: [hashbytes * 256]u8 = undefined; var final: [hashbytes]u8 = undefined; - @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@typeOf(key))); - @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@typeOf(hashes))); - @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@typeOf(final))); + @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key))); + @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes))); + @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final))); var i: u32 = 0; while (i < 256) : (i += 1) { @@ -295,11 +295,11 @@ fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { var h = hash_fn(key[0..i], 256 - i); if (builtin.endian == builtin.Endian.Big) - h = @byteSwap(@typeOf(h), h); + h = @byteSwap(@TypeOf(h), h); @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes); } - return @truncate(u32, hash_fn(hashes, 0)); + return @truncate(u32, hash_fn(&hashes, 0)); } test "murmur2_32" { diff --git a/lib/std/hash/siphash.zig b/lib/std/hash/siphash.zig index 3d67ba685..ccef47c4b 100644 --- a/lib/std/hash/siphash.zig +++ b/lib/std/hash/siphash.zig @@ -11,7 +11,7 @@ const testing = std.testing; const math = std.math; const mem = std.mem; -const Endian = @import("builtin").Endian; +const Endian = std.builtin.Endian; pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type { return SipHash(u64, c_rounds, d_rounds); @@ -62,7 +62,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round var off: usize = 0; while (off < b.len) : (off += 8) { - @inlineCall(self.round, b[off .. off + 8]); + @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 8]}); } self.msg_len +%= @truncate(u8, b.len); @@ -84,9 +84,12 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round self.v2 ^= 0xff; } + // TODO this is a workaround, should be able to supply the value without a separate variable + const inl = std.builtin.CallOptions{ .modifier = .always_inline }; + comptime var i: usize = 0; inline while (i < d_rounds) : (i += 1) { - @inlineCall(sipRound, self); + @call(inl, sipRound, .{self}); } const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; @@ -98,11 +101,11 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round comptime var j: usize = 0; inline while (j < d_rounds) : (j += 1) { - @inlineCall(sipRound, self); + @call(inl, sipRound, .{self}); } const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3; - return (u128(b2) << 64) | b1; + return (@as(u128, b2) << 64) | b1; } fn round(self: *Self, b: []const u8) void { @@ -111,9 +114,11 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round const m = mem.readIntSliceLittle(u64, b[0..]); self.v3 ^= m; + // TODO this is a workaround, should be able to supply the value without a separate variable + const inl = std.builtin.CallOptions{ .modifier = .always_inline }; comptime var i: usize = 0; inline while (i < c_rounds) : (i += 1) { - @inlineCall(sipRound, self); + @call(inl, sipRound, .{self}); } self.v0 ^= m; @@ -121,27 +126,27 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round fn sipRound(d: *Self) void { d.v0 +%= d.v1; - d.v1 = math.rotl(u64, d.v1, u64(13)); + d.v1 = math.rotl(u64, d.v1, @as(u64, 13)); d.v1 ^= d.v0; - d.v0 = math.rotl(u64, d.v0, u64(32)); + d.v0 = math.rotl(u64, d.v0, @as(u64, 32)); d.v2 +%= d.v3; - d.v3 = math.rotl(u64, d.v3, u64(16)); + d.v3 = math.rotl(u64, d.v3, @as(u64, 16)); d.v3 ^= d.v2; d.v0 +%= d.v3; - d.v3 = math.rotl(u64, d.v3, u64(21)); + d.v3 = math.rotl(u64, d.v3, @as(u64, 21)); d.v3 ^= d.v0; d.v2 +%= d.v1; - d.v1 = math.rotl(u64, d.v1, u64(17)); + d.v1 = math.rotl(u64, d.v1, @as(u64, 17)); d.v1 ^= d.v2; - d.v2 = math.rotl(u64, d.v2, u64(32)); + d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); } pub fn hash(key: []const u8, input: []const u8) T { const aligned_len = input.len - (input.len % 8); var c = Self.init(key); - @inlineCall(c.update, input[0..aligned_len]); - return @inlineCall(c.final, input[aligned_len..]); + @call(.{ .modifier = .always_inline }, c.update, .{input[0..aligned_len]}); + return @call(.{ .modifier = .always_inline }, c.final, .{input[aligned_len..]}); } }; } @@ -202,70 +207,70 @@ const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x test "siphash64-2-4 sanity" { const vectors = [_][8]u8{ - "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72", // "" - "\xfd\x67\xdc\x93\xc5\x39\xf8\x74", // "\x00" - "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d", // "\x00\x01" ... etc - "\x2d\x7e\xfb\xd7\x96\x66\x67\x85", - "\xb7\x87\x71\x27\xe0\x94\x27\xcf", - "\x8d\xa6\x99\xcd\x64\x55\x76\x18", - "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb", - "\x37\xd1\x01\x8b\xf5\x00\x02\xab", - "\x62\x24\x93\x9a\x79\xf5\xf5\x93", - "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e", - "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a", - "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4", - "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75", - "\x90\x3d\x84\xc0\x27\x56\xea\x14", - "\xee\xf2\x7a\x8e\x90\xca\x23\xf7", - "\xe5\x45\xbe\x49\x61\xca\x29\xa1", - "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f", - "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69", - "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b", - "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb", - "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe", - "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0", - "\x88\x3e\xa3\xe3\x95\x67\x53\x93", - "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8", - "\x94\xaf\x49\xf6\xc6\x50\xad\xb8", - "\xea\xb8\x85\x8a\xde\x92\xe1\xbc", - "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17", - "\xad\xcf\x6b\x07\x63\x61\x2e\x2f", - "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde", - "\x71\x65\x95\x87\x66\x50\xa2\xa6", - "\x28\xef\x49\x5c\x53\xa3\x87\xad", - "\x42\xc3\x41\xd8\xfa\x92\xd8\x32", - "\xce\x7c\xf2\x72\x2f\x51\x27\x71", - "\xe3\x78\x59\xf9\x46\x23\xf3\xa7", - "\x38\x12\x05\xbb\x1a\xb0\xe0\x12", - "\xae\x97\xa1\x0f\xd4\x34\xe0\x15", - "\xb4\xa3\x15\x08\xbe\xff\x4d\x31", - "\x81\x39\x62\x29\xf0\x90\x79\x02", - "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca", - "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a", - "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e", - "\x92\x59\x58\xfc\xd6\x42\x0c\xad", - "\xa9\x15\xc2\x9b\xc8\x06\x73\x18", - "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4", - "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9", - "\x87\x57\x75\x19\x04\x8f\x53\xa9", - "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb", - "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0", - "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6", - "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7", - "\x72\xfe\x52\x97\x5a\x43\x64\xee", - "\x5a\x16\x45\xb2\x76\xd5\x92\xa1", - "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a", - "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81", - "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f", - "\x99\x24\xa4\x3c\xc1\x31\x57\x24", - "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7", - "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea", - "\x13\x50\x79\xa3\x23\x1c\xe6\x60", - "\x93\x2b\x28\x46\xe4\xd7\x06\x66", - "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c", - "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f", - "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5", - "\x72\x45\x06\xeb\x4c\x32\x8a\x95", + "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72".*, // "" + "\xfd\x67\xdc\x93\xc5\x39\xf8\x74".*, // "\x00" + "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d".*, // "\x00\x01" ... etc + "\x2d\x7e\xfb\xd7\x96\x66\x67\x85".*, + "\xb7\x87\x71\x27\xe0\x94\x27\xcf".*, + "\x8d\xa6\x99\xcd\x64\x55\x76\x18".*, + "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb".*, + "\x37\xd1\x01\x8b\xf5\x00\x02\xab".*, + "\x62\x24\x93\x9a\x79\xf5\xf5\x93".*, + "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e".*, + "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a".*, + "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4".*, + "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75".*, + "\x90\x3d\x84\xc0\x27\x56\xea\x14".*, + "\xee\xf2\x7a\x8e\x90\xca\x23\xf7".*, + "\xe5\x45\xbe\x49\x61\xca\x29\xa1".*, + "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f".*, + "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69".*, + "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b".*, + "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb".*, + "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe".*, + "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0".*, + "\x88\x3e\xa3\xe3\x95\x67\x53\x93".*, + "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8".*, + "\x94\xaf\x49\xf6\xc6\x50\xad\xb8".*, + "\xea\xb8\x85\x8a\xde\x92\xe1\xbc".*, + "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17".*, + "\xad\xcf\x6b\x07\x63\x61\x2e\x2f".*, + "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde".*, + "\x71\x65\x95\x87\x66\x50\xa2\xa6".*, + "\x28\xef\x49\x5c\x53\xa3\x87\xad".*, + "\x42\xc3\x41\xd8\xfa\x92\xd8\x32".*, + "\xce\x7c\xf2\x72\x2f\x51\x27\x71".*, + "\xe3\x78\x59\xf9\x46\x23\xf3\xa7".*, + "\x38\x12\x05\xbb\x1a\xb0\xe0\x12".*, + "\xae\x97\xa1\x0f\xd4\x34\xe0\x15".*, + "\xb4\xa3\x15\x08\xbe\xff\x4d\x31".*, + "\x81\x39\x62\x29\xf0\x90\x79\x02".*, + "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca".*, + "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a".*, + "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e".*, + "\x92\x59\x58\xfc\xd6\x42\x0c\xad".*, + "\xa9\x15\xc2\x9b\xc8\x06\x73\x18".*, + "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4".*, + "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9".*, + "\x87\x57\x75\x19\x04\x8f\x53\xa9".*, + "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb".*, + "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0".*, + "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6".*, + "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7".*, + "\x72\xfe\x52\x97\x5a\x43\x64\xee".*, + "\x5a\x16\x45\xb2\x76\xd5\x92\xa1".*, + "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a".*, + "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81".*, + "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f".*, + "\x99\x24\xa4\x3c\xc1\x31\x57\x24".*, + "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7".*, + "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea".*, + "\x13\x50\x79\xa3\x23\x1c\xe6\x60".*, + "\x93\x2b\x28\x46\xe4\xd7\x06\x66".*, + "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c".*, + "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f".*, + "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5".*, + "\x72\x45\x06\xeb\x4c\x32\x8a\x95".*, }; const siphash = SipHash64(2, 4); @@ -281,70 +286,70 @@ test "siphash64-2-4 sanity" { test "siphash128-2-4 sanity" { const vectors = [_][16]u8{ - "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93", - "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45", - "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4", - "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51", - "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79", - "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27", - "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e", - "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39", - "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4", - "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed", - "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba", - "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18", - "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25", - "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7", - "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02", - "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9", - "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77", - "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40", - "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23", - "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1", - "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb", - "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12", - "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae", - "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c", - "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad", - "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f", - "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66", - "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94", - "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4", - "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7", - "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87", - "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35", - "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68", - "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf", - "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde", - "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8", - "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11", - "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b", - "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5", - "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9", - "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8", - "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb", - "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b", - "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89", - "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42", - "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c", - "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02", - "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b", - "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16", - "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03", - "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f", - "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38", - "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c", - "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e", - "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87", - "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda", - "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36", - "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e", - "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d", - "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59", - "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40", - "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a", - "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd", - "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c", + "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93".*, + "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45".*, + "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4".*, + "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51".*, + "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79".*, + "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27".*, + "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e".*, + "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39".*, + "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4".*, + "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed".*, + "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba".*, + "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18".*, + "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25".*, + "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7".*, + "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02".*, + "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9".*, + "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77".*, + "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40".*, + "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23".*, + "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1".*, + "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb".*, + "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12".*, + "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae".*, + "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c".*, + "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad".*, + "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f".*, + "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66".*, + "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94".*, + "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4".*, + "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7".*, + "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87".*, + "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35".*, + "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68".*, + "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf".*, + "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde".*, + "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8".*, + "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11".*, + "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b".*, + "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5".*, + "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9".*, + "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8".*, + "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb".*, + "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b".*, + "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89".*, + "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42".*, + "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c".*, + "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02".*, + "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b".*, + "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16".*, + "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03".*, + "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f".*, + "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38".*, + "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c".*, + "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e".*, + "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87".*, + "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda".*, + "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36".*, + "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e".*, + "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d".*, + "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59".*, + "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40".*, + "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a".*, + "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd".*, + "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c".*, }; const siphash = SipHash128(2, 4); diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig index 7e35ccc6d..8d11c700c 100644 --- a/lib/std/hash/wyhash.zig +++ b/lib/std/hash/wyhash.zig @@ -65,7 +65,7 @@ const WyhashStateless = struct { var off: usize = 0; while (off < b.len) : (off += 32) { - @inlineCall(self.round, b[off .. off + 32]); + @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 32]}); } self.msg_len += b.len; @@ -121,8 +121,8 @@ const WyhashStateless = struct { const aligned_len = input.len - (input.len % 32); var c = WyhashStateless.init(seed); - @inlineCall(c.update, input[0..aligned_len]); - return @inlineCall(c.final, input[aligned_len..]); + @call(.{ .modifier = .always_inline }, c.update, .{input[0..aligned_len]}); + return @call(.{ .modifier = .always_inline }, c.final, .{input[aligned_len..]}); } }; diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 4ffe88067..57a2f58b9 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -36,11 +36,17 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 size: usize, max_distance_from_start_index: usize, allocator: *Allocator, - // this is used to detect bugs where a hashtable is edited while an iterator is running. + + /// This is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, const Self = @This(); + /// A *KV is a mutable pointer into this HashMap's internal storage. + /// Modifying the key is undefined behavior. + /// Modifying the value is harmless. + /// *KV pointers become invalid whenever this HashMap is modified, + /// and then any access to the *KV is undefined behavior. pub const KV = struct { key: K, value: V, @@ -93,7 +99,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 pub fn init(allocator: *Allocator) Self { return Self{ - .entries = [_]Entry{}, + .entries = &[_]Entry{}, .allocator = allocator, .size = 0, .max_distance_from_start_index = 0, @@ -401,7 +407,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } fn keyToIndex(hm: Self, key: K) usize { - return hm.constrainIndex(usize(hash(key))); + return hm.constrainIndex(@as(usize, hash(key))); } fn constrainIndex(hm: Self, i: usize) usize { @@ -413,7 +419,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } test "basic hash map usage" { - var map = AutoHashMap(i32, i32).init(std.heap.direct_allocator); + var map = AutoHashMap(i32, i32).init(std.heap.page_allocator); defer map.deinit(); testing.expect((try map.put(1, 11)) == null); @@ -457,7 +463,7 @@ test "basic hash map usage" { } test "iterator hash map" { - var reset_map = AutoHashMap(i32, i32).init(std.heap.direct_allocator); + var reset_map = AutoHashMap(i32, i32).init(std.heap.page_allocator); defer reset_map.deinit(); try reset_map.putNoClobber(1, 11); @@ -503,7 +509,7 @@ test "iterator hash map" { } test "ensure capacity" { - var map = AutoHashMap(i32, i32).init(std.heap.direct_allocator); + var map = AutoHashMap(i32, i32).init(std.heap.page_allocator); defer map.deinit(); try map.ensureCapacity(20); @@ -550,3 +556,13 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { } }.eql; } + +pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { + return struct { + fn hash(key: K) u32 { + var hasher = Wyhash.init(0); + std.hash.autoHashStrat(&hasher, key, strategy); + return @truncate(u32, hasher.final()); + } + }.hash; +} diff --git a/lib/std/heap.zig b/lib/std/heap.zig index b968b6242..f25d2f017 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -1,4 +1,5 @@ const std = @import("std.zig"); +const root = @import("root"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; @@ -33,18 +34,30 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new /// This allocator makes a syscall directly for every allocation and free. /// Thread-safe and lock-free. -pub const direct_allocator = &direct_allocator_state; -var direct_allocator_state = Allocator{ - .reallocFn = DirectAllocator.realloc, - .shrinkFn = DirectAllocator.shrink, +pub const page_allocator = if (std.Target.current.isWasm()) + &wasm_page_allocator_state +else if (std.Target.current.getOs() == .freestanding) + root.os.heap.page_allocator +else + &page_allocator_state; + +var page_allocator_state = Allocator{ + .reallocFn = PageAllocator.realloc, + .shrinkFn = PageAllocator.shrink, +}; +var wasm_page_allocator_state = Allocator{ + .reallocFn = WasmPageAllocator.realloc, + .shrinkFn = WasmPageAllocator.shrink, }; -const DirectAllocator = struct { - fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { - if (n == 0) - return (([*]u8)(undefined))[0..0]; +/// Deprecated. Use `page_allocator`. +pub const direct_allocator = page_allocator; - if (os.windows.is_the_target) { +const PageAllocator = struct { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { + if (n == 0) return &[0]u8{}; + + if (builtin.os == .windows) { const w = os.windows; // Although officially it's at least aligned to page boundary, @@ -130,7 +143,7 @@ const DirectAllocator = struct { fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (os.windows.is_the_target) { + if (builtin.os == .windows) { const w = os.windows; if (new_size == 0) { // From the docs: @@ -170,7 +183,7 @@ const DirectAllocator = struct { fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { const old_mem = @alignCast(mem.page_size, old_mem_unaligned); - if (os.windows.is_the_target) { + if (builtin.os == .windows) { if (old_mem.len == 0) { return alloc(allocator, new_size, new_align); } @@ -236,6 +249,169 @@ const DirectAllocator = struct { } }; +// TODO Exposed LLVM intrinsics is a bug +// See: https://github.com/ziglang/zig/issues/2291 +extern fn @"llvm.wasm.memory.size.i32"(u32) u32; +extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; + +const WasmPageAllocator = struct { + comptime { + if (!std.Target.current.isWasm()) { + @compileError("WasmPageAllocator is only available for wasm32 arch"); + } + } + + const PageStatus = enum(u1) { + used = 0, + free = 1, + + pub const none_free: u8 = 0; + }; + + const FreeBlock = struct { + data: []u128, + + const Io = std.packed_int_array.PackedIntIo(u1, .Little); + + fn totalPages(self: FreeBlock) usize { + return self.data.len * 128; + } + + fn isInitialized(self: FreeBlock) bool { + return self.data.len > 0; + } + + fn getBit(self: FreeBlock, idx: usize) PageStatus { + const bit_offset = 0; + return @intToEnum(PageStatus, Io.get(@sliceToBytes(self.data), idx, bit_offset)); + } + + fn setBits(self: FreeBlock, start_idx: usize, len: usize, val: PageStatus) void { + const bit_offset = 0; + var i: usize = 0; + while (i < len) : (i += 1) { + Io.set(@sliceToBytes(self.data), start_idx + i, bit_offset, @enumToInt(val)); + } + } + + // Use '0xFFFFFFFF' as a _missing_ sentinel + // This saves ~50 bytes compared to returning a nullable + + // We can guarantee that conventional memory never gets this big, + // and wasm32 would not be able to address this memory (32 GB > usize). + + // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806 + const not_found = std.math.maxInt(usize); + + fn useRecycled(self: FreeBlock, num_pages: usize) usize { + @setCold(true); + for (self.data) |segment, i| { + const spills_into_next = @bitCast(i128, segment) < 0; + const has_enough_bits = @popCount(u128, segment) >= num_pages; + + if (!spills_into_next and !has_enough_bits) continue; + + var j: usize = i * 128; + while (j < (i + 1) * 128) : (j += 1) { + var count: usize = 0; + while (j + count < self.totalPages() and self.getBit(j + count) == .free) { + count += 1; + if (count >= num_pages) { + self.setBits(j, num_pages, .used); + return j; + } + } + j += count; + } + } + return not_found; + } + + fn recycle(self: FreeBlock, start_idx: usize, len: usize) void { + self.setBits(start_idx, len, .free); + } + }; + + var _conventional_data = [_]u128{0} ** 16; + // Marking `conventional` as const saves ~40 bytes + const conventional = FreeBlock{ .data = &_conventional_data }; + var extended = FreeBlock{ .data = &[_]u128{} }; + + fn extendedOffset() usize { + return conventional.totalPages(); + } + + fn nPages(memsize: usize) usize { + return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size; + } + + fn alloc(allocator: *Allocator, page_count: usize, alignment: u29) error{OutOfMemory}!usize { + var idx = conventional.useRecycled(page_count); + if (idx != FreeBlock.not_found) { + return idx; + } + + idx = extended.useRecycled(page_count); + if (idx != FreeBlock.not_found) { + return idx + extendedOffset(); + } + + const prev_page_count = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, page_count)); + if (prev_page_count <= 0) { + return error.OutOfMemory; + } + + return @intCast(usize, prev_page_count); + } + + pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 { + if (new_align > std.mem.page_size) { + return error.OutOfMemory; + } + + if (nPages(new_size) == nPages(old_mem.len)) { + return old_mem.ptr[0..new_size]; + } else if (new_size < old_mem.len) { + return shrink(allocator, old_mem, old_align, new_size, new_align); + } else { + const page_idx = try alloc(allocator, nPages(new_size), new_align); + const new_mem = @intToPtr([*]u8, page_idx * std.mem.page_size)[0..new_size]; + std.mem.copy(u8, new_mem, old_mem); + _ = shrink(allocator, old_mem, old_align, 0, 0); + return new_mem; + } + } + + pub fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { + @setCold(true); + const free_start = nPages(@ptrToInt(old_mem.ptr) + new_size); + var free_end = nPages(@ptrToInt(old_mem.ptr) + old_mem.len); + + if (free_end > free_start) { + if (free_start < extendedOffset()) { + const clamped_end = std.math.min(extendedOffset(), free_end); + conventional.recycle(free_start, clamped_end - free_start); + } + + if (free_end > extendedOffset()) { + if (!extended.isInitialized()) { + // Steal the last page from the memory currently being recycled + // TODO: would it be better if we use the first page instead? + free_end -= 1; + + extended.data = @intToPtr([*]u128, free_end * std.mem.page_size)[0 .. std.mem.page_size / @sizeOf(u128)]; + // Since this is the first page being freed and we consume it, assume *nothing* is free. + std.mem.set(u128, extended.data, PageStatus.none_free); + } + const clamped_start = std.math.max(extendedOffset(), free_start); + extended.recycle(clamped_start - extendedOffset(), free_end - clamped_start); + } + } + + return old_mem[0..new_size]; + } +}; + pub const HeapAllocator = switch (builtin.os) { .windows => struct { allocator: Allocator, @@ -261,8 +437,7 @@ pub const HeapAllocator = switch (builtin.os) { fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { const self = @fieldParentPtr(HeapAllocator, "allocator", allocator); - if (n == 0) - return (([*]u8)(undefined))[0..0]; + if (n == 0) return &[0]u8{}; const amt = n + alignment + @sizeOf(usize); const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); @@ -338,7 +513,7 @@ pub const HeapAllocator = switch (builtin.os) { /// This allocator takes an existing allocator, wraps it, and provides an interface /// where you can allocate without freeing, and then free it all together. pub const ArenaAllocator = struct { - pub allocator: Allocator, + allocator: Allocator, child_allocator: *Allocator, buffer_list: std.SinglyLinkedList([]u8), @@ -486,103 +661,6 @@ pub const FixedBufferAllocator = struct { } }; -// FIXME: Exposed LLVM intrinsics is a bug -// See: https://github.com/ziglang/zig/issues/2291 -extern fn @"llvm.wasm.memory.size.i32"(u32) u32; -extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; - -pub const wasm_allocator = &wasm_allocator_state.allocator; -var wasm_allocator_state = WasmAllocator{ - .allocator = Allocator{ - .reallocFn = WasmAllocator.realloc, - .shrinkFn = WasmAllocator.shrink, - }, - .start_ptr = undefined, - .num_pages = 0, - .end_index = 0, -}; - -const WasmAllocator = struct { - allocator: Allocator, - start_ptr: [*]u8, - num_pages: usize, - end_index: usize, - - comptime { - if (builtin.arch != .wasm32) { - @compileError("WasmAllocator is only available for wasm32 arch"); - } - } - - fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 { - const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); - - const addr = @ptrToInt(self.start_ptr) + self.end_index; - const adjusted_addr = mem.alignForward(addr, alignment); - const adjusted_index = self.end_index + (adjusted_addr - addr); - const new_end_index = adjusted_index + size; - - if (new_end_index > self.num_pages * mem.page_size) { - const required_memory = new_end_index - (self.num_pages * mem.page_size); - - var num_pages: usize = required_memory / mem.page_size; - if (required_memory % mem.page_size != 0) { - num_pages += 1; - } - - const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); - if (prev_page == -1) { - return error.OutOfMemory; - } - - self.num_pages += num_pages; - } - - const result = self.start_ptr[adjusted_index..new_end_index]; - self.end_index = new_end_index; - - return result; - } - - // Check if memory is the last "item" and is aligned correctly - fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool { - const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); - return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr); - } - - fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { - const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); - - // Initialize start_ptr at the first realloc - if (self.num_pages == 0) { - self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); - } - - if (is_last_item(allocator, old_mem, new_align)) { - const start_index = self.end_index - old_mem.len; - const new_end_index = start_index + new_size; - - if (new_end_index > self.num_pages * mem.page_size) { - _ = try alloc(allocator, new_end_index - self.end_index, new_align); - } - const result = self.start_ptr[start_index..new_end_index]; - - self.end_index = new_end_index; - return result; - } else if (new_size <= old_mem.len and new_align <= old_align) { - return error.OutOfMemory; - } else { - const result = try alloc(allocator, new_size, new_align); - mem.copy(u8, result, old_mem); - return result; - } - } - - fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { - return old_mem[0..new_size]; - } -}; - pub const ThreadSafeFixedBufferAllocator = blk: { if (builtin.single_threaded) { break :blk FixedBufferAllocator; @@ -677,7 +755,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type { ) catch { const result = try self.fallback_allocator.reallocFn( self.fallback_allocator, - ([*]u8)(undefined)[0..0], + &[0]u8{}, undefined, new_size, new_align, @@ -727,17 +805,56 @@ test "c_allocator" { } } -test "DirectAllocator" { - const allocator = direct_allocator; +test "WasmPageAllocator internals" { + if (comptime std.Target.current.isWasm()) { + const conventional_memsize = WasmPageAllocator.conventional.totalPages() * std.mem.page_size; + const initial = try page_allocator.alloc(u8, std.mem.page_size); + 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. + + var inplace = try page_allocator.realloc(initial, 1); + testing.expectEqual(initial.ptr, inplace.ptr); + inplace = try page_allocator.realloc(inplace, 4); + testing.expectEqual(initial.ptr, inplace.ptr); + page_allocator.free(inplace); + + const reuse = try page_allocator.alloc(u8, 1); + testing.expectEqual(initial.ptr, reuse.ptr); + page_allocator.free(reuse); + + // This segment may span conventional and extended which has really complex rules so we're just ignoring it for now. + const padding = try page_allocator.alloc(u8, conventional_memsize); + page_allocator.free(padding); + + const extended = try page_allocator.alloc(u8, conventional_memsize); + testing.expect(@ptrToInt(extended.ptr) >= conventional_memsize); + + const use_small = try page_allocator.alloc(u8, 1); + testing.expectEqual(initial.ptr, use_small.ptr); + page_allocator.free(use_small); + + inplace = try page_allocator.realloc(extended, 1); + testing.expectEqual(extended.ptr, inplace.ptr); + page_allocator.free(inplace); + + const reuse_extended = try page_allocator.alloc(u8, conventional_memsize); + testing.expectEqual(extended.ptr, reuse_extended.ptr); + page_allocator.free(reuse_extended); + } +} + +test "PageAllocator" { + const allocator = page_allocator; try testAllocator(allocator); try testAllocatorAligned(allocator, 16); - try testAllocatorLargeAlignment(allocator); - try testAllocatorAlignedShrink(allocator); + if (!std.Target.current.isWasm()) { + try testAllocatorLargeAlignment(allocator); + try testAllocatorAlignedShrink(allocator); + } if (builtin.os == .windows) { // Trying really large alignment. As mentionned in the implementation, // VirtualAlloc returns 64K aligned addresses. We want to make sure - // DirectAllocator works beyond that, as it's not tested by + // PageAllocator works beyond that, as it's not tested by // `testAllocatorLargeAlignment`. const slice = try allocator.alignedAlloc(u8, 1 << 20, 128); slice[0] = 0x12; @@ -760,7 +877,7 @@ test "HeapAllocator" { } test "ArenaAllocator" { - var arena_allocator = ArenaAllocator.init(direct_allocator); + var arena_allocator = ArenaAllocator.init(page_allocator); defer arena_allocator.deinit(); try testAllocator(&arena_allocator.allocator); @@ -769,7 +886,7 @@ test "ArenaAllocator" { try testAllocatorAlignedShrink(&arena_allocator.allocator); } -var test_fixed_buffer_allocator_memory: [80000 * @sizeOf(u64)]u8 = undefined; +var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined; test "FixedBufferAllocator" { var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); @@ -895,10 +1012,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo if (mem.page_size << 2 > maxInt(usize)) return; const USizeShift = @IntType(false, std.math.log2(usize.bit_count)); - const large_align = u29(mem.page_size << 2); + const large_align = @as(u29, mem.page_size << 2); var align_mask: usize = undefined; - _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(u29, large_align)), &align_mask); + _ = @shlWithOverflow(usize, ~@as(usize, 0), @as(USizeShift, @ctz(u29, large_align)), &align_mask); var slice = try allocator.alignedAlloc(u8, large_align, 500); testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); @@ -946,3 +1063,7 @@ fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!voi testing.expect(slice[0] == 0x12); testing.expect(slice[60] == 0x34); } + +test "heap" { + _ = @import("heap/logging_allocator.zig"); +} diff --git a/lib/std/heap/logging_allocator.zig b/lib/std/heap/logging_allocator.zig index c1f09a1aa..d1b7f4776 100644 --- a/lib/std/heap/logging_allocator.zig +++ b/lib/std/heap/logging_allocator.zig @@ -27,15 +27,15 @@ pub const LoggingAllocator = struct { fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { const self = @fieldParentPtr(Self, "allocator", allocator); if (old_mem.len == 0) { - self.out_stream.print("allocation of {} ", new_size) catch {}; + self.out_stream.print("allocation of {} ", .{new_size}) catch {}; } else { - self.out_stream.print("resize from {} to {} ", old_mem.len, new_size) catch {}; + self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {}; } const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align); if (result) |buff| { - self.out_stream.print("success!\n") catch {}; + self.out_stream.print("success!\n", .{}) catch {}; } else |err| { - self.out_stream.print("failure!\n") catch {}; + self.out_stream.print("failure!\n", .{}) catch {}; } return result; } @@ -44,10 +44,27 @@ pub const LoggingAllocator = struct { const self = @fieldParentPtr(Self, "allocator", allocator); const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align); if (new_size == 0) { - self.out_stream.print("free of {} bytes success!\n", old_mem.len) catch {}; + self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {}; } else { - self.out_stream.print("shrink from {} bytes to {} bytes success!\n", old_mem.len, new_size) catch {}; + self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {}; } return result; } }; + +test "LoggingAllocator" { + var buf: [255]u8 = undefined; + var slice_stream = std.io.SliceOutStream.init(buf[0..]); + const stream = &slice_stream.stream; + + const allocator = &LoggingAllocator.init(std.heap.page_allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator; + + const ptr = try allocator.alloc(u8, 10); + allocator.free(ptr); + + std.testing.expectEqualSlices(u8, + \\allocation of 10 success! + \\free of 10 bytes success! + \\ + , slice_stream.getWritten()); +} diff --git a/lib/std/http/headers.zig b/lib/std/http/headers.zig index a8dfa6862..b1d047aee 100644 --- a/lib/std/http/headers.zig +++ b/lib/std/http/headers.zig @@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool { const HeaderEntry = struct { allocator: *Allocator, - pub name: []const u8, - pub value: []u8, - pub never_index: bool, + name: []const u8, + value: []u8, + never_index: bool, const Self = @This(); @@ -70,12 +70,12 @@ const HeaderEntry = struct { } // Sort lexicographically on header name - return mem.compare(u8, a.name, b.name) == mem.Compare.LessThan; + return mem.order(u8, a.name, b.name) == .lt; } // Sort lexicographically on header value if (!mem.eql(u8, a.value, b.value)) { - return mem.compare(u8, a.value, b.value) == mem.Compare.LessThan; + return mem.order(u8, a.value, b.value) == .lt; } // Doesn't matter here; need to pick something for sort consistency @@ -133,8 +133,7 @@ pub const Headers = struct { self.index.deinit(); } { - var it = self.data.iterator(); - while (it.next()) |entry| { + for (self.data.toSliceConst()) |entry| { entry.deinit(); } self.data.deinit(); @@ -144,27 +143,20 @@ pub const Headers = struct { pub fn clone(self: Self, allocator: *Allocator) !Self { var other = Headers.init(allocator); errdefer other.deinit(); - try other.data.ensureCapacity(self.data.count()); + try other.data.ensureCapacity(self.data.len); try other.index.initCapacity(self.index.entries.len); - var it = self.data.iterator(); - while (it.next()) |entry| { + for (self.data.toSliceConst()) |entry| { try other.append(entry.name, entry.value, entry.never_index); } return other; } - pub fn count(self: Self) usize { - return self.data.count(); - } - - pub const Iterator = HeaderList.Iterator; - - pub fn iterator(self: Self) Iterator { - return self.data.iterator(); + pub fn toSlice(self: Self) []const HeaderEntry { + return self.data.toSliceConst(); } pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void { - const n = self.data.count() + 1; + const n = self.data.len + 1; try self.data.ensureCapacity(n); var entry: HeaderEntry = undefined; if (self.index.get(name)) |kv| { @@ -190,7 +182,7 @@ pub const Headers = struct { pub fn upsert(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void { if (self.index.get(name)) |kv| { const dex = kv.value; - if (dex.count() != 1) + if (dex.len != 1) return error.CannotUpsertMultiValuedField; var e = &self.data.at(dex.at(0)); try e.modify(value, never_index); @@ -209,7 +201,7 @@ pub const Headers = struct { if (self.index.remove(name)) |kv| { var dex = &kv.value; // iterate backwards - var i = dex.count(); + var i = dex.len; while (i > 0) { i -= 1; const data_index = dex.at(i); @@ -232,18 +224,18 @@ pub const Headers = struct { const removed = self.data.orderedRemove(i); const kv = self.index.get(removed.name).?; var dex = &kv.value; - if (dex.count() == 1) { + if (dex.len == 1) { // was last item; delete the index _ = self.index.remove(kv.key); dex.deinit(); removed.deinit(); self.allocator.free(kv.key); } else { - dex.shrink(dex.count() - 1); + dex.shrink(dex.len - 1); removed.deinit(); } // if it was the last item; no need to rebuild index - if (i != self.data.count()) { + if (i != self.data.len) { self.rebuild_index(); } } @@ -254,18 +246,18 @@ pub const Headers = struct { const removed = self.data.swapRemove(i); const kv = self.index.get(removed.name).?; var dex = &kv.value; - if (dex.count() == 1) { + if (dex.len == 1) { // was last item; delete the index _ = self.index.remove(kv.key); dex.deinit(); removed.deinit(); self.allocator.free(kv.key); } else { - dex.shrink(dex.count() - 1); + dex.shrink(dex.len - 1); removed.deinit(); } // if it was the last item; no need to rebuild index - if (i != self.data.count()) { + if (i != self.data.len) { self.rebuild_index(); } } @@ -289,10 +281,9 @@ pub const Headers = struct { pub fn get(self: Self, allocator: *Allocator, name: []const u8) !?[]const HeaderEntry { const dex = self.getIndices(name) orelse return null; - const buf = try allocator.alloc(HeaderEntry, dex.count()); - var it = dex.iterator(); + const buf = try allocator.alloc(HeaderEntry, dex.len); var n: usize = 0; - while (it.next()) |idx| { + for (dex.toSliceConst()) |idx| { buf[n] = self.data.at(idx); n += 1; } @@ -314,9 +305,8 @@ pub const Headers = struct { // adapted from mem.join const total_len = blk: { - var sum: usize = dex.count() - 1; // space for separator(s) - var it = dex.iterator(); - while (it.next()) |idx| + var sum: usize = dex.len - 1; // space for separator(s) + for (dex.toSliceConst()) |idx| sum += self.data.at(idx).value.len; break :blk sum; }; @@ -348,10 +338,9 @@ pub const Headers = struct { } } { // fill up indexes again; we know capacity is fine from before - var it = self.data.iterator(); - while (it.next()) |entry| { + for (self.data.toSliceConst()) |entry, i| { var dex = &self.index.get(entry.name).?.value; - dex.appendAssumeCapacity(it.count); + dex.appendAssumeCapacity(i); } } } @@ -367,10 +356,9 @@ pub const Headers = struct { options: std.fmt.FormatOptions, context: var, comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, + output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { - var it = self.iterator(); - while (it.next()) |entry| { + for (self.toSlice()) |entry| { try output(context, entry.name); try output(context, ": "); try output(context, entry.value); @@ -386,8 +374,7 @@ test "Headers.iterator" { try h.append("cookie", "somevalue", null); var count: i32 = 0; - var it = h.iterator(); - while (it.next()) |e| { + for (h.toSlice()) |e| { if (count == 0) { testing.expectEqualSlices(u8, "foo", e.name); testing.expectEqualSlices(u8, "bar", e.value); @@ -399,7 +386,7 @@ test "Headers.iterator" { } count += 1; } - testing.expectEqual(i32(2), count); + testing.expectEqual(@as(i32, 2), count); } test "Headers.contains" { @@ -420,10 +407,10 @@ test "Headers.delete" { try h.append("cookie", "somevalue", null); testing.expectEqual(false, h.delete("not-present")); - testing.expectEqual(usize(3), h.count()); + testing.expectEqual(@as(usize, 3), h.toSlice().len); testing.expectEqual(true, h.delete("foo")); - testing.expectEqual(usize(2), h.count()); + testing.expectEqual(@as(usize, 2), h.toSlice().len); { const e = h.at(0); testing.expectEqualSlices(u8, "baz", e.name); @@ -448,7 +435,7 @@ test "Headers.orderedRemove" { try h.append("cookie", "somevalue", null); h.orderedRemove(0); - testing.expectEqual(usize(2), h.count()); + testing.expectEqual(@as(usize, 2), h.toSlice().len); { const e = h.at(0); testing.expectEqualSlices(u8, "baz", e.name); @@ -471,7 +458,7 @@ test "Headers.swapRemove" { try h.append("cookie", "somevalue", null); h.swapRemove(0); - testing.expectEqual(usize(2), h.count()); + testing.expectEqual(@as(usize, 2), h.toSlice().len); { const e = h.at(0); testing.expectEqualSlices(u8, "cookie", e.name); @@ -514,8 +501,8 @@ test "Headers.getIndices" { try h.append("set-cookie", "y=2", null); testing.expect(null == h.getIndices("not-present")); - testing.expectEqualSlices(usize, [_]usize{0}, h.getIndices("foo").?.toSliceConst()); - testing.expectEqualSlices(usize, [_]usize{ 1, 2 }, h.getIndices("set-cookie").?.toSliceConst()); + testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.toSliceConst()); + testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.toSliceConst()); } test "Headers.get" { @@ -610,5 +597,5 @@ test "Headers.format" { \\foo: bar \\cookie: somevalue \\ - , try std.fmt.bufPrint(buf[0..], "{}", h)); + , try std.fmt.bufPrint(buf[0..], "{}", .{h})); } diff --git a/lib/std/io.zig b/lib/std/io.zig index 25106e24b..93f2ae768 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -17,9 +17,15 @@ const File = std.fs.File; const testing = std.testing; pub const Mode = enum { + /// I/O operates normally, waiting for the operating system syscalls to complete. blocking, + + /// I/O functions are generated async and rely on a global event loop. Event-based I/O. evented, }; + +/// The application's chosen I/O mode. This defaults to `Mode.blocking` but can be overridden +/// by `root.event_loop`. pub const mode: Mode = if (@hasDecl(root, "io_mode")) root.io_mode else if (@hasDecl(root, "event_loop")) @@ -28,122 +34,68 @@ else Mode.blocking; pub const is_async = mode != .blocking; -pub const GetStdIoError = os.windows.GetStdHandleError; - -pub fn getStdOut() GetStdIoError!File { - if (os.windows.is_the_target) { - const handle = try os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE); - return File.openHandle(handle); +fn getStdOutHandle() os.fd_t { + if (builtin.os == .windows) { + return os.windows.peb().ProcessParameters.hStdOutput; } - return File.openHandle(os.STDOUT_FILENO); + + if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdOutHandle")) { + return root.os.io.getStdOutHandle(); + } + + return os.STDOUT_FILENO; } -pub fn getStdErr() GetStdIoError!File { - if (os.windows.is_the_target) { - const handle = try os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE); - return File.openHandle(handle); - } - return File.openHandle(os.STDERR_FILENO); +pub fn getStdOut() File { + return File.openHandle(getStdOutHandle()); } -pub fn getStdIn() GetStdIoError!File { - if (os.windows.is_the_target) { - const handle = try os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE); - return File.openHandle(handle); +fn getStdErrHandle() os.fd_t { + if (builtin.os == .windows) { + return os.windows.peb().ProcessParameters.hStdError; } - return File.openHandle(os.STDIN_FILENO); + + if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdErrHandle")) { + return root.os.io.getStdErrHandle(); + } + + return os.STDERR_FILENO; +} + +pub fn getStdErr() File { + return File.openHandle(getStdErrHandle()); +} + +fn getStdInHandle() os.fd_t { + if (builtin.os == .windows) { + return os.windows.peb().ProcessParameters.hStdInput; + } + + if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdInHandle")) { + return root.os.io.getStdInHandle(); + } + + return os.STDIN_FILENO; +} + +pub fn getStdIn() File { + return File.openHandle(getStdInHandle()); } pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; pub const COutStream = @import("io/c_out_stream.zig").COutStream; pub const InStream = @import("io/in_stream.zig").InStream; +pub const OutStream = @import("io/out_stream.zig").OutStream; -pub fn OutStream(comptime WriteError: type) type { - return struct { - const Self = @This(); - pub const Error = WriteError; - - writeFn: fn (self: *Self, bytes: []const u8) Error!void, - - pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void { - return std.fmt.format(self, Error, self.writeFn, format, args); - } - - pub fn write(self: *Self, bytes: []const u8) Error!void { - return self.writeFn(self, bytes); - } - - pub fn writeByte(self: *Self, byte: u8) Error!void { - const slice = (*const [1]u8)(&byte)[0..]; - return self.writeFn(self, slice); - } - - pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void { - const slice = (*const [1]u8)(&byte)[0..]; - var i: usize = 0; - while (i < n) : (i += 1) { - try self.writeFn(self, slice); - } - } - - /// Write a native-endian integer. - pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntNative(T, &bytes, value); - return self.writeFn(self, bytes); - } - - /// Write a foreign-endian integer. - pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntForeign(T, &bytes, value); - return self.writeFn(self, bytes); - } - - pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntLittle(T, &bytes, value); - return self.writeFn(self, bytes); - } - - pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeIntBig(T, &bytes, value); - return self.writeFn(self, bytes); - } - - pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { - var bytes: [(T.bit_count + 7) / 8]u8 = undefined; - mem.writeInt(T, &bytes, value, endian); - return self.writeFn(self, bytes); - } - }; -} - +/// Deprecated; use `std.fs.Dir.writeFile`. pub fn writeFile(path: []const u8, data: []const u8) !void { - var file = try File.openWrite(path); - defer file.close(); - try file.write(data); + return fs.cwd().writeFile(path, data); } -/// On success, caller owns returned buffer. +/// Deprecated; use `std.fs.Dir.readFileAlloc`. pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { - return readFileAllocAligned(allocator, path, @alignOf(u8)); -} - -/// On success, caller owns returned buffer. -pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { - var file = try File.openRead(path); - defer file.close(); - - const size = try math.cast(usize, try file.getEndPos()); - const buf = try allocator.alignedAlloc(u8, A, size); - errdefer allocator.free(buf); - - var adapter = file.inStream(); - try adapter.stream.readNoEof(buf[0..size]); - return buf; + return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); } pub fn BufferedInStream(comptime Error: type) type { @@ -155,7 +107,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) const Self = @This(); const Stream = InStream(Error); - pub stream: Stream, + stream: Stream, unbuffered_in_stream: *Stream, @@ -182,6 +134,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) fn readFn(in_stream: *Stream, dest: []u8) !usize { const self = @fieldParentPtr(Self, "stream", in_stream); + // Hot path for one byte reads + if (dest.len == 1 and self.end_index > self.start_index) { + dest[0] = self.buffer[self.start_index]; + self.start_index += 1; + return 1; + } + var dest_index: usize = 0; while (true) { const dest_space = dest.len - dest_index; @@ -201,6 +160,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) if (dest_space < buffer_size) { self.start_index = 0; self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]); + + // Shortcut + if (self.end_index >= dest_space) { + mem.copy(u8, dest[dest_index..], self.buffer[0..dest_space]); + self.start_index = dest_space; + return dest.len; + } } else { // asking for so much data that buffering is actually less efficient. // forward the request directly to the unbuffered stream @@ -267,7 +233,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ pub const Error = InStreamError; pub const Stream = InStream(Error); - pub stream: Stream, + stream: Stream, base: *Stream, // Right now the look-ahead space is statically allocated, but a version with dynamic allocation @@ -330,7 +296,7 @@ pub const SliceInStream = struct { pub const Error = error{}; pub const Stream = InStream(Error); - pub stream: Stream, + stream: Stream, pos: usize, slice: []const u8, @@ -405,21 +371,21 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { const Buf = @IntType(false, buf_bit_count); const BufShift = math.Log2Int(Buf); - out_bits.* = usize(0); + out_bits.* = @as(usize, 0); if (U == u0 or bits == 0) return 0; - var out_buffer = Buf(0); + var out_buffer = @as(Buf, 0); if (self.bit_count > 0) { const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; const shift = u7_bit_count - n; switch (endian) { builtin.Endian.Big => { - out_buffer = Buf(self.bit_buffer >> shift); + out_buffer = @as(Buf, self.bit_buffer >> shift); self.bit_buffer <<= n; }, builtin.Endian.Little => { const value = (self.bit_buffer << shift) >> shift; - out_buffer = Buf(value); + out_buffer = @as(Buf, value); self.bit_buffer >>= n; }, } @@ -445,28 +411,28 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { if (n >= u8_bit_count) { out_buffer <<= @intCast(u3, u8_bit_count - 1); out_buffer <<= 1; - out_buffer |= Buf(next_byte); + out_buffer |= @as(Buf, next_byte); out_bits.* += u8_bit_count; continue; } const shift = @intCast(u3, u8_bit_count - n); out_buffer <<= @intCast(BufShift, n); - out_buffer |= Buf(next_byte >> shift); + out_buffer |= @as(Buf, next_byte >> shift); out_bits.* += n; self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); self.bit_count = shift; }, builtin.Endian.Little => { if (n >= u8_bit_count) { - out_buffer |= Buf(next_byte) << @intCast(BufShift, out_bits.*); + out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*); out_bits.* += u8_bit_count; continue; } const shift = @intCast(u3, u8_bit_count - n); const value = (next_byte << shift) >> shift; - out_buffer |= Buf(value) << @intCast(BufShift, out_bits.*); + out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*); out_bits.* += n; self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); self.bit_count = shift; @@ -486,7 +452,7 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { var self = @fieldParentPtr(Self, "stream", self_stream); var out_bits: usize = undefined; - var out_bits_total = usize(0); + var out_bits_total = @as(usize, 0); //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced if (self.bit_count > 0) { for (buffer) |*b, i| { @@ -502,15 +468,15 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { }; } -/// This is a simple OutStream that writes to a slice, and returns an error +/// This is a simple OutStream that writes to a fixed buffer, and returns an error /// when it runs out of space. pub const SliceOutStream = struct { pub const Error = error{OutOfSpace}; pub const Stream = OutStream(Error); - pub stream: Stream, + stream: Stream, - pub pos: usize, + pos: usize, slice: []u8, pub fn init(slice: []u8) SliceOutStream { @@ -553,7 +519,7 @@ test "io.SliceOutStream" { var slice_stream = SliceOutStream.init(buf[0..]); const stream = &slice_stream.stream; - try stream.print("{}{}!", "Hello", "World"); + try stream.print("{}{}!", .{ "Hello", "World" }); testing.expectEqualSlices(u8, "HelloWorld!", slice_stream.getWritten()); } @@ -565,7 +531,7 @@ pub const NullOutStream = struct { pub const Error = error{}; pub const Stream = OutStream(Error); - pub stream: Stream, + stream: Stream, pub fn init() NullOutStream { return NullOutStream{ @@ -589,8 +555,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type { pub const Stream = OutStream(Error); pub const Error = OutStreamError; - pub stream: Stream, - pub bytes_written: u64, + stream: Stream, + bytes_written: u64, child_stream: *Stream, pub fn init(child_stream: *Stream) Self { @@ -629,7 +595,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr pub const Stream = OutStream(Error); pub const Error = OutStreamError; - pub stream: Stream, + stream: Stream, unbuffered_out_stream: *Stream, @@ -650,10 +616,19 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr self.index = 0; } - fn writeFn(out_stream: *Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { const self = @fieldParentPtr(Self, "stream", out_stream); - if (bytes.len >= self.buffer.len) { + if (bytes.len == 1) { + // This is not required logic but a shorter path + // for single byte writes + self.buffer[self.index] = bytes[0]; + self.index += 1; + if (self.index == buffer_size) { + try self.flush(); + } + return; + } else if (bytes.len >= self.buffer.len) { try self.flush(); return self.unbuffered_out_stream.write(bytes); } @@ -724,7 +699,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { if (bits == 0) return; - const U = @typeOf(value); + const U = @TypeOf(value); comptime assert(trait.isUnsignedInt(U)); //by extending the buffer to a minimum of u8 we can cover a number of edge cases @@ -866,8 +841,7 @@ pub const BufferedAtomicFile = struct { }; pub fn readLine(buf: *std.Buffer) ![]u8 { - var stdin = try getStdIn(); - var stdin_stream = stdin.inStream(); + var stdin_stream = getStdIn().inStream(); return readLineFrom(&stdin_stream.stream, buf); } @@ -908,8 +882,7 @@ test "io.readLineFrom" { } pub fn readLineSlice(slice: []u8) ![]u8 { - var stdin = try getStdIn(); - var stdin_stream = stdin.inStream(); + var stdin_stream = getStdIn().inStream(); return readLineSliceFrom(&stdin_stream.stream, slice); } @@ -1001,14 +974,14 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); } - var result = U(0); + var result = @as(U, 0); for (buffer) |byte, i| { switch (endian) { builtin.Endian.Big => { result = (result << u8_bit_count) | byte; }, builtin.Endian.Little => { - result |= U(byte) << @intCast(Log2U, u8_bit_count * i); + result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i); }, } } @@ -1016,33 +989,6 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, return @bitCast(T, result); } - //@TODO: Replace this with @unionInit or whatever when it is added - // see: #1315 - fn setTag(ptr: var, tag: var) void { - const T = @typeOf(ptr); - comptime assert(trait.isPtrTo(builtin.TypeId.Union)(T)); - const U = meta.Child(T); - - const info = @typeInfo(U).Union; - if (info.tag_type) |TagType| { - comptime assert(TagType == @typeOf(tag)); - - var ptr_tag = ptr: { - if (@alignOf(TagType) >= @alignOf(U)) break :ptr @ptrCast(*TagType, ptr); - const offset = comptime max: { - var max_field_size: comptime_int = 0; - for (info.fields) |field_info| { - const field_size = @sizeOf(field_info.field_type); - max_field_size = math.max(max_field_size, field_size); - } - break :max math.max(max_field_size, @alignOf(U)); - }; - break :ptr @intToPtr(*TagType, @ptrToInt(ptr) + offset); - }; - ptr_tag.* = tag; - } - } - /// Deserializes and returns data of the specified type from the stream pub fn deserialize(self: *Self, comptime T: type) !T { var value: T = undefined; @@ -1052,7 +998,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, /// Deserializes data into the type pointed to by `ptr` pub fn deserializeInto(self: *Self, ptr: var) !void { - const T = @typeOf(ptr); + const T = @TypeOf(ptr); comptime assert(trait.is(builtin.TypeId.Pointer)(T)); if (comptime trait.isSlice(T) or comptime trait.isPtrTo(builtin.TypeId.Array)(T)) { @@ -1106,17 +1052,11 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, const TagInt = @TagType(TagType); const tag = try self.deserializeInt(TagInt); - { - @setRuntimeSafety(false); - //See: #1315 - setTag(ptr, @intToEnum(TagType, tag)); - } - inline for (info.fields) |field_info| { if (field_info.enum_field.?.value == tag) { const name = field_info.name; const FieldType = field_info.field_type; - @field(ptr, name) = FieldType(undefined); + ptr.* = @unionInit(C, name, undefined); try self.deserializeInto(&@field(ptr, name)); return; } @@ -1135,7 +1075,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, return; } - ptr.* = OC(undefined); //make it non-null so the following .? is guaranteed safe + ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe const val_ptr = &ptr.*.?; try self.deserializeInto(val_ptr); }, @@ -1187,7 +1127,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co } fn serializeInt(self: *Self, value: var) Error!void { - const T = @typeOf(value); + const T = @TypeOf(value); comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); const t_bit_count = comptime meta.bitCount(T); @@ -1214,12 +1154,12 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); } - try self.out_stream.write(buffer); + try self.out_stream.write(&buffer); } /// Serializes the passed value into the stream pub fn serialize(self: *Self, value: var) Error!void { - const T = comptime @typeOf(value); + const T = comptime @TypeOf(value); if (comptime trait.isIndexable(T)) { for (value) |v| @@ -1239,7 +1179,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co switch (@typeId(T)) { builtin.TypeId.Void => return, - builtin.TypeId.Bool => try self.serializeInt(u1(@boolToInt(value))), + builtin.TypeId.Bool => try self.serializeInt(@as(u1, @boolToInt(value))), builtin.TypeId.Float, builtin.TypeId.Int => try self.serializeInt(value), builtin.TypeId.Struct => { const info = @typeInfo(T); @@ -1282,10 +1222,10 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co }, builtin.TypeId.Optional => { if (value == null) { - try self.serializeInt(u1(@boolToInt(false))); + try self.serializeInt(@as(u1, @boolToInt(false))); return; } - try self.serializeInt(u1(@boolToInt(true))); + try self.serializeInt(@as(u1, @boolToInt(true))); const OC = comptime meta.Child(T); const val_ptr = &value.?; diff --git a/lib/std/io/in_stream.zig b/lib/std/io/in_stream.zig index 44c74fcca..ab02d20ee 100644 --- a/lib/std/io/in_stream.zig +++ b/lib/std/io/in_stream.zig @@ -5,13 +5,13 @@ const math = std.math; const assert = std.debug.assert; const mem = std.mem; const Buffer = std.Buffer; +const testing = std.testing; pub const default_stack_size = 1 * 1024 * 1024; pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream")) root.stack_size_std_io_InStream else default_stack_size; -pub const stack_align = 16; pub fn InStream(comptime ReadError: type) type { return struct { @@ -34,7 +34,7 @@ pub fn InStream(comptime ReadError: type) type { if (std.io.is_async) { // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read. @setRuntimeSafety(false); - var stack_frame: [stack_size]u8 align(stack_align) = undefined; + var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer); } else { return self.readFn(self, buffer); @@ -130,10 +130,52 @@ pub fn InStream(comptime ReadError: type) type { return buf.toOwnedSlice(); } + /// Reads from the stream until specified byte is found. If the buffer is not + /// large enough to hold the entire contents, `error.StreamTooLong` is returned. + /// If end-of-stream is found, returns the rest of the stream. If this + /// function is called again after that, returns null. + /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The + /// delimiter byte is not included in the returned slice. + pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 { + var index: usize = 0; + while (true) { + const byte = self.readByte() catch |err| switch (err) { + error.EndOfStream => { + if (index == 0) { + return null; + } else { + return buf[0..index]; + } + }, + else => |e| return e, + }; + + if (byte == delimiter) return buf[0..index]; + if (index >= buf.len) return error.StreamTooLong; + + buf[index] = byte; + index += 1; + } + } + + /// Reads from the stream until specified byte is found, discarding all data, + /// including the delimiter. + /// If end-of-stream is found, this function succeeds. + pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void { + while (true) { + const byte = self.readByte() catch |err| switch (err) { + error.EndOfStream => return, + else => |e| return e, + }; + if (byte == delimiter) return; + } + } + /// Reads 1 byte from the stream or returns `error.EndOfStream`. pub fn readByte(self: *Self) !u8 { var result: [1]u8 = undefined; - try self.readNoEof(result[0..]); + const amt_read = try self.read(result[0..]); + if (amt_read < 1) return error.EndOfStream; return result[0]; } @@ -196,5 +238,39 @@ pub fn InStream(comptime ReadError: type) type { try self.readNoEof(@sliceToBytes(res[0..])); return res[0]; } + + /// Reads an integer with the same size as the given enum's tag type. If the integer matches + /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error. + /// TODO optimization taking advantage of most fields being in order + pub fn readEnum(self: *Self, comptime Enum: type, endian: builtin.Endian) !Enum { + const E = error{ + /// An integer was read, but it did not match any of the tags in the supplied enum. + InvalidValue, + }; + const type_info = @typeInfo(Enum).Enum; + const tag = try self.readInt(type_info.tag_type, endian); + + inline for (std.meta.fields(Enum)) |field| { + if (tag == field.value) { + return @field(Enum, field.name); + } + } + + return E.InvalidValue; + } }; } + +test "InStream" { + var buf = "a\x02".*; + var slice_stream = std.io.SliceInStream.init(&buf); + const in_stream = &slice_stream.stream; + testing.expect((try in_stream.readByte()) == 'a'); + testing.expect((try in_stream.readEnum(enum(u8) { + a = 0, + b = 99, + c = 2, + d = 3, + }, undefined)) == .c); + testing.expectError(error.EndOfStream, in_stream.readByte()); +} diff --git a/lib/std/io/out_stream.zig b/lib/std/io/out_stream.zig new file mode 100644 index 000000000..b8f5db6ff --- /dev/null +++ b/lib/std/io/out_stream.zig @@ -0,0 +1,87 @@ +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const root = @import("root"); +const mem = std.mem; + +pub const default_stack_size = 1 * 1024 * 1024; +pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream")) + root.stack_size_std_io_OutStream +else + default_stack_size; + +/// TODO this is not integrated with evented I/O yet. +/// https://github.com/ziglang/zig/issues/3557 +pub fn OutStream(comptime WriteError: type) type { + return struct { + const Self = @This(); + pub const Error = WriteError; + // TODO https://github.com/ziglang/zig/issues/3557 + pub const WriteFn = if (std.io.is_async and false) + async fn (self: *Self, bytes: []const u8) Error!void + else + fn (self: *Self, bytes: []const u8) Error!void; + + writeFn: WriteFn, + + pub fn write(self: *Self, bytes: []const u8) Error!void { + // TODO https://github.com/ziglang/zig/issues/3557 + if (std.io.is_async and false) { + // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write. + @setRuntimeSafety(false); + var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; + return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes); + } else { + return self.writeFn(self, bytes); + } + } + + pub fn print(self: *Self, comptime format: []const u8, args: var) Error!void { + return std.fmt.format(self, Error, self.writeFn, format, args); + } + + pub fn writeByte(self: *Self, byte: u8) Error!void { + const slice = @as(*const [1]u8, &byte)[0..]; + return self.writeFn(self, slice); + } + + pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void { + const slice = @as(*const [1]u8, &byte)[0..]; + var i: usize = 0; + while (i < n) : (i += 1) { + try self.writeFn(self, slice); + } + } + + /// Write a native-endian integer. + pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntNative(T, &bytes, value); + return self.writeFn(self, &bytes); + } + + /// Write a foreign-endian integer. + pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntForeign(T, &bytes, value); + return self.writeFn(self, &bytes); + } + + pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntLittle(T, &bytes, value); + return self.writeFn(self, &bytes); + } + + pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeIntBig(T, &bytes, value); + return self.writeFn(self, &bytes); + } + + pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { + var bytes: [(T.bit_count + 7) / 8]u8 = undefined; + mem.writeInt(T, &bytes, value, endian); + return self.writeFn(self, &bytes); + } + }; +} diff --git a/lib/std/io/seekable_stream.zig b/lib/std/io/seekable_stream.zig index 86f76d8c1..48dc31b78 100644 --- a/lib/std/io/seekable_stream.zig +++ b/lib/std/io/seekable_stream.zig @@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct { pub const Stream = InStream(Error); pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError); - pub stream: Stream, - pub seekable_stream: SeekableInStream, + stream: Stream, + seekable_stream: SeekableInStream, pos: usize, slice: []const u8, diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 8b2dc9e89..92259fd6e 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -14,26 +14,28 @@ test "write a file, read it, then delete it" { var raw_bytes: [200 * 1024]u8 = undefined; var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; + const cwd = fs.cwd(); + var data: [1024]u8 = undefined; var prng = DefaultPrng.init(1234); prng.random.bytes(data[0..]); const tmp_file_name = "temp_test_file.txt"; { - var file = try File.openWrite(tmp_file_name); + var file = try cwd.createFile(tmp_file_name, .{}); defer file.close(); var file_out_stream = file.outStream(); var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream); const st = &buf_stream.stream; - try st.print("begin"); + try st.print("begin", .{}); try st.write(data[0..]); - try st.print("end"); + try st.print("end", .{}); try buf_stream.flush(); } { - // make sure openWriteNoClobber doesn't harm the file - if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| { + // Make sure the exclusive flag is honored. + if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| { unreachable; } else |err| { std.debug.assert(err == File.OpenError.PathAlreadyExists); @@ -41,7 +43,7 @@ test "write a file, read it, then delete it" { } { - var file = try File.openRead(tmp_file_name); + var file = try cwd.openFile(tmp_file_name, .{}); defer file.close(); const file_size = try file.getEndPos(); @@ -55,10 +57,10 @@ test "write a file, read it, then delete it" { defer allocator.free(contents); expect(mem.eql(u8, contents[0.."begin".len], "begin")); - expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); + expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data)); expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); } - try fs.deleteFile(tmp_file_name); + try cwd.deleteFile(tmp_file_name); } test "BufferOutStream" { @@ -70,14 +72,14 @@ test "BufferOutStream" { const x: i32 = 42; const y: i32 = 1234; - try buf_stream.print("x: {}\ny: {}\n", x, y); + try buf_stream.print("x: {}\ny: {}\n", .{ x, y }); expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); } test "SliceInStream" { const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 }; - var ss = io.SliceInStream.init(bytes); + var ss = io.SliceInStream.init(&bytes); var dest: [4]u8 = undefined; @@ -95,7 +97,7 @@ test "SliceInStream" { test "PeekStream" { const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; - var ss = io.SliceInStream.init(bytes); + var ss = io.SliceInStream.init(&bytes); var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream); var dest: [4]u8 = undefined; @@ -226,55 +228,55 @@ test "BitOutStream" { const OutError = io.SliceOutStream.Error; var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream); - try bit_stream_be.writeBits(u2(1), 1); - try bit_stream_be.writeBits(u5(2), 2); - try bit_stream_be.writeBits(u128(3), 3); - try bit_stream_be.writeBits(u8(4), 4); - try bit_stream_be.writeBits(u9(5), 5); - try bit_stream_be.writeBits(u1(1), 1); + try bit_stream_be.writeBits(@as(u2, 1), 1); + try bit_stream_be.writeBits(@as(u5, 2), 2); + try bit_stream_be.writeBits(@as(u128, 3), 3); + try bit_stream_be.writeBits(@as(u8, 4), 4); + try bit_stream_be.writeBits(@as(u9, 5), 5); + try bit_stream_be.writeBits(@as(u1, 1), 1); expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); mem_out_be.pos = 0; - try bit_stream_be.writeBits(u15(0b110011010000101), 15); + try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); try bit_stream_be.flushBits(); expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); mem_out_be.pos = 0; - try bit_stream_be.writeBits(u32(0b110011010000101), 16); + try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); - try bit_stream_be.writeBits(u0(0), 0); + try bit_stream_be.writeBits(@as(u0, 0), 0); var mem_out_le = io.SliceOutStream.init(mem_le[0..]); var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream); - try bit_stream_le.writeBits(u2(1), 1); - try bit_stream_le.writeBits(u5(2), 2); - try bit_stream_le.writeBits(u128(3), 3); - try bit_stream_le.writeBits(u8(4), 4); - try bit_stream_le.writeBits(u9(5), 5); - try bit_stream_le.writeBits(u1(1), 1); + try bit_stream_le.writeBits(@as(u2, 1), 1); + try bit_stream_le.writeBits(@as(u5, 2), 2); + try bit_stream_le.writeBits(@as(u128, 3), 3); + try bit_stream_le.writeBits(@as(u8, 4), 4); + try bit_stream_le.writeBits(@as(u9, 5), 5); + try bit_stream_le.writeBits(@as(u1, 1), 1); expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); mem_out_le.pos = 0; - try bit_stream_le.writeBits(u15(0b110011010000101), 15); + try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); try bit_stream_le.flushBits(); expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); mem_out_le.pos = 0; - try bit_stream_le.writeBits(u32(0b1100110100001011), 16); + try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); - try bit_stream_le.writeBits(u0(0), 0); + try bit_stream_le.writeBits(@as(u0, 0), 0); } test "BitStreams with File Stream" { const tmp_file_name = "temp_test_file.txt"; { - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer file.close(); var file_out = file.outStream(); @@ -282,16 +284,16 @@ test "BitStreams with File Stream" { const OutError = File.WriteError; var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); - try bit_stream.writeBits(u2(1), 1); - try bit_stream.writeBits(u5(2), 2); - try bit_stream.writeBits(u128(3), 3); - try bit_stream.writeBits(u8(4), 4); - try bit_stream.writeBits(u9(5), 5); - try bit_stream.writeBits(u1(1), 1); + try bit_stream.writeBits(@as(u2, 1), 1); + try bit_stream.writeBits(@as(u5, 2), 2); + try bit_stream.writeBits(@as(u128, 3), 3); + try bit_stream.writeBits(@as(u8, 4), 4); + try bit_stream.writeBits(@as(u9, 5), 5); + try bit_stream.writeBits(@as(u1, 1), 1); try bit_stream.flushBits(); } { - var file = try File.openRead(tmp_file_name); + var file = try fs.cwd().openFile(tmp_file_name, .{}); defer file.close(); var file_in = file.inStream(); @@ -316,7 +318,7 @@ test "BitStreams with File Stream" { expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); } - try fs.deleteFile(tmp_file_name); + try fs.cwd().deleteFile(tmp_file_name); } fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { @@ -345,8 +347,8 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packi inline while (i <= max_test_bitsize) : (i += 1) { const U = @IntType(false, i); const S = @IntType(true, i); - try serializer.serializeInt(U(i)); - if (i != 0) try serializer.serializeInt(S(-1)) else try serializer.serialize(S(0)); + try serializer.serializeInt(@as(U, i)); + if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0)); } try serializer.flush(); @@ -356,8 +358,8 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packi const S = @IntType(true, i); const x = try deserializer.deserializeInt(U); const y = try deserializer.deserializeInt(S); - expect(x == U(i)); - if (i != 0) expect(y == S(-1)) else expect(y == 0); + expect(x == @as(U, i)); + if (i != 0) expect(y == @as(S, -1)) else expect(y == 0); } const u8_bit_count = comptime meta.bitCount(u8); @@ -577,11 +579,11 @@ fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !v var in_stream = &in.stream; var deserializer = io.Deserializer(endian, packing, InError).init(in_stream); - try serializer.serialize(u14(3)); + try serializer.serialize(@as(u14, 3)); expectError(error.InvalidEnumTag, deserializer.deserialize(A)); out.pos = 0; - try serializer.serialize(u14(3)); - try serializer.serialize(u14(88)); + try serializer.serialize(@as(u14, 3)); + try serializer.serialize(@as(u14, 88)); expectError(error.InvalidEnumTag, deserializer.deserialize(C)); } @@ -595,26 +597,26 @@ test "Deserializer bad data" { test "c out stream" { if (!builtin.link_libc) return error.SkipZigTest; - const filename = c"tmp_io_test_file.txt"; - const out_file = std.c.fopen(filename, c"w") orelse return error.UnableToOpenTestFile; + const filename = "tmp_io_test_file.txt"; + const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; defer { _ = std.c.fclose(out_file); - fs.deleteFileC(filename) catch {}; + fs.cwd().deleteFileC(filename) catch {}; } const out_stream = &io.COutStream.init(out_file).stream; - try out_stream.print("hi: {}\n", i32(123)); + try out_stream.print("hi: {}\n", .{@as(i32, 123)}); } test "File seek ops" { const tmp_file_name = "temp_test_file.txt"; - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer { file.close(); - fs.deleteFile(tmp_file_name) catch {}; + fs.cwd().deleteFile(tmp_file_name) catch {}; } - try file.write([_]u8{0x55} ** 8192); + try file.write(&([_]u8{0x55} ** 8192)); // Seek to the end try file.seekFromEnd(0); @@ -629,3 +631,21 @@ test "File seek ops" { try file.seekTo(1234); std.testing.expect((try file.getPos()) == 1234); } + +test "updateTimes" { + const tmp_file_name = "just_a_temporary_file.txt"; + var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true }); + defer { + file.close(); + std.fs.cwd().deleteFile(tmp_file_name) catch {}; + } + var stat_old = try file.stat(); + // Set atime and mtime to 5s before + try file.updateTimes( + stat_old.atime - 5 * std.time.ns_per_s, + stat_old.mtime - 5 * std.time.ns_per_s, + ); + var stat_new = try file.stat(); + std.testing.expect(stat_new.atime < stat_old.atime); + std.testing.expect(stat_new.mtime < stat_old.mtime); +} diff --git a/lib/std/json.zig b/lib/std/json.zig index f562a672f..1b10be076 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -4,90 +4,72 @@ const std = @import("std.zig"); const debug = std.debug; +const assert = debug.assert; const testing = std.testing; const mem = std.mem; const maxInt = std.math.maxInt; -// A single token slice into the parent string. -// -// Use `token.slice()` on the input at the current position to get the current slice. -pub const Token = struct { - id: Id, - // How many bytes do we skip before counting - offset: u1, - // Whether string contains a \uXXXX sequence and cannot be zero-copied - string_has_escape: bool, - // Whether number is simple and can be represented by an integer (i.e. no `.` or `e`) - number_is_integer: bool, - // How many bytes from the current position behind the start of this token is. - count: usize, +pub const WriteStream = @import("json/write_stream.zig").WriteStream; - pub const Id = enum { - ObjectBegin, - ObjectEnd, - ArrayBegin, - ArrayEnd, - String, - Number, - True, - False, - Null, - }; +const StringEscapes = union(enum) { + None, - pub fn init(id: Id, count: usize, offset: u1) Token { - return Token{ - .id = id, - .offset = offset, - .string_has_escape = false, - .number_is_integer = true, - .count = count, - }; - } - - pub fn initString(count: usize, has_unicode_escape: bool) Token { - return Token{ - .id = Id.String, - .offset = 0, - .string_has_escape = has_unicode_escape, - .number_is_integer = true, - .count = count, - }; - } - - pub fn initNumber(count: usize, number_is_integer: bool) Token { - return Token{ - .id = Id.Number, - .offset = 0, - .string_has_escape = false, - .number_is_integer = number_is_integer, - .count = count, - }; - } - - // A marker token is a zero-length - pub fn initMarker(id: Id) Token { - return Token{ - .id = id, - .offset = 0, - .string_has_escape = false, - .number_is_integer = true, - .count = 0, - }; - } - - // Slice into the underlying input string. - pub fn slice(self: Token, input: []const u8, i: usize) []const u8 { - return input[i + self.offset - self.count .. i + self.offset]; - } + Some: struct { + size_diff: isize, + }, }; -// A small streaming JSON parser. This accepts input one byte at a time and returns tokens as -// they are encountered. No copies or allocations are performed during parsing and the entire -// parsing state requires ~40-50 bytes of stack space. -// -// Conforms strictly to RFC8529. -// -// For a non-byte based wrapper, consider using TokenStream instead. +/// A single token slice into the parent string. +/// +/// Use `token.slice()` on the input at the current position to get the current slice. +pub const Token = union(enum) { + ObjectBegin, + ObjectEnd, + ArrayBegin, + ArrayEnd, + String: struct { + /// How many bytes the token is. + count: usize, + + /// Whether string contains an escape sequence and cannot be zero-copied + escapes: StringEscapes, + + pub fn decodedLength(self: @This()) usize { + return self.count +% switch (self.escapes) { + .None => 0, + .Some => |s| @bitCast(usize, s.size_diff), + }; + } + + /// Slice into the underlying input string. + pub fn slice(self: @This(), input: []const u8, i: usize) []const u8 { + return input[i - self.count .. i]; + } + }, + Number: struct { + /// How many bytes the token is. + count: usize, + + /// Whether number is simple and can be represented by an integer (i.e. no `.` or `e`) + is_integer: bool, + + /// Slice into the underlying input string. + pub fn slice(self: @This(), input: []const u8, i: usize) []const u8 { + return input[i - self.count .. i]; + } + }, + True, + False, + Null, +}; + +/// A small streaming JSON parser. This accepts input one byte at a time and returns tokens as +/// they are encountered. No copies or allocations are performed during parsing and the entire +/// parsing state requires ~40-50 bytes of stack space. +/// +/// Conforms strictly to RFC8529. +/// +/// For a non-byte based wrapper, consider using TokenStream instead. pub const StreamingParser = struct { // Current state state: State, @@ -100,7 +82,14 @@ pub const StreamingParser = struct { // If we stopped now, would the complete parsed string to now be a valid json string complete: bool, // Current token flags to pass through to the next generated, see Token. - string_has_escape: bool, + string_escapes: StringEscapes, + // When in .String states, was the previous character a high surrogate? + string_last_was_high_surrogate: bool, + // Used inside of StringEscapeHexUnicode* states + string_unicode_codepoint: u21, + // The first byte needs to be stored to validate 3- and 4-byte sequences. + sequence_first_byte: u8 = undefined, + // When in .Number states, is the number a (still) valid integer? number_is_integer: bool, // Bit-stack for nested object/map literals (max 255 nestings). @@ -118,16 +107,18 @@ pub const StreamingParser = struct { } pub fn reset(p: *StreamingParser) void { - p.state = State.TopLevelBegin; + p.state = .TopLevelBegin; p.count = 0; // Set before ever read in main transition function p.after_string_state = undefined; - p.after_value_state = State.ValueEnd; // handle end of values normally + p.after_value_state = .ValueEnd; // handle end of values normally p.stack = 0; p.stack_used = 0; p.complete = false; - p.string_has_escape = false; - p.number_is_integer = true; + p.string_escapes = undefined; + p.string_last_was_high_surrogate = undefined; + p.string_unicode_codepoint = undefined; + p.number_is_integer = undefined; } pub const State = enum { @@ -143,9 +134,12 @@ pub const StreamingParser = struct { ValueBeginNoClosing, String, - StringUtf8Byte3, - StringUtf8Byte2, - StringUtf8Byte1, + StringUtf8Byte2Of2, + StringUtf8Byte2Of3, + StringUtf8Byte3Of3, + StringUtf8Byte2Of4, + StringUtf8Byte3Of4, + StringUtf8Byte4Of4, StringEscapeCharacter, StringEscapeHexUnicode4, StringEscapeHexUnicode3, @@ -203,10 +197,10 @@ pub const StreamingParser = struct { InvalidControlCharacter, }; - // Give another byte to the parser and obtain any new tokens. This may (rarely) return two - // tokens. token2 is always null if token1 is null. - // - // There is currently no error recovery on a bad stream. + /// Give another byte to the parser and obtain any new tokens. This may (rarely) return two + /// tokens. token2 is always null if token1 is null. + /// + /// There is currently no error recovery on a bad stream. pub fn feed(p: *StreamingParser, c: u8, token1: *?Token, token2: *?Token) Error!void { token1.* = null; token2.* = null; @@ -221,66 +215,67 @@ pub const StreamingParser = struct { // Perform a single transition on the state machine and return any possible token. fn transition(p: *StreamingParser, c: u8, token: *?Token) Error!bool { switch (p.state) { - State.TopLevelBegin => switch (c) { + .TopLevelBegin => switch (c) { '{' => { p.stack <<= 1; p.stack |= object_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ObjectSeparator; + p.state = .ValueBegin; + p.after_string_state = .ObjectSeparator; - token.* = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.ObjectBegin; }, '[' => { p.stack <<= 1; p.stack |= array_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ValueEnd; + p.state = .ValueBegin; + p.after_string_state = .ValueEnd; - token.* = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.ArrayBegin; }, '-' => { p.number_is_integer = true; - p.state = State.Number; - p.after_value_state = State.TopLevelEnd; + p.state = .Number; + p.after_value_state = .TopLevelEnd; p.count = 0; }, '0' => { p.number_is_integer = true; - p.state = State.NumberMaybeDotOrExponent; - p.after_value_state = State.TopLevelEnd; + p.state = .NumberMaybeDotOrExponent; + p.after_value_state = .TopLevelEnd; p.count = 0; }, '1'...'9' => { p.number_is_integer = true; - p.state = State.NumberMaybeDigitOrDotOrExponent; - p.after_value_state = State.TopLevelEnd; + p.state = .NumberMaybeDigitOrDotOrExponent; + p.after_value_state = .TopLevelEnd; p.count = 0; }, '"' => { - p.state = State.String; - p.after_value_state = State.TopLevelEnd; + p.state = .String; + p.after_value_state = .TopLevelEnd; // We don't actually need the following since after_value_state should override. - p.after_string_state = State.ValueEnd; - p.string_has_escape = false; + p.after_string_state = .ValueEnd; + p.string_escapes = .None; + p.string_last_was_high_surrogate = false; p.count = 0; }, 't' => { - p.state = State.TrueLiteral1; - p.after_value_state = State.TopLevelEnd; + p.state = .TrueLiteral1; + p.after_value_state = .TopLevelEnd; p.count = 0; }, 'f' => { - p.state = State.FalseLiteral1; - p.after_value_state = State.TopLevelEnd; + p.state = .FalseLiteral1; + p.after_value_state = .TopLevelEnd; p.count = 0; }, 'n' => { - p.state = State.NullLiteral1; - p.after_value_state = State.TopLevelEnd; + p.state = .NullLiteral1; + p.after_value_state = .TopLevelEnd; p.count = 0; }, 0x09, 0x0A, 0x0D, 0x20 => { @@ -291,7 +286,7 @@ pub const StreamingParser = struct { }, }, - State.TopLevelEnd => switch (c) { + .TopLevelEnd => switch (c) { 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, @@ -300,7 +295,7 @@ pub const StreamingParser = struct { }, }, - State.ValueBegin => switch (c) { + .ValueBegin => switch (c) { // NOTE: These are shared in ValueEnd as well, think we can reorder states to // be a bit clearer and avoid this duplication. '}' => { @@ -312,7 +307,7 @@ pub const StreamingParser = struct { return error.TooManyClosingItems; } - p.state = State.ValueBegin; + p.state = .ValueBegin; p.after_string_state = State.fromInt(p.stack & 1); p.stack >>= 1; @@ -321,14 +316,14 @@ pub const StreamingParser = struct { switch (p.stack_used) { 0 => { p.complete = true; - p.state = State.TopLevelEnd; + p.state = .TopLevelEnd; }, else => { - p.state = State.ValueEnd; + p.state = .ValueEnd; }, } - token.* = Token.initMarker(Token.Id.ObjectEnd); + token.* = Token.ObjectEnd; }, ']' => { if (p.stack & 1 != array_bit) { @@ -338,7 +333,7 @@ pub const StreamingParser = struct { return error.TooManyClosingItems; } - p.state = State.ValueBegin; + p.state = .ValueBegin; p.after_string_state = State.fromInt(p.stack & 1); p.stack >>= 1; @@ -347,14 +342,14 @@ pub const StreamingParser = struct { switch (p.stack_used) { 0 => { p.complete = true; - p.state = State.TopLevelEnd; + p.state = .TopLevelEnd; }, else => { - p.state = State.ValueEnd; + p.state = .ValueEnd; }, } - token.* = Token.initMarker(Token.Id.ArrayEnd); + token.* = Token.ArrayEnd; }, '{' => { if (p.stack_used == max_stack_size) { @@ -365,10 +360,10 @@ pub const StreamingParser = struct { p.stack |= object_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ObjectSeparator; + p.state = .ValueBegin; + p.after_string_state = .ObjectSeparator; - token.* = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.ObjectBegin; }, '[' => { if (p.stack_used == max_stack_size) { @@ -379,37 +374,42 @@ pub const StreamingParser = struct { p.stack |= array_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ValueEnd; + p.state = .ValueBegin; + p.after_string_state = .ValueEnd; - token.* = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.ArrayBegin; }, '-' => { - p.state = State.Number; + p.number_is_integer = true; + p.state = .Number; p.count = 0; }, '0' => { - p.state = State.NumberMaybeDotOrExponent; + p.number_is_integer = true; + p.state = .NumberMaybeDotOrExponent; p.count = 0; }, '1'...'9' => { - p.state = State.NumberMaybeDigitOrDotOrExponent; + p.number_is_integer = true; + p.state = .NumberMaybeDigitOrDotOrExponent; p.count = 0; }, '"' => { - p.state = State.String; + p.state = .String; + p.string_escapes = .None; + p.string_last_was_high_surrogate = false; p.count = 0; }, 't' => { - p.state = State.TrueLiteral1; + p.state = .TrueLiteral1; p.count = 0; }, 'f' => { - p.state = State.FalseLiteral1; + p.state = .FalseLiteral1; p.count = 0; }, 'n' => { - p.state = State.NullLiteral1; + p.state = .NullLiteral1; p.count = 0; }, 0x09, 0x0A, 0x0D, 0x20 => { @@ -421,7 +421,7 @@ pub const StreamingParser = struct { }, // TODO: A bit of duplication here and in the following state, redo. - State.ValueBeginNoClosing => switch (c) { + .ValueBeginNoClosing => switch (c) { '{' => { if (p.stack_used == max_stack_size) { return error.TooManyNestedItems; @@ -431,10 +431,10 @@ pub const StreamingParser = struct { p.stack |= object_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ObjectSeparator; + p.state = .ValueBegin; + p.after_string_state = .ObjectSeparator; - token.* = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.ObjectBegin; }, '[' => { if (p.stack_used == max_stack_size) { @@ -445,37 +445,42 @@ pub const StreamingParser = struct { p.stack |= array_bit; p.stack_used += 1; - p.state = State.ValueBegin; - p.after_string_state = State.ValueEnd; + p.state = .ValueBegin; + p.after_string_state = .ValueEnd; - token.* = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.ArrayBegin; }, '-' => { - p.state = State.Number; + p.number_is_integer = true; + p.state = .Number; p.count = 0; }, '0' => { - p.state = State.NumberMaybeDotOrExponent; + p.number_is_integer = true; + p.state = .NumberMaybeDotOrExponent; p.count = 0; }, '1'...'9' => { - p.state = State.NumberMaybeDigitOrDotOrExponent; + p.number_is_integer = true; + p.state = .NumberMaybeDigitOrDotOrExponent; p.count = 0; }, '"' => { - p.state = State.String; + p.state = .String; + p.string_escapes = .None; + p.string_last_was_high_surrogate = false; p.count = 0; }, 't' => { - p.state = State.TrueLiteral1; + p.state = .TrueLiteral1; p.count = 0; }, 'f' => { - p.state = State.FalseLiteral1; + p.state = .FalseLiteral1; p.count = 0; }, 'n' => { - p.state = State.NullLiteral1; + p.state = .NullLiteral1; p.count = 0; }, 0x09, 0x0A, 0x0D, 0x20 => { @@ -486,17 +491,17 @@ pub const StreamingParser = struct { }, }, - State.ValueEnd => switch (c) { + .ValueEnd => switch (c) { ',' => { p.after_string_state = State.fromInt(p.stack & 1); - p.state = State.ValueBeginNoClosing; + p.state = .ValueBeginNoClosing; }, ']' => { if (p.stack_used == 0) { return error.UnbalancedBrackets; } - p.state = State.ValueEnd; + p.state = .ValueEnd; p.after_string_state = State.fromInt(p.stack & 1); p.stack >>= 1; @@ -504,17 +509,17 @@ pub const StreamingParser = struct { if (p.stack_used == 0) { p.complete = true; - p.state = State.TopLevelEnd; + p.state = .TopLevelEnd; } - token.* = Token.initMarker(Token.Id.ArrayEnd); + token.* = Token.ArrayEnd; }, '}' => { if (p.stack_used == 0) { return error.UnbalancedBraces; } - p.state = State.ValueEnd; + p.state = .ValueEnd; p.after_string_state = State.fromInt(p.stack & 1); p.stack >>= 1; @@ -522,10 +527,10 @@ pub const StreamingParser = struct { if (p.stack_used == 0) { p.complete = true; - p.state = State.TopLevelEnd; + p.state = .TopLevelEnd; } - token.* = Token.initMarker(Token.Id.ObjectEnd); + token.* = Token.ObjectEnd; }, 0x09, 0x0A, 0x0D, 0x20 => { // whitespace @@ -535,10 +540,10 @@ pub const StreamingParser = struct { }, }, - State.ObjectSeparator => switch (c) { + .ObjectSeparator => switch (c) { ':' => { - p.state = State.ValueBegin; - p.after_string_state = State.ValueEnd; + p.state = .ValueBegin; + p.after_string_state = .ValueEnd; }, 0x09, 0x0A, 0x0D, 0x20 => { // whitespace @@ -548,55 +553,105 @@ pub const StreamingParser = struct { }, }, - State.String => switch (c) { + .String => switch (c) { 0x00...0x1F => { return error.InvalidControlCharacter; }, '"' => { p.state = p.after_string_state; - if (p.after_value_state == State.TopLevelEnd) { - p.state = State.TopLevelEnd; + if (p.after_value_state == .TopLevelEnd) { + p.state = .TopLevelEnd; p.complete = true; } - token.* = Token.initString(p.count - 1, p.string_has_escape); + token.* = .{ + .String = .{ + .count = p.count - 1, + .escapes = p.string_escapes, + }, + }; + p.string_escapes = undefined; + p.string_last_was_high_surrogate = undefined; }, '\\' => { - p.state = State.StringEscapeCharacter; + p.state = .StringEscapeCharacter; + switch (p.string_escapes) { + .None => { + p.string_escapes = .{ .Some = .{ .size_diff = 0 } }; + }, + .Some => {}, + } }, 0x20, 0x21, 0x23...0x5B, 0x5D...0x7F => { // non-control ascii + p.string_last_was_high_surrogate = false; }, - 0xC0...0xDF => { - p.state = State.StringUtf8Byte1; + 0xC2...0xDF => { + p.state = .StringUtf8Byte2Of2; }, 0xE0...0xEF => { - p.state = State.StringUtf8Byte2; + p.state = .StringUtf8Byte2Of3; + p.sequence_first_byte = c; }, - 0xF0...0xFF => { - p.state = State.StringUtf8Byte3; + 0xF0...0xF4 => { + p.state = .StringUtf8Byte2Of4; + p.sequence_first_byte = c; }, else => { return error.InvalidUtf8Byte; }, }, - State.StringUtf8Byte3 => switch (c >> 6) { - 0b10 => p.state = State.StringUtf8Byte2, + .StringUtf8Byte2Of2 => switch (c >> 6) { + 0b10 => p.state = .String, + else => return error.InvalidUtf8Byte, + }, + .StringUtf8Byte2Of3 => { + switch (p.sequence_first_byte) { + 0xE0 => switch (c) { + 0xA0...0xBF => {}, + else => return error.InvalidUtf8Byte, + }, + 0xE1...0xEF => switch (c) { + 0x80...0xBF => {}, + else => return error.InvalidUtf8Byte, + }, + else => return error.InvalidUtf8Byte, + } + p.state = .StringUtf8Byte3Of3; + }, + .StringUtf8Byte3Of3 => switch (c) { + 0x80...0xBF => p.state = .String, + else => return error.InvalidUtf8Byte, + }, + .StringUtf8Byte2Of4 => { + switch (p.sequence_first_byte) { + 0xF0 => switch (c) { + 0x90...0xBF => {}, + else => return error.InvalidUtf8Byte, + }, + 0xF1...0xF3 => switch (c) { + 0x80...0xBF => {}, + else => return error.InvalidUtf8Byte, + }, + 0xF4 => switch (c) { + 0x80...0x8F => {}, + else => return error.InvalidUtf8Byte, + }, + else => return error.InvalidUtf8Byte, + } + p.state = .StringUtf8Byte3Of4; + }, + .StringUtf8Byte3Of4 => switch (c) { + 0x80...0xBF => p.state = .StringUtf8Byte4Of4, + else => return error.InvalidUtf8Byte, + }, + .StringUtf8Byte4Of4 => switch (c) { + 0x80...0xBF => p.state = .String, else => return error.InvalidUtf8Byte, }, - State.StringUtf8Byte2 => switch (c >> 6) { - 0b10 => p.state = State.StringUtf8Byte1, - else => return error.InvalidUtf8Byte, - }, - - State.StringUtf8Byte1 => switch (c >> 6) { - 0b10 => p.state = State.String, - else => return error.InvalidUtf8Byte, - }, - - State.StringEscapeCharacter => switch (c) { + .StringEscapeCharacter => switch (c) { // NOTE: '/' is allowed as an escaped character but it also is allowed // as unescaped according to the RFC. There is a reported errata which suggests // removing the non-escaped variant but it makes more sense to simply disallow @@ -606,54 +661,121 @@ pub const StreamingParser = struct { // however, so we default to the status quo where both are accepted until this // is further clarified. '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => { - p.string_has_escape = true; - p.state = State.String; + p.string_escapes.Some.size_diff -= 1; + p.state = .String; + p.string_last_was_high_surrogate = false; }, 'u' => { - p.string_has_escape = true; - p.state = State.StringEscapeHexUnicode4; + p.state = .StringEscapeHexUnicode4; }, else => { return error.InvalidEscapeCharacter; }, }, - State.StringEscapeHexUnicode4 => switch (c) { - '0'...'9', 'A'...'F', 'a'...'f' => { - p.state = State.StringEscapeHexUnicode3; - }, - else => return error.InvalidUnicodeHexSymbol, + .StringEscapeHexUnicode4 => { + var codepoint: u21 = undefined; + switch (c) { + else => return error.InvalidUnicodeHexSymbol, + '0'...'9' => { + codepoint = c - '0'; + }, + 'A'...'F' => { + codepoint = c - 'A' + 10; + }, + 'a'...'f' => { + codepoint = c - 'a' + 10; + }, + } + p.state = .StringEscapeHexUnicode3; + p.string_unicode_codepoint = codepoint << 12; }, - State.StringEscapeHexUnicode3 => switch (c) { - '0'...'9', 'A'...'F', 'a'...'f' => { - p.state = State.StringEscapeHexUnicode2; - }, - else => return error.InvalidUnicodeHexSymbol, + .StringEscapeHexUnicode3 => { + var codepoint: u21 = undefined; + switch (c) { + else => return error.InvalidUnicodeHexSymbol, + '0'...'9' => { + codepoint = c - '0'; + }, + 'A'...'F' => { + codepoint = c - 'A' + 10; + }, + 'a'...'f' => { + codepoint = c - 'a' + 10; + }, + } + p.state = .StringEscapeHexUnicode2; + p.string_unicode_codepoint |= codepoint << 8; }, - State.StringEscapeHexUnicode2 => switch (c) { - '0'...'9', 'A'...'F', 'a'...'f' => { - p.state = State.StringEscapeHexUnicode1; - }, - else => return error.InvalidUnicodeHexSymbol, + .StringEscapeHexUnicode2 => { + var codepoint: u21 = undefined; + switch (c) { + else => return error.InvalidUnicodeHexSymbol, + '0'...'9' => { + codepoint = c - '0'; + }, + 'A'...'F' => { + codepoint = c - 'A' + 10; + }, + 'a'...'f' => { + codepoint = c - 'a' + 10; + }, + } + p.state = .StringEscapeHexUnicode1; + p.string_unicode_codepoint |= codepoint << 4; }, - State.StringEscapeHexUnicode1 => switch (c) { - '0'...'9', 'A'...'F', 'a'...'f' => { - p.state = State.String; - }, - else => return error.InvalidUnicodeHexSymbol, + .StringEscapeHexUnicode1 => { + var codepoint: u21 = undefined; + switch (c) { + else => return error.InvalidUnicodeHexSymbol, + '0'...'9' => { + codepoint = c - '0'; + }, + 'A'...'F' => { + codepoint = c - 'A' + 10; + }, + 'a'...'f' => { + codepoint = c - 'a' + 10; + }, + } + p.state = .String; + p.string_unicode_codepoint |= codepoint; + if (p.string_unicode_codepoint < 0xD800 or p.string_unicode_codepoint >= 0xE000) { + // not part of surrogate pair + p.string_escapes.Some.size_diff -= @as(isize, 6 - (std.unicode.utf8CodepointSequenceLength(p.string_unicode_codepoint) catch unreachable)); + p.string_last_was_high_surrogate = false; + } else if (p.string_unicode_codepoint < 0xDC00) { + // 'high' surrogate + // takes 3 bytes to encode a half surrogate pair into wtf8 + p.string_escapes.Some.size_diff -= 6 - 3; + p.string_last_was_high_surrogate = true; + } else { + // 'low' surrogate + p.string_escapes.Some.size_diff -= 6; + if (p.string_last_was_high_surrogate) { + // takes 4 bytes to encode a full surrogate pair into utf8 + // 3 bytes are already reserved by high surrogate + p.string_escapes.Some.size_diff -= -1; + } else { + // takes 3 bytes to encode a half surrogate pair into wtf8 + p.string_escapes.Some.size_diff -= -3; + } + p.string_last_was_high_surrogate = false; + } + p.string_unicode_codepoint = undefined; }, - State.Number => { - p.complete = p.after_value_state == State.TopLevelEnd; + .Number => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '0' => { - p.state = State.NumberMaybeDotOrExponent; + p.state = .NumberMaybeDotOrExponent; }, '1'...'9' => { - p.state = State.NumberMaybeDigitOrDotOrExponent; + p.state = .NumberMaybeDigitOrDotOrExponent; }, else => { return error.InvalidNumber; @@ -661,52 +783,63 @@ pub const StreamingParser = struct { } }, - State.NumberMaybeDotOrExponent => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberMaybeDotOrExponent => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '.' => { p.number_is_integer = false; - p.state = State.NumberFractionalRequired; + p.state = .NumberFractionalRequired; }, 'e', 'E' => { p.number_is_integer = false; - p.state = State.NumberExponent; + p.state = .NumberExponent; }, else => { p.state = p.after_value_state; - token.* = Token.initNumber(p.count, p.number_is_integer); + token.* = .{ + .Number = .{ + .count = p.count, + .is_integer = p.number_is_integer, + }, + }; + p.number_is_integer = undefined; return true; }, } }, - State.NumberMaybeDigitOrDotOrExponent => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberMaybeDigitOrDotOrExponent => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '.' => { p.number_is_integer = false; - p.state = State.NumberFractionalRequired; + p.state = .NumberFractionalRequired; }, 'e', 'E' => { p.number_is_integer = false; - p.state = State.NumberExponent; + p.state = .NumberExponent; }, '0'...'9' => { // another digit }, else => { p.state = p.after_value_state; - token.* = Token.initNumber(p.count, p.number_is_integer); + token.* = .{ + .Number = .{ + .count = p.count, + .is_integer = p.number_is_integer, + }, + }; return true; }, } }, - State.NumberFractionalRequired => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberFractionalRequired => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '0'...'9' => { - p.state = State.NumberFractional; + p.state = .NumberFractional; }, else => { return error.InvalidNumber; @@ -714,139 +847,154 @@ pub const StreamingParser = struct { } }, - State.NumberFractional => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberFractional => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '0'...'9' => { // another digit }, 'e', 'E' => { p.number_is_integer = false; - p.state = State.NumberExponent; + p.state = .NumberExponent; }, else => { p.state = p.after_value_state; - token.* = Token.initNumber(p.count, p.number_is_integer); + token.* = .{ + .Number = .{ + .count = p.count, + .is_integer = p.number_is_integer, + }, + }; return true; }, } }, - State.NumberMaybeExponent => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberMaybeExponent => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { 'e', 'E' => { p.number_is_integer = false; - p.state = State.NumberExponent; + p.state = .NumberExponent; }, else => { p.state = p.after_value_state; - token.* = Token.initNumber(p.count, p.number_is_integer); + token.* = .{ + .Number = .{ + .count = p.count, + .is_integer = p.number_is_integer, + }, + }; return true; }, } }, - State.NumberExponent => switch (c) { + .NumberExponent => switch (c) { '-', '+' => { p.complete = false; - p.state = State.NumberExponentDigitsRequired; + p.state = .NumberExponentDigitsRequired; }, '0'...'9' => { - p.complete = p.after_value_state == State.TopLevelEnd; - p.state = State.NumberExponentDigits; + p.complete = p.after_value_state == .TopLevelEnd; + p.state = .NumberExponentDigits; }, else => { return error.InvalidNumber; }, }, - State.NumberExponentDigitsRequired => switch (c) { + .NumberExponentDigitsRequired => switch (c) { '0'...'9' => { - p.complete = p.after_value_state == State.TopLevelEnd; - p.state = State.NumberExponentDigits; + p.complete = p.after_value_state == .TopLevelEnd; + p.state = .NumberExponentDigits; }, else => { return error.InvalidNumber; }, }, - State.NumberExponentDigits => { - p.complete = p.after_value_state == State.TopLevelEnd; + .NumberExponentDigits => { + p.complete = p.after_value_state == .TopLevelEnd; switch (c) { '0'...'9' => { // another digit }, else => { p.state = p.after_value_state; - token.* = Token.initNumber(p.count, p.number_is_integer); + token.* = .{ + .Number = .{ + .count = p.count, + .is_integer = p.number_is_integer, + }, + }; return true; }, } }, - State.TrueLiteral1 => switch (c) { - 'r' => p.state = State.TrueLiteral2, + .TrueLiteral1 => switch (c) { + 'r' => p.state = .TrueLiteral2, else => return error.InvalidLiteral, }, - State.TrueLiteral2 => switch (c) { - 'u' => p.state = State.TrueLiteral3, + .TrueLiteral2 => switch (c) { + 'u' => p.state = .TrueLiteral3, else => return error.InvalidLiteral, }, - State.TrueLiteral3 => switch (c) { + .TrueLiteral3 => switch (c) { 'e' => { p.state = p.after_value_state; - p.complete = p.state == State.TopLevelEnd; - token.* = Token.init(Token.Id.True, p.count + 1, 1); + p.complete = p.state == .TopLevelEnd; + token.* = Token.True; }, else => { return error.InvalidLiteral; }, }, - State.FalseLiteral1 => switch (c) { - 'a' => p.state = State.FalseLiteral2, + .FalseLiteral1 => switch (c) { + 'a' => p.state = .FalseLiteral2, else => return error.InvalidLiteral, }, - State.FalseLiteral2 => switch (c) { - 'l' => p.state = State.FalseLiteral3, + .FalseLiteral2 => switch (c) { + 'l' => p.state = .FalseLiteral3, else => return error.InvalidLiteral, }, - State.FalseLiteral3 => switch (c) { - 's' => p.state = State.FalseLiteral4, + .FalseLiteral3 => switch (c) { + 's' => p.state = .FalseLiteral4, else => return error.InvalidLiteral, }, - State.FalseLiteral4 => switch (c) { + .FalseLiteral4 => switch (c) { 'e' => { p.state = p.after_value_state; - p.complete = p.state == State.TopLevelEnd; - token.* = Token.init(Token.Id.False, p.count + 1, 1); + p.complete = p.state == .TopLevelEnd; + token.* = Token.False; }, else => { return error.InvalidLiteral; }, }, - State.NullLiteral1 => switch (c) { - 'u' => p.state = State.NullLiteral2, + .NullLiteral1 => switch (c) { + 'u' => p.state = .NullLiteral2, else => return error.InvalidLiteral, }, - State.NullLiteral2 => switch (c) { - 'l' => p.state = State.NullLiteral3, + .NullLiteral2 => switch (c) { + 'l' => p.state = .NullLiteral3, else => return error.InvalidLiteral, }, - State.NullLiteral3 => switch (c) { + .NullLiteral3 => switch (c) { 'l' => { p.state = p.after_value_state; - p.complete = p.state == State.TopLevelEnd; - token.* = Token.init(Token.Id.Null, p.count + 1, 1); + p.complete = p.state == .TopLevelEnd; + token.* = Token.Null; }, else => { return error.InvalidLiteral; @@ -858,13 +1006,15 @@ pub const StreamingParser = struct { } }; -// A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens. +/// A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens. pub const TokenStream = struct { i: usize, slice: []const u8, parser: StreamingParser, token: ?Token, + pub const Error = StreamingParser.Error || error{UnexpectedEndOfJson}; + pub fn init(slice: []const u8) TokenStream { return TokenStream{ .i = 0, @@ -874,8 +1024,9 @@ pub const TokenStream = struct { }; } - pub fn next(self: *TokenStream) !?Token { + pub fn next(self: *TokenStream) Error!?Token { if (self.token) |token| { + // TODO: Audit this pattern once #2915 is closed const copy = token; self.token = null; return copy; @@ -894,22 +1045,23 @@ pub const TokenStream = struct { } } - if (self.i > self.slice.len) { - try self.parser.feed(' ', &t1, &t2); - self.i += 1; + // Without this a bare number fails, the streaming parser doesn't know the input ended + try self.parser.feed(' ', &t1, &t2); + self.i += 1; - if (t1) |token| { - return token; - } + if (t1) |token| { + return token; + } else if (self.parser.complete) { + return null; + } else { + return error.UnexpectedEndOfJson; } - - return null; } }; -fn checkNext(p: *TokenStream, id: Token.Id) void { +fn checkNext(p: *TokenStream, id: std.meta.TagType(Token)) void { const token = (p.next() catch unreachable).?; - debug.assert(token.id == id); + debug.assert(std.meta.activeTag(token) == id); } test "json.token" { @@ -932,41 +1084,41 @@ test "json.token" { var p = TokenStream.init(s); - checkNext(&p, Token.Id.ObjectBegin); - checkNext(&p, Token.Id.String); // Image - checkNext(&p, Token.Id.ObjectBegin); - checkNext(&p, Token.Id.String); // Width - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.String); // Height - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.String); // Title - checkNext(&p, Token.Id.String); - checkNext(&p, Token.Id.String); // Thumbnail - checkNext(&p, Token.Id.ObjectBegin); - checkNext(&p, Token.Id.String); // Url - checkNext(&p, Token.Id.String); - checkNext(&p, Token.Id.String); // Height - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.String); // Width - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.ObjectEnd); - checkNext(&p, Token.Id.String); // Animated - checkNext(&p, Token.Id.False); - checkNext(&p, Token.Id.String); // IDs - checkNext(&p, Token.Id.ArrayBegin); - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.Number); - checkNext(&p, Token.Id.ArrayEnd); - checkNext(&p, Token.Id.ObjectEnd); - checkNext(&p, Token.Id.ObjectEnd); + checkNext(&p, .ObjectBegin); + checkNext(&p, .String); // Image + checkNext(&p, .ObjectBegin); + checkNext(&p, .String); // Width + checkNext(&p, .Number); + checkNext(&p, .String); // Height + checkNext(&p, .Number); + checkNext(&p, .String); // Title + checkNext(&p, .String); + checkNext(&p, .String); // Thumbnail + checkNext(&p, .ObjectBegin); + checkNext(&p, .String); // Url + checkNext(&p, .String); + checkNext(&p, .String); // Height + checkNext(&p, .Number); + checkNext(&p, .String); // Width + checkNext(&p, .Number); + checkNext(&p, .ObjectEnd); + checkNext(&p, .String); // Animated + checkNext(&p, .False); + checkNext(&p, .String); // IDs + checkNext(&p, .ArrayBegin); + checkNext(&p, .Number); + checkNext(&p, .Number); + checkNext(&p, .Number); + checkNext(&p, .Number); + checkNext(&p, .ArrayEnd); + checkNext(&p, .ObjectEnd); + checkNext(&p, .ObjectEnd); testing.expect((try p.next()) == null); } -// Validate a JSON string. This does not limit number precision so a decoder may not necessarily -// be able to decode the string even if this returns true. +/// Validate a JSON string. This does not limit number precision so a decoder may not necessarily +/// be able to decode the string even if this returns true. pub fn validate(s: []const u8) bool { var p = StreamingParser.init(); @@ -1001,140 +1153,63 @@ pub const ValueTree = struct { }; pub const ObjectMap = StringHashMap(Value); +pub const Array = ArrayList(Value); +/// Represents a JSON value +/// Currently only supports numbers that fit into i64 or f64. pub const Value = union(enum) { Null, Bool: bool, Integer: i64, Float: f64, String: []const u8, - Array: ArrayList(Value), + Array: Array, Object: ObjectMap, pub fn dump(self: Value) void { - switch (self) { - Value.Null => { - debug.warn("null"); - }, - Value.Bool => |inner| { - debug.warn("{}", inner); - }, - Value.Integer => |inner| { - debug.warn("{}", inner); - }, - Value.Float => |inner| { - debug.warn("{:.5}", inner); - }, - Value.String => |inner| { - debug.warn("\"{}\"", inner); - }, - Value.Array => |inner| { - var not_first = false; - debug.warn("["); - for (inner.toSliceConst()) |value| { - if (not_first) { - debug.warn(","); - } - not_first = true; - value.dump(); - } - debug.warn("]"); - }, - Value.Object => |inner| { - var not_first = false; - debug.warn("{{"); - var it = inner.iterator(); + var held = std.debug.getStderrMutex().acquire(); + defer held.release(); - while (it.next()) |entry| { - if (not_first) { - debug.warn(","); - } - not_first = true; - debug.warn("\"{}\":", entry.key); - entry.value.dump(); - } - debug.warn("}}"); - }, - } + const stderr = std.debug.getStderrStream(); + self.dumpStream(stderr, 1024) catch return; } - pub fn dumpIndent(self: Value, indent: usize) void { + pub fn dumpIndent(self: Value, comptime indent: usize) void { if (indent == 0) { self.dump(); } else { - self.dumpIndentLevel(indent, 0); + var held = std.debug.getStderrMutex().acquire(); + defer held.release(); + + const stderr = std.debug.getStderrStream(); + self.dumpStreamIndent(indent, stderr, 1024) catch return; } } - fn dumpIndentLevel(self: Value, indent: usize, level: usize) void { - switch (self) { - Value.Null => { - debug.warn("null"); - }, - Value.Bool => |inner| { - debug.warn("{}", inner); - }, - Value.Integer => |inner| { - debug.warn("{}", inner); - }, - Value.Float => |inner| { - debug.warn("{:.5}", inner); - }, - Value.String => |inner| { - debug.warn("\"{}\"", inner); - }, - Value.Array => |inner| { - var not_first = false; - debug.warn("[\n"); - - for (inner.toSliceConst()) |value| { - if (not_first) { - debug.warn(",\n"); - } - not_first = true; - padSpace(level + indent); - value.dumpIndentLevel(indent, level + indent); - } - debug.warn("\n"); - padSpace(level); - debug.warn("]"); - }, - Value.Object => |inner| { - var not_first = false; - debug.warn("{{\n"); - var it = inner.iterator(); - - while (it.next()) |entry| { - if (not_first) { - debug.warn(",\n"); - } - not_first = true; - padSpace(level + indent); - debug.warn("\"{}\": ", entry.key); - entry.value.dumpIndentLevel(indent, level + indent); - } - debug.warn("\n"); - padSpace(level); - debug.warn("}}"); - }, - } + pub fn dumpStream(self: @This(), stream: var, comptime max_depth: usize) !void { + var w = std.json.WriteStream(@TypeOf(stream).Child, max_depth).init(stream); + w.newline = ""; + w.one_indent = ""; + w.space = ""; + try w.emitJson(self); } - fn padSpace(indent: usize) void { - var i: usize = 0; - while (i < indent) : (i += 1) { - debug.warn(" "); - } + pub fn dumpStreamIndent(self: @This(), comptime indent: usize, stream: var, comptime max_depth: usize) !void { + var one_indent = " " ** indent; + + var w = std.json.WriteStream(@TypeOf(stream).Child, max_depth).init(stream); + w.one_indent = one_indent; + try w.emitJson(self); } }; -// A non-stream JSON parser which constructs a tree of Value's. +/// A non-stream JSON parser which constructs a tree of Value's. pub const Parser = struct { allocator: *Allocator, state: State, copy_strings: bool, // Stores parent nodes and un-combined Values. - stack: ArrayList(Value), + stack: Array, const State = enum { ObjectKey, @@ -1146,9 +1221,9 @@ pub const Parser = struct { pub fn init(allocator: *Allocator, copy_strings: bool) Parser { return Parser{ .allocator = allocator, - .state = State.Simple, + .state = .Simple, .copy_strings = copy_strings, - .stack = ArrayList(Value).init(allocator), + .stack = Array.init(allocator), }; } @@ -1157,7 +1232,7 @@ pub const Parser = struct { } pub fn reset(p: *Parser) void { - p.state = State.Simple; + p.state = .Simple; p.stack.shrink(0); } @@ -1183,8 +1258,8 @@ pub const Parser = struct { // can be cleaned up on error correctly during a `parse` on call. fn transition(p: *Parser, allocator: *Allocator, input: []const u8, i: usize, token: Token) !void { switch (p.state) { - State.ObjectKey => switch (token.id) { - Token.Id.ObjectEnd => { + .ObjectKey => switch (token) { + .ObjectEnd => { if (p.stack.len == 1) { return; } @@ -1192,62 +1267,65 @@ pub const Parser = struct { var value = p.stack.pop(); try p.pushToParent(&value); }, - Token.Id.String => { - try p.stack.append(try p.parseString(allocator, token, input, i)); - p.state = State.ObjectValue; + .String => |s| { + try p.stack.append(try p.parseString(allocator, s, input, i)); + p.state = .ObjectValue; }, else => { - unreachable; + // The streaming parser would return an error eventually. + // To prevent invalid state we return an error now. + // TODO make the streaming parser return an error as soon as it encounters an invalid object key + return error.InvalidLiteral; }, }, - State.ObjectValue => { + .ObjectValue => { var object = &p.stack.items[p.stack.len - 2].Object; var key = p.stack.items[p.stack.len - 1].String; - switch (token.id) { - Token.Id.ObjectBegin => { + switch (token) { + .ObjectBegin => { try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.ArrayBegin => { - try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); - p.state = State.ArrayValue; + .ArrayBegin => { + try p.stack.append(Value{ .Array = Array.init(allocator) }); + p.state = .ArrayValue; }, - Token.Id.String => { - _ = try object.put(key, try p.parseString(allocator, token, input, i)); + .String => |s| { + _ = try object.put(key, try p.parseString(allocator, s, input, i)); _ = p.stack.pop(); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.Number => { - _ = try object.put(key, try p.parseNumber(token, input, i)); + .Number => |n| { + _ = try object.put(key, try p.parseNumber(n, input, i)); _ = p.stack.pop(); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.True => { + .True => { _ = try object.put(key, Value{ .Bool = true }); _ = p.stack.pop(); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.False => { + .False => { _ = try object.put(key, Value{ .Bool = false }); _ = p.stack.pop(); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.Null => { + .Null => { _ = try object.put(key, Value.Null); _ = p.stack.pop(); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.ObjectEnd, Token.Id.ArrayEnd => { + .ObjectEnd, .ArrayEnd => { unreachable; }, } }, - State.ArrayValue => { + .ArrayValue => { var array = &p.stack.items[p.stack.len - 1].Array; - switch (token.id) { - Token.Id.ArrayEnd => { + switch (token) { + .ArrayEnd => { if (p.stack.len == 1) { return; } @@ -1255,59 +1333,59 @@ pub const Parser = struct { var value = p.stack.pop(); try p.pushToParent(&value); }, - Token.Id.ObjectBegin => { + .ObjectBegin => { try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.ArrayBegin => { - try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); - p.state = State.ArrayValue; + .ArrayBegin => { + try p.stack.append(Value{ .Array = Array.init(allocator) }); + p.state = .ArrayValue; }, - Token.Id.String => { - try array.append(try p.parseString(allocator, token, input, i)); + .String => |s| { + try array.append(try p.parseString(allocator, s, input, i)); }, - Token.Id.Number => { - try array.append(try p.parseNumber(token, input, i)); + .Number => |n| { + try array.append(try p.parseNumber(n, input, i)); }, - Token.Id.True => { + .True => { try array.append(Value{ .Bool = true }); }, - Token.Id.False => { + .False => { try array.append(Value{ .Bool = false }); }, - Token.Id.Null => { + .Null => { try array.append(Value.Null); }, - Token.Id.ObjectEnd => { + .ObjectEnd => { unreachable; }, } }, - State.Simple => switch (token.id) { - Token.Id.ObjectBegin => { + .Simple => switch (token) { + .ObjectBegin => { try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, - Token.Id.ArrayBegin => { - try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); - p.state = State.ArrayValue; + .ArrayBegin => { + try p.stack.append(Value{ .Array = Array.init(allocator) }); + p.state = .ArrayValue; }, - Token.Id.String => { - try p.stack.append(try p.parseString(allocator, token, input, i)); + .String => |s| { + try p.stack.append(try p.parseString(allocator, s, input, i)); }, - Token.Id.Number => { - try p.stack.append(try p.parseNumber(token, input, i)); + .Number => |n| { + try p.stack.append(try p.parseNumber(n, input, i)); }, - Token.Id.True => { + .True => { try p.stack.append(Value{ .Bool = true }); }, - Token.Id.False => { + .False => { try p.stack.append(Value{ .Bool = false }); }, - Token.Id.Null => { + .Null => { try p.stack.append(Value.Null); }, - Token.Id.ObjectEnd, Token.Id.ArrayEnd => { + .ObjectEnd, .ArrayEnd => { unreachable; }, }, @@ -1322,12 +1400,12 @@ pub const Parser = struct { var object = &p.stack.items[p.stack.len - 1].Object; _ = try object.put(key, value.*); - p.state = State.ObjectKey; + p.state = .ObjectKey; }, // Array Parent -> [ ..., , value ] Value.Array => |*array| { try array.append(value.*); - p.state = State.ArrayValue; + p.state = .ArrayValue; }, else => { unreachable; @@ -1335,23 +1413,92 @@ pub const Parser = struct { } } - fn parseString(p: *Parser, allocator: *Allocator, token: Token, input: []const u8, i: usize) !Value { - // TODO: We don't strictly have to copy values which do not contain any escape - // characters if flagged with the option. - const slice = token.slice(input, i); - return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; + fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value { + const slice = s.slice(input, i); + switch (s.escapes) { + .None => return Value{ .String = if (p.copy_strings) try mem.dupe(allocator, u8, slice) else slice }, + .Some => |some_escapes| { + const output = try allocator.alloc(u8, s.decodedLength()); + errdefer allocator.free(output); + try unescapeString(output, slice); + return Value{ .String = output }; + }, + } } - fn parseNumber(p: *Parser, token: Token, input: []const u8, i: usize) !Value { - return if (token.number_is_integer) - Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } + fn parseNumber(p: *Parser, n: std.meta.TagPayloadType(Token, Token.Number), input: []const u8, i: usize) !Value { + return if (n.is_integer) + Value{ .Integer = try std.fmt.parseInt(i64, n.slice(input, i), 10) } else - Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) }; + Value{ .Float = try std.fmt.parseFloat(f64, n.slice(input, i)) }; } }; +// Unescape a JSON string +// Only to be used on strings already validated by the parser +// (note the unreachable statements and lack of bounds checking) +fn unescapeString(output: []u8, input: []const u8) !void { + var inIndex: usize = 0; + var outIndex: usize = 0; + + while (inIndex < input.len) { + if (input[inIndex] != '\\') { + // not an escape sequence + output[outIndex] = input[inIndex]; + inIndex += 1; + outIndex += 1; + } else if (input[inIndex + 1] != 'u') { + // a simple escape sequence + output[outIndex] = @as(u8, switch (input[inIndex + 1]) { + '\\' => '\\', + '/' => '/', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + 'f' => 12, + 'b' => 8, + '"' => '"', + else => unreachable, + }); + inIndex += 2; + outIndex += 1; + } else { + // a unicode escape sequence + const firstCodeUnit = std.fmt.parseInt(u16, input[inIndex + 2 .. inIndex + 6], 16) catch unreachable; + + // guess optimistically that it's not a surrogate pair + if (std.unicode.utf8Encode(firstCodeUnit, output[outIndex..])) |byteCount| { + outIndex += byteCount; + inIndex += 6; + } else |err| { + // it might be a surrogate pair + if (err != error.Utf8CannotEncodeSurrogateHalf) { + return error.InvalidUnicodeHexSymbol; + } + // check if a second code unit is present + if (inIndex + 7 >= input.len or input[inIndex + 6] != '\\' or input[inIndex + 7] != 'u') { + return error.InvalidUnicodeHexSymbol; + } + + const secondCodeUnit = std.fmt.parseInt(u16, input[inIndex + 8 .. inIndex + 12], 16) catch unreachable; + + if (std.unicode.utf16leToUtf8(output[outIndex..], &[2]u16{ firstCodeUnit, secondCodeUnit })) |byteCount| { + outIndex += byteCount; + inIndex += 12; + } else |_| { + return error.InvalidUnicodeHexSymbol; + } + } + } + } + assert(outIndex == output.len); +} + test "json.parser.dynamic" { - var p = Parser.init(debug.global_allocator, false); + var memory: [1024 * 16]u8 = undefined; + var buf_alloc = std.heap.FixedBufferAllocator.init(&memory); + + var p = Parser.init(&buf_alloc.allocator, false); defer p.deinit(); const s = @@ -1404,4 +1551,142 @@ test "json.parser.dynamic" { test "import more json tests" { _ = @import("json/test.zig"); + _ = @import("json/write_stream.zig"); +} + +test "write json then parse it" { + var out_buffer: [1000]u8 = undefined; + + var slice_out_stream = std.io.SliceOutStream.init(&out_buffer); + const out_stream = &slice_out_stream.stream; + var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream); + + try jw.beginObject(); + + try jw.objectField("f"); + try jw.emitBool(false); + + try jw.objectField("t"); + try jw.emitBool(true); + + try jw.objectField("int"); + try jw.emitNumber(@as(i32, 1234)); + + try jw.objectField("array"); + try jw.beginArray(); + + try jw.arrayElem(); + try jw.emitNull(); + + try jw.arrayElem(); + try jw.emitNumber(@as(f64, 12.34)); + + try jw.endArray(); + + try jw.objectField("str"); + try jw.emitString("hello"); + + try jw.endObject(); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var parser = Parser.init(allocator, false); + const tree = try parser.parse(slice_out_stream.getWritten()); + + testing.expect(tree.root.Object.get("f").?.value.Bool == false); + testing.expect(tree.root.Object.get("t").?.value.Bool == true); + testing.expect(tree.root.Object.get("int").?.value.Integer == 1234); + testing.expect(tree.root.Object.get("array").?.value.Array.at(0).Null == {}); + testing.expect(tree.root.Object.get("array").?.value.Array.at(1).Float == 12.34); + testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello")); +} + +fn test_parse(memory: []u8, json_str: []const u8) !Value { + // buf_alloc goes out of scope, but we don't use it after parsing + var buf_alloc = std.heap.FixedBufferAllocator.init(memory); + var p = Parser.init(&buf_alloc.allocator, false); + return (try p.parse(json_str)).root; +} + +test "parsing empty string gives appropriate error" { + var memory: [1024 * 4]u8 = undefined; + testing.expectError(error.UnexpectedEndOfJson, test_parse(&memory, "")); +} + +test "integer after float has proper type" { + var memory: [1024 * 8]u8 = undefined; + const json = try test_parse(&memory, + \\{ + \\ "float": 3.14, + \\ "ints": [1, 2, 3] + \\} + ); + std.testing.expect(json.Object.getValue("ints").?.Array.at(0) == .Integer); +} + +test "escaped characters" { + var memory: [1024 * 16]u8 = undefined; + const input = + \\{ + \\ "backslash": "\\", + \\ "forwardslash": "\/", + \\ "newline": "\n", + \\ "carriagereturn": "\r", + \\ "tab": "\t", + \\ "formfeed": "\f", + \\ "backspace": "\b", + \\ "doublequote": "\"", + \\ "unicode": "\u0105", + \\ "surrogatepair": "\ud83d\ude02" + \\} + ; + + const obj = (try test_parse(&memory, input)).Object; + + testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); + testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); + testing.expectEqualSlices(u8, obj.get("newline").?.value.String, "\n"); + testing.expectEqualSlices(u8, obj.get("carriagereturn").?.value.String, "\r"); + testing.expectEqualSlices(u8, obj.get("tab").?.value.String, "\t"); + testing.expectEqualSlices(u8, obj.get("formfeed").?.value.String, "\x0C"); + testing.expectEqualSlices(u8, obj.get("backspace").?.value.String, "\x08"); + testing.expectEqualSlices(u8, obj.get("doublequote").?.value.String, "\""); + testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ฤ…"); + testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "๐Ÿ˜‚"); +} + +test "string copy option" { + const input = + \\{ + \\ "noescape": "aฤ…๐Ÿ˜‚", + \\ "simple": "\\\/\n\r\t\f\b\"", + \\ "unicode": "\u0105", + \\ "surrogatepair": "\ud83d\ude02" + \\} + ; + + var mem_buffer: [1024 * 16]u8 = undefined; + var buf_alloc = std.heap.FixedBufferAllocator.init(&mem_buffer); + + const tree_nocopy = try Parser.init(&buf_alloc.allocator, false).parse(input); + const obj_nocopy = tree_nocopy.root.Object; + + const tree_copy = try Parser.init(&buf_alloc.allocator, true).parse(input); + const obj_copy = tree_copy.root.Object; + + for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| { + testing.expectEqualSlices(u8, obj_nocopy.getValue(field_name).?.String, obj_copy.getValue(field_name).?.String); + } + + const nocopy_addr = &obj_nocopy.getValue("noescape").?.String[0]; + const copy_addr = &obj_copy.getValue("noescape").?.String[0]; + + var found_nocopy = false; + for (input) |_, index| { + testing.expect(copy_addr != &input[index]); + if (nocopy_addr == &input[index]) { + found_nocopy = true; + } + } + testing.expect(found_nocopy); } diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig index 7c89dcd12..28fdca1b0 100644 --- a/lib/std/json/test.zig +++ b/lib/std/json/test.zig @@ -7,14 +7,60 @@ const std = @import("../std.zig"); fn ok(comptime s: []const u8) void { std.testing.expect(std.json.validate(s)); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var p = std.json.Parser.init(allocator, false); + + _ = p.parse(s) catch unreachable; } fn err(comptime s: []const u8) void { std.testing.expect(!std.json.validate(s)); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var p = std.json.Parser.init(allocator, false); + + if (p.parse(s)) |_| { + unreachable; + } else |_| {} +} + +fn utf8Error(comptime s: []const u8) void { + std.testing.expect(!std.json.validate(s)); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var p = std.json.Parser.init(allocator, false); + + if (p.parse(s)) |_| { + unreachable; + } else |e| { + std.testing.expect(e == error.InvalidUtf8Byte); + } } fn any(comptime s: []const u8) void { - std.testing.expect(true); + _ = std.json.validate(s); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var p = std.json.Parser.init(allocator, false); + + _ = p.parse(s) catch {}; +} + +fn anyStreamingErrNonStreaming(comptime s: []const u8) void { + _ = std.json.validate(s); + + var mem_buffer: [1024 * 20]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buffer).allocator; + var p = std.json.Parser.init(allocator, false); + + if (p.parse(s)) |_| { + unreachable; + } else |_| {} } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -611,9 +657,9 @@ test "n_array_colon_instead_of_comma" { } test "n_array_comma_after_close" { - //err( - // \\[""], - //); + err( + \\[""], + ); } test "n_array_comma_and_number" { @@ -641,9 +687,9 @@ test "n_array_extra_close" { } test "n_array_extra_comma" { - //err( - // \\["",] - //); + err( + \\["",] + ); } test "n_array_incomplete_invalid_value" { @@ -1708,9 +1754,11 @@ test "i_number_double_huge_neg_exp" { } test "i_number_huge_exp" { - any( - \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] - ); + return error.SkipZigTest; + // FIXME Integer overflow in parseFloat + // any( + // \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] + // ); } test "i_number_neg_int_huge_exp" { @@ -1762,49 +1810,49 @@ test "i_number_very_big_negative_int" { } test "i_object_key_lone_2nd_surrogate" { - any( + anyStreamingErrNonStreaming( \\{"\uDFAA":0} ); } test "i_string_1st_surrogate_but_2nd_missing" { - any( + anyStreamingErrNonStreaming( \\["\uDADA"] ); } test "i_string_1st_valid_surrogate_2nd_invalid" { - any( + anyStreamingErrNonStreaming( \\["\uD888\u1234"] ); } test "i_string_incomplete_surrogate_and_escape_valid" { - any( + anyStreamingErrNonStreaming( \\["\uD800\n"] ); } test "i_string_incomplete_surrogate_pair" { - any( + anyStreamingErrNonStreaming( \\["\uDd1ea"] ); } test "i_string_incomplete_surrogates_escape_valid" { - any( + anyStreamingErrNonStreaming( \\["\uD800\uD800\n"] ); } test "i_string_invalid_lonely_surrogate" { - any( + anyStreamingErrNonStreaming( \\["\ud800"] ); } test "i_string_invalid_surrogate" { - any( + anyStreamingErrNonStreaming( \\["\ud800abc"] ); } @@ -1816,7 +1864,7 @@ test "i_string_invalid_utf-8" { } test "i_string_inverted_surrogates_U+1D11E" { - any( + anyStreamingErrNonStreaming( \\["\uDd1e\uD834"] ); } @@ -1828,7 +1876,7 @@ test "i_string_iso_latin_1" { } test "i_string_lone_second_surrogate" { - any( + anyStreamingErrNonStreaming( \\["\uDFAA"] ); } @@ -1902,3 +1950,55 @@ test "i_structure_UTF-8_BOM_empty_object" { \\รฏยปยฟ{} ); } + +test "truncated UTF-8 sequence" { + utf8Error("\"\xc2\""); + utf8Error("\"\xdf\""); + utf8Error("\"\xed\xa0\""); + utf8Error("\"\xf0\x80\""); + utf8Error("\"\xf0\x80\x80\""); +} + +test "invalid continuation byte" { + utf8Error("\"\xc2\x00\""); + utf8Error("\"\xc2\x7f\""); + utf8Error("\"\xc2\xc0\""); + utf8Error("\"\xc3\xc1\""); + utf8Error("\"\xc4\xf5\""); + utf8Error("\"\xc5\xff\""); + utf8Error("\"\xe4\x80\x00\""); + utf8Error("\"\xe5\x80\x10\""); + utf8Error("\"\xe6\x80\xc0\""); + utf8Error("\"\xe7\x80\xf5\""); + utf8Error("\"\xe8\x00\x80\""); + utf8Error("\"\xf2\x00\x80\x80\""); + utf8Error("\"\xf0\x80\x00\x80\""); + utf8Error("\"\xf1\x80\xc0\x80\""); + utf8Error("\"\xf2\x80\x80\x00\""); + utf8Error("\"\xf3\x80\x80\xc0\""); + utf8Error("\"\xf4\x80\x80\xf5\""); +} + +test "disallowed overlong form" { + utf8Error("\"\xc0\x80\""); + utf8Error("\"\xc0\x90\""); + utf8Error("\"\xc1\x80\""); + utf8Error("\"\xc1\x90\""); + utf8Error("\"\xe0\x80\x80\""); + utf8Error("\"\xf0\x80\x80\x80\""); +} + +test "out of UTF-16 range" { + utf8Error("\"\xf4\x90\x80\x80\""); + utf8Error("\"\xf5\x80\x80\x80\""); + utf8Error("\"\xf6\x80\x80\x80\""); + utf8Error("\"\xf7\x80\x80\x80\""); + utf8Error("\"\xf8\x80\x80\x80\""); + utf8Error("\"\xf9\x80\x80\x80\""); + utf8Error("\"\xfa\x80\x80\x80\""); + utf8Error("\"\xfb\x80\x80\x80\""); + utf8Error("\"\xfc\x80\x80\x80\""); + utf8Error("\"\xfd\x80\x80\x80\""); + utf8Error("\"\xfe\x80\x80\x80\""); + utf8Error("\"\xff\x80\x80\x80\""); +} diff --git a/lib/std/json/write_stream.zig b/lib/std/json/write_stream.zig new file mode 100644 index 000000000..51afe7fcb --- /dev/null +++ b/lib/std/json/write_stream.zig @@ -0,0 +1,308 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const maxInt = std.math.maxInt; + +const State = enum { + Complete, + Value, + ArrayStart, + Array, + ObjectStart, + Object, +}; + +/// Writes JSON ([RFC8259](https://tools.ietf.org/html/rfc8259)) formatted data +/// to a stream. `max_depth` is a comptime-known upper bound on the nesting depth. +/// TODO A future iteration of this API will allow passing `null` for this value, +/// and disable safety checks in release builds. +pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type { + return struct { + const Self = @This(); + + pub const Stream = OutStream; + + /// The string used for indenting. + one_indent: []const u8 = " ", + + /// The string used as a newline character. + newline: []const u8 = "\n", + + /// The string used as spacing. + space: []const u8 = " ", + + stream: *OutStream, + state_index: usize, + state: [max_depth]State, + + pub fn init(stream: *OutStream) Self { + var self = Self{ + .stream = stream, + .state_index = 1, + .state = undefined, + }; + self.state[0] = .Complete; + self.state[1] = .Value; + return self; + } + + pub fn beginArray(self: *Self) !void { + assert(self.state[self.state_index] == State.Value); // need to call arrayElem or objectField + try self.stream.writeByte('['); + self.state[self.state_index] = State.ArrayStart; + } + + pub fn beginObject(self: *Self) !void { + assert(self.state[self.state_index] == State.Value); // need to call arrayElem or objectField + try self.stream.writeByte('{'); + self.state[self.state_index] = State.ObjectStart; + } + + pub fn arrayElem(self: *Self) !void { + const state = self.state[self.state_index]; + switch (state) { + .Complete => unreachable, + .Value => unreachable, + .ObjectStart => unreachable, + .Object => unreachable, + .Array, .ArrayStart => { + if (state == .Array) { + try self.stream.writeByte(','); + } + self.state[self.state_index] = .Array; + self.pushState(.Value); + try self.indent(); + }, + } + } + + pub fn objectField(self: *Self, name: []const u8) !void { + const state = self.state[self.state_index]; + switch (state) { + .Complete => unreachable, + .Value => unreachable, + .ArrayStart => unreachable, + .Array => unreachable, + .Object, .ObjectStart => { + if (state == .Object) { + try self.stream.writeByte(','); + } + self.state[self.state_index] = .Object; + self.pushState(.Value); + try self.indent(); + try self.writeEscapedString(name); + try self.stream.write(":"); + try self.stream.write(self.space); + }, + } + } + + pub fn endArray(self: *Self) !void { + switch (self.state[self.state_index]) { + .Complete => unreachable, + .Value => unreachable, + .ObjectStart => unreachable, + .Object => unreachable, + .ArrayStart => { + try self.stream.writeByte(']'); + self.popState(); + }, + .Array => { + try self.indent(); + self.popState(); + try self.stream.writeByte(']'); + }, + } + } + + pub fn endObject(self: *Self) !void { + switch (self.state[self.state_index]) { + .Complete => unreachable, + .Value => unreachable, + .ArrayStart => unreachable, + .Array => unreachable, + .ObjectStart => { + try self.stream.writeByte('}'); + self.popState(); + }, + .Object => { + try self.indent(); + self.popState(); + try self.stream.writeByte('}'); + }, + } + } + + pub fn emitNull(self: *Self) !void { + assert(self.state[self.state_index] == State.Value); + try self.stream.write("null"); + self.popState(); + } + + pub fn emitBool(self: *Self, value: bool) !void { + assert(self.state[self.state_index] == State.Value); + if (value) { + try self.stream.write("true"); + } else { + try self.stream.write("false"); + } + self.popState(); + } + + pub fn emitNumber( + self: *Self, + /// An integer, float, or `std.math.BigInt`. Emitted as a bare number if it fits losslessly + /// in a IEEE 754 double float, otherwise emitted as a string to the full precision. + value: var, + ) !void { + assert(self.state[self.state_index] == State.Value); + switch (@typeInfo(@TypeOf(value))) { + .Int => |info| { + if (info.bits < 53) { + try self.stream.print("{}", .{value}); + self.popState(); + return; + } + if (value < 4503599627370496 and (!info.is_signed or value > -4503599627370496)) { + try self.stream.print("{}", .{value}); + self.popState(); + return; + } + }, + .Float => if (@floatCast(f64, value) == value) { + try self.stream.print("{}", .{value}); + self.popState(); + return; + }, + else => {}, + } + try self.stream.print("\"{}\"", .{value}); + self.popState(); + } + + pub fn emitString(self: *Self, string: []const u8) !void { + try self.writeEscapedString(string); + self.popState(); + } + + fn writeEscapedString(self: *Self, string: []const u8) !void { + try self.stream.writeByte('"'); + for (string) |s| { + switch (s) { + '"' => try self.stream.write("\\\""), + '\t' => try self.stream.write("\\t"), + '\r' => try self.stream.write("\\r"), + '\n' => try self.stream.write("\\n"), + 8 => try self.stream.write("\\b"), + 12 => try self.stream.write("\\f"), + '\\' => try self.stream.write("\\\\"), + else => try self.stream.writeByte(s), + } + } + try self.stream.writeByte('"'); + } + + /// Writes the complete json into the output stream + pub fn emitJson(self: *Self, json: std.json.Value) Stream.Error!void { + switch (json) { + .Null => try self.emitNull(), + .Bool => |inner| try self.emitBool(inner), + .Integer => |inner| try self.emitNumber(inner), + .Float => |inner| try self.emitNumber(inner), + .String => |inner| try self.emitString(inner), + .Array => |inner| { + try self.beginArray(); + for (inner.toSliceConst()) |elem| { + try self.arrayElem(); + try self.emitJson(elem); + } + try self.endArray(); + }, + .Object => |inner| { + try self.beginObject(); + var it = inner.iterator(); + while (it.next()) |entry| { + try self.objectField(entry.key); + try self.emitJson(entry.value); + } + try self.endObject(); + }, + } + } + + fn indent(self: *Self) !void { + assert(self.state_index >= 1); + try self.stream.write(self.newline); + var i: usize = 0; + while (i < self.state_index - 1) : (i += 1) { + try self.stream.write(self.one_indent); + } + } + + fn pushState(self: *Self, state: State) void { + self.state_index += 1; + self.state[self.state_index] = state; + } + + fn popState(self: *Self) void { + self.state_index -= 1; + } + }; +} + +test "json write stream" { + var out_buf: [1024]u8 = undefined; + var slice_stream = std.io.SliceOutStream.init(&out_buf); + const out = &slice_stream.stream; + + var mem_buf: [1024 * 10]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(&mem_buf).allocator; + + var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out); + try w.emitJson(try getJson(allocator)); + + const result = slice_stream.getWritten(); + const expected = + \\{ + \\ "object": { + \\ "one": 1, + \\ "two": 2.0e+00 + \\ }, + \\ "string": "This is a string", + \\ "array": [ + \\ "Another string", + \\ 1, + \\ 3.14e+00 + \\ ], + \\ "int": 10, + \\ "float": 3.14e+00 + \\} + ; + std.testing.expect(std.mem.eql(u8, expected, result)); +} + +fn getJson(allocator: *std.mem.Allocator) !std.json.Value { + var value = std.json.Value{ .Object = std.json.ObjectMap.init(allocator) }; + _ = try value.Object.put("string", std.json.Value{ .String = "This is a string" }); + _ = try value.Object.put("int", std.json.Value{ .Integer = @intCast(i64, 10) }); + _ = try value.Object.put("float", std.json.Value{ .Float = 3.14 }); + _ = try value.Object.put("array", try getJsonArray(allocator)); + _ = try value.Object.put("object", try getJsonObject(allocator)); + return value; +} + +fn getJsonObject(allocator: *std.mem.Allocator) !std.json.Value { + var value = std.json.Value{ .Object = std.json.ObjectMap.init(allocator) }; + _ = try value.Object.put("one", std.json.Value{ .Integer = @intCast(i64, 1) }); + _ = try value.Object.put("two", std.json.Value{ .Float = 2.0 }); + return value; +} + +fn getJsonArray(allocator: *std.mem.Allocator) !std.json.Value { + var value = std.json.Value{ .Array = std.json.Array.init(allocator) }; + var array = &value.Array; + _ = try array.append(std.json.Value{ .String = "Another string" }); + _ = try array.append(std.json.Value{ .Integer = @intCast(i64, 1) }); + _ = try array.append(std.json.Value{ .Float = 3.14 }); + + return value; +} diff --git a/lib/std/lazy_init.zig b/lib/std/lazy_init.zig index 7beabb9cd..dc792b398 100644 --- a/lib/std/lazy_init.zig +++ b/lib/std/lazy_init.zig @@ -1,24 +1,26 @@ const std = @import("std.zig"); -const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; -const AtomicRmwOp = builtin.AtomicRmwOp; -const AtomicOrder = builtin.AtomicOrder; /// Thread-safe initialization of global data. /// TODO use a mutex instead of a spinlock pub fn lazyInit(comptime T: type) LazyInit(T) { return LazyInit(T){ .data = undefined, - .state = 0, }; } fn LazyInit(comptime T: type) type { return struct { - state: u8, // TODO make this an enum + state: State = .NotResolved, data: Data, + const State = enum(u8) { + NotResolved, + Resolving, + Resolved, + }; + const Self = @This(); // TODO this isn't working for void, investigate and then remove this special case @@ -30,16 +32,16 @@ fn LazyInit(comptime T: type) type { /// perform the initialization and then call resolve(). pub fn get(self: *Self) ?Ptr { while (true) { - var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + var state = @cmpxchgWeak(State, &self.state, .NotResolved, .Resolving, .SeqCst, .SeqCst) orelse return null; switch (state) { - 0 => continue, - 1 => { + .NotResolved => continue, + .Resolving => { // TODO mutex instead of a spinlock continue; }, - 2 => { + .Resolved => { if (@sizeOf(T) == 0) { - return T(undefined); + return @as(T, undefined); } else { return &self.data; } @@ -50,8 +52,8 @@ fn LazyInit(comptime T: type) type { } pub fn resolve(self: *Self) void { - const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); - assert(prev == 1); // resolve() called twice + const prev = @atomicRmw(State, &self.state, .Xchg, .Resolved, .SeqCst); + assert(prev != .Resolved); // resolve() called twice } }; } diff --git a/lib/std/math.zig b/lib/std/math.zig index e47021512..5885964c3 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -4,14 +4,41 @@ const TypeId = builtin.TypeId; const assert = std.debug.assert; const testing = std.testing; +/// Euler's number (e) pub const e = 2.71828182845904523536028747135266249775724709369995; + +/// Archimedes' constant (ฯ€) pub const pi = 3.14159265358979323846264338327950288419716939937510; +/// Circle constant (ฯ„) +pub const tau = 2 * pi; + +/// log2(e) +pub const log2e = 1.442695040888963407359924681001892137; + +/// log10(e) +pub const log10e = 0.434294481903251827651128918916605082; + +/// ln(2) +pub const ln2 = 0.693147180559945309417232121458176568; + +/// ln(10) +pub const ln10 = 2.302585092994045684017991454684364208; + +/// 2/sqrt(ฯ€) +pub const two_sqrtpi = 1.128379167095512573896158903121545172; + +/// sqrt(2) +pub const sqrt2 = 1.414213562373095048801688724209698079; + +/// 1/sqrt(2) +pub const sqrt1_2 = 0.707106781186547524400844362104849039; + // From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) -pub const f128_true_min = @bitCast(f128, u128(0x00000000000000000000000000000001)); -pub const f128_min = @bitCast(f128, u128(0x00010000000000000000000000000000)); -pub const f128_max = @bitCast(f128, u128(0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); -pub const f128_epsilon = @bitCast(f128, u128(0x3F8F0000000000000000000000000000)); +pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); +pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); +pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); +pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); pub const f128_toint = 1.0 / f128_epsilon; // float.h details @@ -33,28 +60,28 @@ pub const f16_max = 65504; pub const f16_epsilon = 0.0009765625; // 2**-10 pub const f16_toint = 1.0 / f16_epsilon; -pub const nan_u16 = u16(0x7C01); +pub const nan_u16 = @as(u16, 0x7C01); pub const nan_f16 = @bitCast(f16, nan_u16); -pub const inf_u16 = u16(0x7C00); +pub const inf_u16 = @as(u16, 0x7C00); pub const inf_f16 = @bitCast(f16, inf_u16); -pub const nan_u32 = u32(0x7F800001); +pub const nan_u32 = @as(u32, 0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); -pub const inf_u32 = u32(0x7F800000); +pub const inf_u32 = @as(u32, 0x7F800000); pub const inf_f32 = @bitCast(f32, inf_u32); -pub const nan_u64 = u64(0x7FF << 52) | 1; +pub const nan_u64 = @as(u64, 0x7FF << 52) | 1; pub const nan_f64 = @bitCast(f64, nan_u64); -pub const inf_u64 = u64(0x7FF << 52); +pub const inf_u64 = @as(u64, 0x7FF << 52); pub const inf_f64 = @bitCast(f64, inf_u64); -pub const nan_u128 = u128(0x7fff0000000000000000000000000001); +pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); pub const nan_f128 = @bitCast(f128, nan_u128); -pub const inf_u128 = u128(0x7fff0000000000000000000000000000); +pub const inf_u128 = @as(u128, 0x7fff0000000000000000000000000000); pub const inf_f128 = @bitCast(f128, inf_u128); pub const nan = @import("math/nan.zig").nan; @@ -68,7 +95,7 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { // TODO: Hide the following in an internal module. pub fn forceEval(value: var) void { - const T = @typeOf(value); + const T = @TypeOf(value); switch (T) { f16 => { var x: f16 = undefined; @@ -166,54 +193,8 @@ pub const Complex = complex.Complex; pub const big = @import("math/big.zig"); -test "math" { - _ = @import("math/nan.zig"); - _ = @import("math/isnan.zig"); - _ = @import("math/fabs.zig"); - _ = @import("math/ceil.zig"); - _ = @import("math/floor.zig"); - _ = @import("math/trunc.zig"); - _ = @import("math/round.zig"); - _ = @import("math/frexp.zig"); - _ = @import("math/modf.zig"); - _ = @import("math/copysign.zig"); - _ = @import("math/isfinite.zig"); - _ = @import("math/isinf.zig"); - _ = @import("math/isnormal.zig"); - _ = @import("math/signbit.zig"); - _ = @import("math/scalbn.zig"); - _ = @import("math/pow.zig"); - _ = @import("math/powi.zig"); - _ = @import("math/sqrt.zig"); - _ = @import("math/cbrt.zig"); - _ = @import("math/acos.zig"); - _ = @import("math/asin.zig"); - _ = @import("math/atan.zig"); - _ = @import("math/atan2.zig"); - _ = @import("math/hypot.zig"); - _ = @import("math/exp.zig"); - _ = @import("math/exp2.zig"); - _ = @import("math/expm1.zig"); - _ = @import("math/ilogb.zig"); - _ = @import("math/ln.zig"); - _ = @import("math/log.zig"); - _ = @import("math/log2.zig"); - _ = @import("math/log10.zig"); - _ = @import("math/log1p.zig"); - _ = @import("math/fma.zig"); - _ = @import("math/asinh.zig"); - _ = @import("math/acosh.zig"); - _ = @import("math/atanh.zig"); - _ = @import("math/sinh.zig"); - _ = @import("math/cosh.zig"); - _ = @import("math/tanh.zig"); - _ = @import("math/sin.zig"); - _ = @import("math/cos.zig"); - _ = @import("math/tan.zig"); - - _ = @import("math/complex.zig"); - - _ = @import("math/big.zig"); +test "" { + std.meta.refAllDecls(@This()); } pub fn floatMantissaBits(comptime T: type) comptime_int { @@ -258,13 +239,13 @@ pub fn Min(comptime A: type, comptime B: type) type { }, else => {}, } - return @typeOf(A(0) + B(0)); + return @TypeOf(@as(A, 0) + @as(B, 0)); } /// Returns the smaller number. When one of the parameter's type's full range fits in the other, /// the return type is the smaller type. -pub fn min(x: var, y: var) Min(@typeOf(x), @typeOf(y)) { - const Result = Min(@typeOf(x), @typeOf(y)); +pub fn min(x: var, y: var) Min(@TypeOf(x), @TypeOf(y)) { + const Result = Min(@TypeOf(x), @TypeOf(y)); if (x < y) { // TODO Zig should allow this as an implicit cast because x is immutable and in this // scope it is known to fit in the return type. @@ -283,43 +264,62 @@ pub fn min(x: var, y: var) Min(@typeOf(x), @typeOf(y)) { } test "math.min" { - testing.expect(min(i32(-1), i32(2)) == -1); + testing.expect(min(@as(i32, -1), @as(i32, 2)) == -1); { var a: u16 = 999; var b: u32 = 10; var result = min(a, b); - testing.expect(@typeOf(result) == u16); + testing.expect(@TypeOf(result) == u16); testing.expect(result == 10); } { var a: f64 = 10.34; var b: f32 = 999.12; var result = min(a, b); - testing.expect(@typeOf(result) == f64); + testing.expect(@TypeOf(result) == f64); testing.expect(result == 10.34); } { var a: i8 = -127; var b: i16 = -200; var result = min(a, b); - testing.expect(@typeOf(result) == i16); + testing.expect(@TypeOf(result) == i16); testing.expect(result == -200); } { const a = 10.34; var b: f32 = 999.12; var result = min(a, b); - testing.expect(@typeOf(result) == f32); + testing.expect(@TypeOf(result) == f32); testing.expect(result == 10.34); } } -pub fn max(x: var, y: var) @typeOf(x + y) { +pub fn max(x: var, y: var) @TypeOf(x + y) { return if (x > y) x else y; } test "math.max" { - testing.expect(max(i32(-1), i32(2)) == 2); + testing.expect(max(@as(i32, -1), @as(i32, 2)) == 2); +} + +pub fn clamp(clamped_val: var, bound_1: var, bound_2: var) Min(@TypeOf(bound_1), @TypeOf(bound_2)) { + const upper_bound = max(bound_1, bound_2); + const lower_bound = min(bound_1, bound_2); + return min(upper_bound, max(clamped_val, lower_bound)); +} +test "math.clamp" { + // Within range + testing.expect(std.math.clamp(@as(i32, -1), @as(i32, -4), @as(i32, 7)) == -1); + // Below + testing.expect(std.math.clamp(@as(i32, -5), @as(i32, -4), @as(i32, 7)) == -4); + // Above + testing.expect(std.math.clamp(@as(i32, 8), @as(i32, -4), @as(i32, 7)) == 7); + + // Reverse + testing.expect(std.math.clamp(@as(i32, -1), @as(i32, 7), @as(i32, -4)) == -1); + testing.expect(std.math.clamp(@as(i32, -5), @as(i32, 7), @as(i32, -4)) == -4); + testing.expect(std.math.clamp(@as(i32, 8), @as(i32, 7), @as(i32, -4)) == 7); } pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { @@ -337,8 +337,8 @@ pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -pub fn negate(x: var) !@typeOf(x) { - return sub(@typeOf(x), 0, x); +pub fn negate(x: var) !@TypeOf(x) { + return sub(@TypeOf(x), 0, x); } pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { @@ -352,7 +352,7 @@ pub fn shl(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); - if (@typeOf(shift_amt) == comptime_int or @typeOf(shift_amt).is_signed) { + if (@TypeOf(shift_amt) == comptime_int or @TypeOf(shift_amt).is_signed) { if (shift_amt < 0) { return a >> casted_shift_amt; } @@ -362,10 +362,10 @@ pub fn shl(comptime T: type, a: T, shift_amt: var) T { } test "math.shl" { - testing.expect(shl(u8, 0b11111111, usize(3)) == 0b11111000); - testing.expect(shl(u8, 0b11111111, usize(8)) == 0); - testing.expect(shl(u8, 0b11111111, usize(9)) == 0); - testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111); + testing.expect(shl(u8, 0b11111111, @as(usize, 3)) == 0b11111000); + testing.expect(shl(u8, 0b11111111, @as(usize, 8)) == 0); + testing.expect(shl(u8, 0b11111111, @as(usize, 9)) == 0); + testing.expect(shl(u8, 0b11111111, @as(isize, -2)) == 0b00111111); testing.expect(shl(u8, 0b11111111, 3) == 0b11111000); testing.expect(shl(u8, 0b11111111, 8) == 0); testing.expect(shl(u8, 0b11111111, 9) == 0); @@ -378,7 +378,7 @@ pub fn shr(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); - if (@typeOf(shift_amt) == comptime_int or @typeOf(shift_amt).is_signed) { + if (@TypeOf(shift_amt) == comptime_int or @TypeOf(shift_amt).is_signed) { if (shift_amt >= 0) { return a >> casted_shift_amt; } else { @@ -390,10 +390,10 @@ pub fn shr(comptime T: type, a: T, shift_amt: var) T { } test "math.shr" { - testing.expect(shr(u8, 0b11111111, usize(3)) == 0b00011111); - testing.expect(shr(u8, 0b11111111, usize(8)) == 0); - testing.expect(shr(u8, 0b11111111, usize(9)) == 0); - testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100); + testing.expect(shr(u8, 0b11111111, @as(usize, 3)) == 0b00011111); + testing.expect(shr(u8, 0b11111111, @as(usize, 8)) == 0); + testing.expect(shr(u8, 0b11111111, @as(usize, 9)) == 0); + testing.expect(shr(u8, 0b11111111, @as(isize, -2)) == 0b11111100); testing.expect(shr(u8, 0b11111111, 3) == 0b00011111); testing.expect(shr(u8, 0b11111111, 8) == 0); testing.expect(shr(u8, 0b11111111, 9) == 0); @@ -412,11 +412,11 @@ pub fn rotr(comptime T: type, x: T, r: var) T { } test "math.rotr" { - testing.expect(rotr(u8, 0b00000001, usize(0)) == 0b00000001); - testing.expect(rotr(u8, 0b00000001, usize(9)) == 0b10000000); - testing.expect(rotr(u8, 0b00000001, usize(8)) == 0b00000001); - testing.expect(rotr(u8, 0b00000001, usize(4)) == 0b00010000); - testing.expect(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); + testing.expect(rotr(u8, 0b00000001, @as(usize, 0)) == 0b00000001); + testing.expect(rotr(u8, 0b00000001, @as(usize, 9)) == 0b10000000); + testing.expect(rotr(u8, 0b00000001, @as(usize, 8)) == 0b00000001); + testing.expect(rotr(u8, 0b00000001, @as(usize, 4)) == 0b00010000); + testing.expect(rotr(u8, 0b00000001, @as(isize, -1)) == 0b00000010); } /// Rotates left. Only unsigned values can be rotated. @@ -431,11 +431,11 @@ pub fn rotl(comptime T: type, x: T, r: var) T { } test "math.rotl" { - testing.expect(rotl(u8, 0b00000001, usize(0)) == 0b00000001); - testing.expect(rotl(u8, 0b00000001, usize(9)) == 0b00000010); - testing.expect(rotl(u8, 0b00000001, usize(8)) == 0b00000001); - testing.expect(rotl(u8, 0b00000001, usize(4)) == 0b00010000); - testing.expect(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); + testing.expect(rotl(u8, 0b00000001, @as(usize, 0)) == 0b00000001); + testing.expect(rotl(u8, 0b00000001, @as(usize, 9)) == 0b00000010); + testing.expect(rotl(u8, 0b00000001, @as(usize, 8)) == 0b00000001); + testing.expect(rotl(u8, 0b00000001, @as(usize, 4)) == 0b00010000); + testing.expect(rotl(u8, 0b00000001, @as(isize, -1)) == 0b10000000); } pub fn Log2Int(comptime T: type) type { @@ -524,12 +524,12 @@ fn testOverflow() void { testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); } -pub fn absInt(x: var) !@typeOf(x) { - const T = @typeOf(x); +pub fn absInt(x: var) !@TypeOf(x) { + const T = @TypeOf(x); comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt comptime assert(T.is_signed); // must pass a signed integer to absInt - if (x == minInt(@typeOf(x))) { + if (x == minInt(@TypeOf(x))) { return error.Overflow; } else { @setRuntimeSafety(false); @@ -542,8 +542,8 @@ test "math.absInt" { comptime testAbsInt(); } fn testAbsInt() void { - testing.expect((absInt(i32(-10)) catch unreachable) == 10); - testing.expect((absInt(i32(10)) catch unreachable) == 10); + testing.expect((absInt(@as(i32, -10)) catch unreachable) == 10); + testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10); } pub const absFloat = fabs; @@ -553,8 +553,8 @@ test "math.absFloat" { comptime testAbsFloat(); } fn testAbsFloat() void { - testing.expect(absFloat(f32(-10.05)) == 10.05); - testing.expect(absFloat(f32(10.05)) == 10.05); + testing.expect(absFloat(@as(f32, -10.05)) == 10.05); + testing.expect(absFloat(@as(f32, 10.05)) == 10.05); } pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @@ -673,40 +673,40 @@ fn testRem() void { /// Returns the absolute value of the integer parameter. /// Result is an unsigned integer. pub fn absCast(x: var) t: { - if (@typeOf(x) == comptime_int) { + if (@TypeOf(x) == comptime_int) { break :t comptime_int; } else { - break :t @IntType(false, @typeOf(x).bit_count); + break :t @IntType(false, @TypeOf(x).bit_count); } } { - if (@typeOf(x) == comptime_int) { + if (@TypeOf(x) == comptime_int) { return if (x < 0) -x else x; } - const uint = @IntType(false, @typeOf(x).bit_count); + const uint = @IntType(false, @TypeOf(x).bit_count); if (x >= 0) return @intCast(uint, x); return @intCast(uint, -(x + 1)) + 1; } test "math.absCast" { - testing.expect(absCast(i32(-999)) == 999); - testing.expect(@typeOf(absCast(i32(-999))) == u32); + testing.expect(absCast(@as(i32, -999)) == 999); + testing.expect(@TypeOf(absCast(@as(i32, -999))) == u32); - testing.expect(absCast(i32(999)) == 999); - testing.expect(@typeOf(absCast(i32(999))) == u32); + testing.expect(absCast(@as(i32, 999)) == 999); + testing.expect(@TypeOf(absCast(@as(i32, 999))) == u32); - testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); - testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); + testing.expect(absCast(@as(i32, minInt(i32))) == -minInt(i32)); + testing.expect(@TypeOf(absCast(@as(i32, minInt(i32)))) == u32); testing.expect(absCast(-999) == 999); } /// Returns the negation of the integer parameter. /// Result is a signed integer. -pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { - if (@typeOf(x).is_signed) return negate(x); +pub fn negateCast(x: var) !@IntType(true, @TypeOf(x).bit_count) { + if (@TypeOf(x).is_signed) return negate(x); - const int = @IntType(true, @typeOf(x).bit_count); + const int = @IntType(true, @TypeOf(x).bit_count); if (x > -minInt(int)) return error.Overflow; if (x == -minInt(int)) return minInt(int); @@ -715,23 +715,23 @@ pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { } test "math.negateCast" { - testing.expect((negateCast(u32(999)) catch unreachable) == -999); - testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); + testing.expect((negateCast(@as(u32, 999)) catch unreachable) == -999); + testing.expect(@TypeOf(negateCast(@as(u32, 999)) catch unreachable) == i32); - testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); - testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); + testing.expect((negateCast(@as(u32, -minInt(i32))) catch unreachable) == minInt(i32)); + testing.expect(@TypeOf(negateCast(@as(u32, -minInt(i32))) catch unreachable) == i32); - testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); + testing.expectError(error.Overflow, negateCast(@as(u32, maxInt(i32) + 10))); } /// Cast an integer to a different integer type. If the value doesn't fit, /// return an error. pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer - comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer - if (maxInt(@typeOf(x)) > maxInt(T) and x > maxInt(T)) { + comptime assert(@typeId(@TypeOf(x)) == builtin.TypeId.Int); // must pass an integer + if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) { return error.Overflow; - } else if (minInt(@typeOf(x)) < minInt(T) and x < minInt(T)) { + } else if (minInt(@TypeOf(x)) < minInt(T) and x < minInt(T)) { return error.Overflow; } else { return @intCast(T, x); @@ -739,19 +739,19 @@ pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { } test "math.cast" { - testing.expectError(error.Overflow, cast(u8, u32(300))); - testing.expectError(error.Overflow, cast(i8, i32(-200))); - testing.expectError(error.Overflow, cast(u8, i8(-1))); - testing.expectError(error.Overflow, cast(u64, i8(-1))); + testing.expectError(error.Overflow, cast(u8, @as(u32, 300))); + testing.expectError(error.Overflow, cast(i8, @as(i32, -200))); + testing.expectError(error.Overflow, cast(u8, @as(i8, -1))); + testing.expectError(error.Overflow, cast(u64, @as(i8, -1))); - testing.expect((try cast(u8, u32(255))) == u8(255)); - testing.expect(@typeOf(try cast(u8, u32(255))) == u8); + testing.expect((try cast(u8, @as(u32, 255))) == @as(u8, 255)); + testing.expect(@TypeOf(try cast(u8, @as(u32, 255))) == u8); } pub const AlignCastError = error{UnalignedMemory}; /// Align cast a pointer but return an error if it's the wrong alignment -pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { +pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) { const addr = @ptrToInt(ptr); if (addr % alignment != 0) { return error.UnalignedMemory; @@ -796,9 +796,9 @@ pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) @IntType(T.is_signed, T comptime assert(@typeId(T) == builtin.TypeId.Int); comptime assert(!T.is_signed); assert(value != 0); - comptime const promotedType = @IntType(T.is_signed, T.bit_count + 1); - comptime const shiftType = std.math.Log2Int(promotedType); - return promotedType(1) << @intCast(shiftType, T.bit_count - @clz(T, value - 1)); + comptime const PromotedType = @IntType(T.is_signed, T.bit_count + 1); + comptime const shiftType = std.math.Log2Int(PromotedType); + return @as(PromotedType, 1) << @intCast(shiftType, T.bit_count - @clz(T, value - 1)); } /// Returns the next power of two (if the value is not already a power of two). @@ -807,8 +807,8 @@ pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) @IntType(T.is_signed, T pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) { comptime assert(@typeId(T) == builtin.TypeId.Int); comptime assert(!T.is_signed); - comptime const promotedType = @IntType(T.is_signed, T.bit_count + 1); - comptime const overflowBit = promotedType(1) << T.bit_count; + comptime const PromotedType = @IntType(T.is_signed, T.bit_count + 1); + comptime const overflowBit = @as(PromotedType, 1) << T.bit_count; var x = ceilPowerOfTwoPromote(T, value); if (overflowBit & x != 0) { return error.Overflow; @@ -822,15 +822,15 @@ test "math.ceilPowerOfTwoPromote" { } fn testCeilPowerOfTwoPromote() void { - testing.expectEqual(u33(1), ceilPowerOfTwoPromote(u32, 1)); - testing.expectEqual(u33(2), ceilPowerOfTwoPromote(u32, 2)); - testing.expectEqual(u33(64), ceilPowerOfTwoPromote(u32, 63)); - testing.expectEqual(u33(64), ceilPowerOfTwoPromote(u32, 64)); - testing.expectEqual(u33(128), ceilPowerOfTwoPromote(u32, 65)); - testing.expectEqual(u6(8), ceilPowerOfTwoPromote(u5, 7)); - testing.expectEqual(u6(8), ceilPowerOfTwoPromote(u5, 8)); - testing.expectEqual(u6(16), ceilPowerOfTwoPromote(u5, 9)); - testing.expectEqual(u5(16), ceilPowerOfTwoPromote(u4, 9)); + testing.expectEqual(@as(u33, 1), ceilPowerOfTwoPromote(u32, 1)); + testing.expectEqual(@as(u33, 2), ceilPowerOfTwoPromote(u32, 2)); + testing.expectEqual(@as(u33, 64), ceilPowerOfTwoPromote(u32, 63)); + testing.expectEqual(@as(u33, 64), ceilPowerOfTwoPromote(u32, 64)); + testing.expectEqual(@as(u33, 128), ceilPowerOfTwoPromote(u32, 65)); + testing.expectEqual(@as(u6, 8), ceilPowerOfTwoPromote(u5, 7)); + testing.expectEqual(@as(u6, 8), ceilPowerOfTwoPromote(u5, 8)); + testing.expectEqual(@as(u6, 16), ceilPowerOfTwoPromote(u5, 9)); + testing.expectEqual(@as(u5, 16), ceilPowerOfTwoPromote(u4, 9)); } test "math.ceilPowerOfTwo" { @@ -839,14 +839,14 @@ test "math.ceilPowerOfTwo" { } fn testCeilPowerOfTwo() !void { - testing.expectEqual(u32(1), try ceilPowerOfTwo(u32, 1)); - testing.expectEqual(u32(2), try ceilPowerOfTwo(u32, 2)); - testing.expectEqual(u32(64), try ceilPowerOfTwo(u32, 63)); - testing.expectEqual(u32(64), try ceilPowerOfTwo(u32, 64)); - testing.expectEqual(u32(128), try ceilPowerOfTwo(u32, 65)); - testing.expectEqual(u5(8), try ceilPowerOfTwo(u5, 7)); - testing.expectEqual(u5(8), try ceilPowerOfTwo(u5, 8)); - testing.expectEqual(u5(16), try ceilPowerOfTwo(u5, 9)); + testing.expectEqual(@as(u32, 1), try ceilPowerOfTwo(u32, 1)); + testing.expectEqual(@as(u32, 2), try ceilPowerOfTwo(u32, 2)); + testing.expectEqual(@as(u32, 64), try ceilPowerOfTwo(u32, 63)); + testing.expectEqual(@as(u32, 64), try ceilPowerOfTwo(u32, 64)); + testing.expectEqual(@as(u32, 128), try ceilPowerOfTwo(u32, 65)); + testing.expectEqual(@as(u5, 8), try ceilPowerOfTwo(u5, 7)); + testing.expectEqual(@as(u5, 8), try ceilPowerOfTwo(u5, 8)); + testing.expectEqual(@as(u5, 16), try ceilPowerOfTwo(u5, 9)); testing.expectError(error.Overflow, ceilPowerOfTwo(u4, 9)); } @@ -858,7 +858,7 @@ pub fn log2_int(comptime T: type, x: T) Log2Int(T) { pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { assert(x != 0); const log2_val = log2_int(T, x); - if (T(1) << log2_val == x) + if (@as(T, 1) << log2_val == x) return log2_val; return log2_val + 1; } @@ -877,11 +877,11 @@ test "std.math.log2_int_ceil" { } pub fn lossyCast(comptime T: type, value: var) T { - switch (@typeInfo(@typeOf(value))) { + switch (@typeInfo(@TypeOf(value))) { builtin.TypeId.Int => return @intToFloat(T, value), builtin.TypeId.Float => return @floatCast(T, value), - builtin.TypeId.ComptimeInt => return T(value), - builtin.TypeId.ComptimeFloat => return T(value), + builtin.TypeId.ComptimeInt => return @as(T, value), + builtin.TypeId.ComptimeFloat => return @as(T, value), else => @compileError("bad type"), } } @@ -945,16 +945,13 @@ test "minInt and maxInt" { } test "max value type" { - // If the type of maxInt(i32) was i32 then this implicit cast to - // u32 would not work. But since the value is a number literal, - // it works fine. const x: u32 = maxInt(i32); testing.expect(x == 2147483647); } pub fn mulWide(comptime T: type, a: T, b: T) @IntType(T.is_signed, T.bit_count * 2) { const ResultInt = @IntType(T.is_signed, T.bit_count * 2); - return ResultInt(a) * ResultInt(b); + return @as(ResultInt, a) * @as(ResultInt, b); } test "math.mulWide" { @@ -962,3 +959,83 @@ test "math.mulWide" { testing.expect(mulWide(i8, 5, -5) == -25); testing.expect(mulWide(u8, 100, 100) == 10000); } + +/// See also `CompareOperator`. +pub const Order = enum { + /// Less than (`<`) + lt, + + /// Equal (`==`) + eq, + + /// Greater than (`>`) + gt, +}; + +/// Given two numbers, this function returns the order they are with respect to each other. +pub fn order(a: var, b: var) Order { + if (a == b) { + return .eq; + } else if (a < b) { + return .lt; + } else if (a > b) { + return .gt; + } else { + unreachable; + } +} + +/// See also `Order`. +pub const CompareOperator = enum { + /// Less than (`<`) + lt, + + /// Less than or equal (`<=`) + lte, + + /// Equal (`==`) + eq, + + /// Greater than or equal (`>=`) + gte, + + /// Greater than (`>`) + gt, + + /// Not equal (`!=`) + neq, +}; + +/// This function does the same thing as comparison operators, however the +/// operator is a runtime-known enum value. Works on any operands that +/// support comparison operators. +pub fn compare(a: var, op: CompareOperator, b: var) bool { + return switch (op) { + .lt => a < b, + .lte => a <= b, + .eq => a == b, + .neq => a != b, + .gt => a > b, + .gte => a >= b, + }; +} + +test "compare between signed and unsigned" { + testing.expect(compare(@as(i8, -1), .lt, @as(u8, 255))); + testing.expect(compare(@as(i8, 2), .gt, @as(u8, 1))); + testing.expect(!compare(@as(i8, -1), .gte, @as(u8, 255))); + testing.expect(compare(@as(u8, 255), .gt, @as(i8, -1))); + testing.expect(!compare(@as(u8, 255), .lte, @as(i8, -1))); + testing.expect(compare(@as(i8, -1), .lt, @as(u9, 255))); + testing.expect(!compare(@as(i8, -1), .gte, @as(u9, 255))); + testing.expect(compare(@as(u9, 255), .gt, @as(i8, -1))); + testing.expect(!compare(@as(u9, 255), .lte, @as(i8, -1))); + testing.expect(compare(@as(i9, -1), .lt, @as(u8, 255))); + testing.expect(!compare(@as(i9, -1), .gte, @as(u8, 255))); + testing.expect(compare(@as(u8, 255), .gt, @as(i9, -1))); + testing.expect(!compare(@as(u8, 255), .lte, @as(i9, -1))); + testing.expect(compare(@as(u8, 1), .lt, @as(u8, 2))); + testing.expect(@bitCast(u8, @as(i8, -1)) == @as(u8, 255)); + testing.expect(!compare(@as(u8, 255), .eq, @as(i8, -1))); + testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1))); +} diff --git a/lib/std/math/acos.zig b/lib/std/math/acos.zig index de07da8fe..aec0d4706 100644 --- a/lib/std/math/acos.zig +++ b/lib/std/math/acos.zig @@ -12,8 +12,8 @@ const expect = std.testing.expect; /// /// Special cases: /// - acos(x) = nan if x < -1 or x > 1 -pub fn acos(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn acos(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => acos32(x), f64 => acos64(x), @@ -149,8 +149,8 @@ fn acos64(x: f64) f64 { } test "math.acos" { - expect(acos(f32(0.0)) == acos32(0.0)); - expect(acos(f64(0.0)) == acos64(0.0)); + expect(acos(@as(f32, 0.0)) == acos32(0.0)); + expect(acos(@as(f64, 0.0)) == acos64(0.0)); } test "math.acos32" { diff --git a/lib/std/math/acosh.zig b/lib/std/math/acosh.zig index 503c0433f..0f9933505 100644 --- a/lib/std/math/acosh.zig +++ b/lib/std/math/acosh.zig @@ -14,8 +14,8 @@ const expect = std.testing.expect; /// Special cases: /// - acosh(x) = snan if x < 1 /// - acosh(nan) = nan -pub fn acosh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn acosh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => acosh32(x), f64 => acosh64(x), @@ -61,8 +61,8 @@ fn acosh64(x: f64) f64 { } test "math.acosh" { - expect(acosh(f32(1.5)) == acosh32(1.5)); - expect(acosh(f64(1.5)) == acosh64(1.5)); + expect(acosh(@as(f32, 1.5)) == acosh32(1.5)); + expect(acosh(@as(f64, 1.5)) == acosh64(1.5)); } test "math.acosh32" { diff --git a/lib/std/math/asin.zig b/lib/std/math/asin.zig index 2db9f86ff..db57e2088 100644 --- a/lib/std/math/asin.zig +++ b/lib/std/math/asin.zig @@ -13,8 +13,8 @@ const expect = std.testing.expect; /// Special Cases: /// - asin(+-0) = +-0 /// - asin(x) = nan if x < -1 or x > 1 -pub fn asin(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn asin(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => asin32(x), f64 => asin64(x), @@ -142,8 +142,8 @@ fn asin64(x: f64) f64 { } test "math.asin" { - expect(asin(f32(0.0)) == asin32(0.0)); - expect(asin(f64(0.0)) == asin64(0.0)); + expect(asin(@as(f32, 0.0)) == asin32(0.0)); + expect(asin(@as(f64, 0.0)) == asin64(0.0)); } test "math.asin32" { diff --git a/lib/std/math/asinh.zig b/lib/std/math/asinh.zig index 0fb51d1b4..ab1b65013 100644 --- a/lib/std/math/asinh.zig +++ b/lib/std/math/asinh.zig @@ -15,8 +15,8 @@ const maxInt = std.math.maxInt; /// - asinh(+-0) = +-0 /// - asinh(+-inf) = +-inf /// - asinh(nan) = nan -pub fn asinh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn asinh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => asinh32(x), f64 => asinh64(x), @@ -89,8 +89,8 @@ fn asinh64(x: f64) f64 { } test "math.asinh" { - expect(asinh(f32(0.0)) == asinh32(0.0)); - expect(asinh(f64(0.0)) == asinh64(0.0)); + expect(asinh(@as(f32, 0.0)) == asinh32(0.0)); + expect(asinh(@as(f64, 0.0)) == asinh64(0.0)); } test "math.asinh32" { diff --git a/lib/std/math/atan.zig b/lib/std/math/atan.zig index 5790eba8c..eb9154b5f 100644 --- a/lib/std/math/atan.zig +++ b/lib/std/math/atan.zig @@ -13,8 +13,8 @@ const expect = std.testing.expect; /// Special Cases: /// - atan(+-0) = +-0 /// - atan(+-inf) = +-pi/2 -pub fn atan(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn atan(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => atan32(x), f64 => atan64(x), @@ -212,8 +212,8 @@ fn atan64(x_: f64) f64 { } test "math.atan" { - expect(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2))); - expect(atan(f64(0.2)) == atan64(0.2)); + expect(@bitCast(u32, atan(@as(f32, 0.2))) == @bitCast(u32, atan32(0.2))); + expect(atan(@as(f64, 0.2)) == atan64(0.2)); } test "math.atan32" { diff --git a/lib/std/math/atanh.zig b/lib/std/math/atanh.zig index 8ba29be76..e58a10fff 100644 --- a/lib/std/math/atanh.zig +++ b/lib/std/math/atanh.zig @@ -15,8 +15,8 @@ const maxInt = std.math.maxInt; /// - atanh(+-1) = +-inf with signal /// - atanh(x) = nan if |x| > 1 with signal /// - atanh(nan) = nan -pub fn atanh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn atanh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => atanh_32(x), f64 => atanh_64(x), @@ -84,8 +84,8 @@ fn atanh_64(x: f64) f64 { } test "math.atanh" { - expect(atanh(f32(0.0)) == atanh_32(0.0)); - expect(atanh(f64(0.0)) == atanh_64(0.0)); + expect(atanh(@as(f32, 0.0)) == atanh_32(0.0)); + expect(atanh(@as(f64, 0.0)) == atanh_64(0.0)); } test "math.atanh_32" { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 8a6f6c1f7..7d8d3a195 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -180,9 +180,9 @@ pub const Int = struct { pub fn dump(self: Int) void { for (self.limbs) |limb| { - debug.warn("{x} ", limb); + debug.warn("{x} ", .{limb}); } - debug.warn("\n"); + debug.warn("\n", .{}); } /// Negate the sign of an Int. @@ -261,14 +261,14 @@ pub const Int = struct { /// the minus sign. This is used for determining the number of characters needed to print the /// value. It is inexact and may exceed the given value by ~1-2 bytes. pub fn sizeInBase(self: Int, base: usize) usize { - const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs(); + const bit_count = @as(usize, @boolToInt(!self.isPositive())) + self.bitCountAbs(); return (bit_count / math.log2(base)) + 1; } /// Sets an Int to value. Value must be an primitive integer type. pub fn set(self: *Int, value: var) Allocator.Error!void { self.assertWritable(); - const T = @typeOf(value); + const T = @TypeOf(value); switch (@typeInfo(T)) { TypeId.Int => |info| { @@ -281,7 +281,7 @@ pub const Int = struct { var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value); if (info.bits <= Limb.bit_count) { - self.limbs[0] = Limb(w_value); + self.limbs[0] = @as(Limb, w_value); self.metadata += 1; } else { var i: usize = 0; @@ -453,7 +453,7 @@ pub const Int = struct { for (self.limbs[0..self.len()]) |limb| { var shift: usize = 0; while (shift < Limb.bit_count) : (shift += base_shift) { - const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & Limb(base - 1)); + const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1)); const ch = try digitToChar(r, base); try digits.append(ch); } @@ -477,9 +477,12 @@ pub const Int = struct { } var q = try self.clone(); + defer q.deinit(); q.abs(); var r = try Int.init(allocator); + defer r.deinit(); var b = try Int.initSet(allocator, limb_base); + defer b.deinit(); while (q.len() >= 2) { try Int.divTrunc(&q, &r, q, b); @@ -522,7 +525,7 @@ pub const Int = struct { options: std.fmt.FormatOptions, context: var, comptime FmtError: type, - output: fn (@typeOf(context), []const u8) FmtError!void, + output: fn (@TypeOf(context), []const u8) FmtError!void, ) FmtError!void { self.assertWritable(); // TODO look at fmt and support other bases @@ -560,7 +563,7 @@ pub const Int = struct { /// Returns -1, 0, 1 if a < b, a == b or a > b respectively. pub fn cmp(a: Int, b: Int) i8 { if (a.isPositive() != b.isPositive()) { - return if (a.isPositive()) i8(1) else -1; + return if (a.isPositive()) @as(i8, 1) else -1; } else { const r = cmpAbs(a, b); return if (a.isPositive()) r else -r; @@ -766,13 +769,11 @@ pub const Int = struct { r.deinit(); }; - try r.ensureCapacity(a.len() + b.len()); + try r.ensureCapacity(a.len() + b.len() + 1); - if (a.len() >= b.len()) { - llmul(r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]); - } else { - llmul(r.limbs, b.limbs[0..b.len()], a.limbs[0..a.len()]); - } + mem.set(Limb, r.limbs[0 .. a.len() + b.len() + 1], 0); + + try llmulacc(rma.allocator.?, r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]); r.normalize(a.len() + b.len()); r.setSign(a.isPositive() == b.isPositive()); @@ -780,13 +781,14 @@ pub const Int = struct { // a + b * c + *carry, sets carry to the overflow bits pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { + @setRuntimeSafety(false); var r1: Limb = undefined; // r1 = a + *carry const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); // r2 = b * c - const bc = DoubleLimb(math.mulWide(Limb, b, c)); + const bc = @as(DoubleLimb, math.mulWide(Limb, b, c)); const r2 = @truncate(Limb, bc); const c2 = @truncate(Limb, bc >> Limb.bit_count); @@ -800,25 +802,178 @@ pub const Int = struct { return r1; } + fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void { + @setRuntimeSafety(false); + if (xi == 0) { + return; + } + + var carry: usize = 0; + var a_lo = acc[0..y.len]; + var a_hi = acc[y.len..]; + + var j: usize = 0; + while (j < a_lo.len) : (j += 1) { + a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry }); + } + + j = 0; + while ((carry != 0) and (j < a_hi.len)) : (j += 1) { + carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); + } + } + // Knuth 4.3.1, Algorithm M. // // r MUST NOT alias any of a or b. - fn llmul(r: []Limb, a: []const Limb, b: []const Limb) void { + fn llmulacc(allocator: *Allocator, r: []Limb, a: []const Limb, b: []const Limb) error{OutOfMemory}!void { @setRuntimeSafety(false); - debug.assert(a.len >= b.len); - debug.assert(r.len >= a.len + b.len); - mem.set(Limb, r[0 .. a.len + b.len], 0); + const a_norm = a[0..llnormalize(a)]; + const b_norm = b[0..llnormalize(b)]; + var x = a_norm; + var y = b_norm; + if (a_norm.len > b_norm.len) { + x = b_norm; + y = a_norm; + } + + debug.assert(r.len >= x.len + y.len + 1); + + // 48 is a pretty abitrary size chosen based on performance of a factorial program. + if (x.len <= 48) { + // Basecase multiplication + var i: usize = 0; + while (i < x.len) : (i += 1) { + llmulDigit(r[i..], y, x[i]); + } + } else { + // Karatsuba multiplication + const split = @divFloor(x.len, 2); + var x0 = x[0..split]; + var x1 = x[split..x.len]; + var y0 = y[0..split]; + var y1 = y[split..y.len]; + + var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1); + defer allocator.free(tmp); + mem.set(Limb, tmp, 0); + + try llmulacc(allocator, tmp, x1, y1); + + var length = llnormalize(tmp); + _ = llaccum(r[split..], tmp[0..length]); + _ = llaccum(r[split * 2 ..], tmp[0..length]); + + mem.set(Limb, tmp[0..length], 0); + + try llmulacc(allocator, tmp, x0, y0); + + length = llnormalize(tmp); + _ = llaccum(r[0..], tmp[0..length]); + _ = llaccum(r[split..], tmp[0..length]); + + const x_cmp = llcmp(x1, x0); + const y_cmp = llcmp(y1, y0); + if (x_cmp * y_cmp == 0) { + return; + } + const x0_len = llnormalize(x0); + const x1_len = llnormalize(x1); + var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len)); + defer allocator.free(j0); + if (x_cmp == 1) { + llsub(j0, x1[0..x1_len], x0[0..x0_len]); + } else { + llsub(j0, x0[0..x0_len], x1[0..x1_len]); + } + + const y0_len = llnormalize(y0); + const y1_len = llnormalize(y1); + var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len)); + defer allocator.free(j1); + if (y_cmp == 1) { + llsub(j1, y1[0..y1_len], y0[0..y0_len]); + } else { + llsub(j1, y0[0..y0_len], y1[0..y1_len]); + } + const j0_len = llnormalize(j0); + const j1_len = llnormalize(j1); + if (x_cmp == y_cmp) { + mem.set(Limb, tmp[0..length], 0); + try llmulacc(allocator, tmp, j0, j1); + + length = Int.llnormalize(tmp); + llsub(r[split..], r[split..], tmp[0..length]); + } else { + try llmulacc(allocator, r[split..], j0, j1); + } + } + } + + // r = r + a + fn llaccum(r: []Limb, a: []const Limb) Limb { + @setRuntimeSafety(false); + debug.assert(r.len != 0 and a.len != 0); + debug.assert(r.len >= a.len); var i: usize = 0; + var carry: Limb = 0; + while (i < a.len) : (i += 1) { - var carry: Limb = 0; - var j: usize = 0; - while (j < b.len) : (j += 1) { - r[i + j] = @inlineCall(addMulLimbWithCarry, r[i + j], a[i], b[j], &carry); - } - r[i + j] = carry; + var c: Limb = 0; + c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i])); + c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); + carry = c; } + + while ((carry != 0) and i < r.len) : (i += 1) { + carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); + } + + return carry; + } + + /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs. + pub fn llcmp(a: []const Limb, b: []const Limb) i8 { + @setRuntimeSafety(false); + const a_len = llnormalize(a); + const b_len = llnormalize(b); + if (a_len < b_len) { + return -1; + } + if (a_len > b_len) { + return 1; + } + + var i: usize = a_len - 1; + while (i != 0) : (i -= 1) { + if (a[i] != b[i]) { + break; + } + } + + if (a[i] < b[i]) { + return -1; + } else if (a[i] > b[i]) { + return 1; + } else { + return 0; + } + } + + // returns the min length the limb could be. + fn llnormalize(a: []const Limb) usize { + @setRuntimeSafety(false); + var j = a.len; + while (j > 0) : (j -= 1) { + if (a[j - 1] != 0) { + break; + } + } + + // Handle zero + return if (j != 0) j else 1; } /// q = a / b (rem r) @@ -932,7 +1087,7 @@ pub const Int = struct { rem.* = 0; for (a) |_, ri| { const i = a.len - ri - 1; - const pdiv = ((DoubleLimb(rem.*) << Limb.bit_count) | a[i]); + const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]); if (pdiv == 0) { quo[i] = 0; @@ -991,9 +1146,9 @@ pub const Int = struct { if (x.limbs[i] == y.limbs[t]) { q.limbs[i - t - 1] = maxInt(Limb); } else { - const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]); - const z = @intCast(Limb, num / DoubleLimb(y.limbs[t])); - q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else Limb(z); + const num = (@as(DoubleLimb, x.limbs[i]) << Limb.bit_count) | @as(DoubleLimb, x.limbs[i - 1]); + const z = @intCast(Limb, num / @as(DoubleLimb, y.limbs[t])); + q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else @as(Limb, z); } // 3.2 @@ -1062,7 +1217,11 @@ pub const Int = struct { const dst_i = src_i + limb_shift; const src_digit = a[src_i]; - r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); + r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{ + Limb, + src_digit, + Limb.bit_count - @intCast(Limb, interior_limb_shift), + }); carry = (src_digit << interior_limb_shift); } @@ -1102,7 +1261,11 @@ pub const Int = struct { const src_digit = a[src_i]; r[dst_i] = carry | (src_digit >> interior_limb_shift); - carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); + carry = @call(.{ .modifier = .always_inline }, math.shl, .{ + Limb, + src_digit, + Limb.bit_count - @intCast(Limb, interior_limb_shift), + }); } } @@ -1210,7 +1373,7 @@ test "big.int comptime_int set" { comptime var i: usize = 0; inline while (i < s_limb_count) : (i += 1) { - const result = Limb(s & maxInt(Limb)); + const result = @as(Limb, s & maxInt(Limb)); s >>= Limb.bit_count / 2; s >>= Limb.bit_count / 2; testing.expect(a.limbs[i] == result); @@ -1225,7 +1388,7 @@ test "big.int comptime_int set negative" { } test "big.int int set unaligned small" { - var a = try Int.initSet(al, u7(45)); + var a = try Int.initSet(al, @as(u7, 45)); testing.expect(a.limbs[0] == 45); testing.expect(a.isPositive() == true); diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig index 6a51931e3..27a3b7300 100644 --- a/lib/std/math/big/rational.zig +++ b/lib/std/math/big/rational.zig @@ -742,10 +742,6 @@ test "big.rational setFloatString" { } test "big.rational toFloat" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } var a = try Rational.init(al); // = 3.14159297943115234375 @@ -758,10 +754,6 @@ test "big.rational toFloat" { } test "big.rational set/to Float round-trip" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } var a = try Rational.init(al); var prng = std.rand.DefaultPrng.init(0x5EED); var i: usize = 0; diff --git a/lib/std/math/cbrt.zig b/lib/std/math/cbrt.zig index 5241e3132..2b219d536 100644 --- a/lib/std/math/cbrt.zig +++ b/lib/std/math/cbrt.zig @@ -14,8 +14,8 @@ const expect = std.testing.expect; /// - cbrt(+-0) = +-0 /// - cbrt(+-inf) = +-inf /// - cbrt(nan) = nan -pub fn cbrt(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn cbrt(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => cbrt32(x), f64 => cbrt64(x), @@ -54,11 +54,11 @@ fn cbrt32(x: f32) f32 { // first step newton to 16 bits var t: f64 = @bitCast(f32, u); var r: f64 = t * t * t; - t = t * (f64(x) + x + r) / (x + r + r); + t = t * (@as(f64, x) + x + r) / (x + r + r); // second step newton to 47 bits r = t * t * t; - t = t * (f64(x) + x + r) / (x + r + r); + t = t * (@as(f64, x) + x + r) / (x + r + r); return @floatCast(f32, t); } @@ -97,7 +97,7 @@ fn cbrt64(x: f64) f64 { } u &= 1 << 63; - u |= u64(hx) << 32; + u |= @as(u64, hx) << 32; var t = @bitCast(f64, u); // cbrt to 23 bits @@ -120,8 +120,8 @@ fn cbrt64(x: f64) f64 { } test "math.cbrt" { - expect(cbrt(f32(0.0)) == cbrt32(0.0)); - expect(cbrt(f64(0.0)) == cbrt64(0.0)); + expect(cbrt(@as(f32, 0.0)) == cbrt32(0.0)); + expect(cbrt(@as(f64, 0.0)) == cbrt64(0.0)); } test "math.cbrt32" { diff --git a/lib/std/math/ceil.zig b/lib/std/math/ceil.zig index 5f86093a6..b94e13a17 100644 --- a/lib/std/math/ceil.zig +++ b/lib/std/math/ceil.zig @@ -15,8 +15,8 @@ const expect = std.testing.expect; /// - ceil(+-0) = +-0 /// - ceil(+-inf) = +-inf /// - ceil(nan) = nan -pub fn ceil(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn ceil(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => ceil32(x), f64 => ceil64(x), @@ -37,7 +37,7 @@ fn ceil32(x: f32) f32 { if (e >= 23) { return x; } else if (e >= 0) { - m = u32(0x007FFFFF) >> @intCast(u5, e); + m = @as(u32, 0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } @@ -87,8 +87,8 @@ fn ceil64(x: f64) f64 { } test "math.ceil" { - expect(ceil(f32(0.0)) == ceil32(0.0)); - expect(ceil(f64(0.0)) == ceil64(0.0)); + expect(ceil(@as(f32, 0.0)) == ceil32(0.0)); + expect(ceil(@as(f64, 0.0)) == ceil64(0.0)); } test "math.ceil32" { diff --git a/lib/std/math/complex.zig b/lib/std/math/complex.zig index e5574f9ce..8bff2313f 100644 --- a/lib/std/math/complex.zig +++ b/lib/std/math/complex.zig @@ -133,8 +133,8 @@ test "complex.div" { const b = Complex(f32).new(2, 7); const c = a.div(b); - testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and - math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); + testing.expect(math.approxEq(f32, c.re, @as(f32, 31) / 53, epsilon) and + math.approxEq(f32, c.im, @as(f32, -29) / 53, epsilon)); } test "complex.conjugate" { @@ -148,8 +148,8 @@ test "complex.reciprocal" { const a = Complex(f32).new(5, 3); const c = a.reciprocal(); - testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and - math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); + testing.expect(math.approxEq(f32, c.re, @as(f32, 5) / 34, epsilon) and + math.approxEq(f32, c.im, @as(f32, -3) / 34, epsilon)); } test "complex.magnitude" { diff --git a/lib/std/math/complex/abs.zig b/lib/std/math/complex/abs.zig index 8105f5721..75b967f3d 100644 --- a/lib/std/math/complex/abs.zig +++ b/lib/std/math/complex/abs.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the absolute value (modulus) of z. -pub fn abs(z: var) @typeOf(z.re) { - const T = @typeOf(z.re); +pub fn abs(z: var) @TypeOf(z.re) { + const T = @TypeOf(z.re); return math.hypot(T, z.re, z.im); } diff --git a/lib/std/math/complex/acos.zig b/lib/std/math/complex/acos.zig index f3526cc9f..24a645375 100644 --- a/lib/std/math/complex/acos.zig +++ b/lib/std/math/complex/acos.zig @@ -5,10 +5,10 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the arc-cosine of z. -pub fn acos(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn acos(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const q = cmath.asin(z); - return Complex(T).new(T(math.pi) / 2 - q.re, -q.im); + return Complex(T).new(@as(T, math.pi) / 2 - q.re, -q.im); } const epsilon = 0.0001; diff --git a/lib/std/math/complex/acosh.zig b/lib/std/math/complex/acosh.zig index 6f0fd2e36..996334034 100644 --- a/lib/std/math/complex/acosh.zig +++ b/lib/std/math/complex/acosh.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the hyperbolic arc-cosine of z. -pub fn acosh(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn acosh(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const q = cmath.acos(z); return Complex(T).new(-q.im, q.re); } diff --git a/lib/std/math/complex/arg.zig b/lib/std/math/complex/arg.zig index d0c9588b8..f690e9214 100644 --- a/lib/std/math/complex/arg.zig +++ b/lib/std/math/complex/arg.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the angular component (in radians) of z. -pub fn arg(z: var) @typeOf(z.re) { - const T = @typeOf(z.re); +pub fn arg(z: var) @TypeOf(z.re) { + const T = @TypeOf(z.re); return math.atan2(T, z.im, z.re); } diff --git a/lib/std/math/complex/asin.zig b/lib/std/math/complex/asin.zig index 76f94a286..01fa33156 100644 --- a/lib/std/math/complex/asin.zig +++ b/lib/std/math/complex/asin.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; // Returns the arc-sine of z. -pub fn asin(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn asin(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const x = z.re; const y = z.im; diff --git a/lib/std/math/complex/asinh.zig b/lib/std/math/complex/asinh.zig index da065aad0..47d8244ad 100644 --- a/lib/std/math/complex/asinh.zig +++ b/lib/std/math/complex/asinh.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the hyperbolic arc-sine of z. -pub fn asinh(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn asinh(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const q = Complex(T).new(-z.im, z.re); const r = cmath.asin(q); return Complex(T).new(r.im, -r.re); diff --git a/lib/std/math/complex/atan.zig b/lib/std/math/complex/atan.zig index 3cd19961c..5ba6f7b0d 100644 --- a/lib/std/math/complex/atan.zig +++ b/lib/std/math/complex/atan.zig @@ -12,8 +12,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the arc-tangent of z. -pub fn atan(z: var) @typeOf(z) { - const T = @typeOf(z.re); +pub fn atan(z: var) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => atan32(z), f64 => atan64(z), @@ -130,10 +130,6 @@ test "complex.catan32" { } test "complex.catan64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const a = Complex(f64).new(5, 3); const c = atan(a); diff --git a/lib/std/math/complex/atanh.zig b/lib/std/math/complex/atanh.zig index 225e7c61d..8b7030622 100644 --- a/lib/std/math/complex/atanh.zig +++ b/lib/std/math/complex/atanh.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the hyperbolic arc-tangent of z. -pub fn atanh(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn atanh(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const q = Complex(T).new(-z.im, z.re); const r = cmath.atan(q); return Complex(T).new(r.im, -r.re); diff --git a/lib/std/math/complex/conj.zig b/lib/std/math/complex/conj.zig index bd71ca3c0..1065d4bb7 100644 --- a/lib/std/math/complex/conj.zig +++ b/lib/std/math/complex/conj.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the complex conjugate of z. -pub fn conj(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn conj(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); return Complex(T).new(z.re, -z.im); } diff --git a/lib/std/math/complex/cos.zig b/lib/std/math/complex/cos.zig index 332009ffe..1aefa73db 100644 --- a/lib/std/math/complex/cos.zig +++ b/lib/std/math/complex/cos.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the cosine of z. -pub fn cos(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn cos(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const p = Complex(T).new(-z.im, z.re); return cmath.cosh(p); } diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig index 89afcac42..a9ac89360 100644 --- a/lib/std/math/complex/cosh.zig +++ b/lib/std/math/complex/cosh.zig @@ -14,8 +14,8 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; /// Returns the hyperbolic arc-cosine of z. -pub fn cosh(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn cosh(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); return switch (T) { f32 => cosh32(z), f64 => cosh64(z), @@ -165,10 +165,6 @@ test "complex.ccosh32" { } test "complex.ccosh64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const a = Complex(f64).new(5, 3); const c = cosh(a); diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig index 5cd1cb4ed..9f9e3db80 100644 --- a/lib/std/math/complex/exp.zig +++ b/lib/std/math/complex/exp.zig @@ -14,8 +14,8 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; /// Returns e raised to the power of z (e^z). -pub fn exp(z: var) @typeOf(z) { - const T = @typeOf(z.re); +pub fn exp(z: var) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => exp32(z), @@ -131,10 +131,6 @@ test "complex.cexp32" { } test "complex.cexp64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const a = Complex(f64).new(5, 3); const c = exp(a); diff --git a/lib/std/math/complex/ldexp.zig b/lib/std/math/complex/ldexp.zig index d6f810793..9eccd4bb9 100644 --- a/lib/std/math/complex/ldexp.zig +++ b/lib/std/math/complex/ldexp.zig @@ -11,8 +11,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns exp(z) scaled to avoid overflow. -pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) { - const T = @typeOf(z.re); +pub fn ldexp_cexp(z: var, expt: i32) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => ldexp_cexp32(z, expt), @@ -59,13 +59,13 @@ fn frexp_exp64(x: f64, expt: *i32) f64 { expt.* = @intCast(i32, hx >> 20) - (0x3ff + 1023) + k; const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); - return @bitCast(f64, (u64(high_word) << 32) | lx); + return @bitCast(f64, (@as(u64, high_word) << 32) | lx); } fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { var ex_expt: i32 = undefined; const exp_x = frexp_exp64(z.re, &ex_expt); - const exptf = i64(expt + ex_expt); + const exptf = @as(i64, expt + ex_expt); const half_expt1 = @divTrunc(exptf, 2); const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20); diff --git a/lib/std/math/complex/log.zig b/lib/std/math/complex/log.zig index 762b4fde9..f1fad3175 100644 --- a/lib/std/math/complex/log.zig +++ b/lib/std/math/complex/log.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the natural logarithm of z. -pub fn log(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn log(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const r = cmath.abs(z); const phi = cmath.arg(z); diff --git a/lib/std/math/complex/proj.zig b/lib/std/math/complex/proj.zig index c8f2d9fc6..349f6b3ab 100644 --- a/lib/std/math/complex/proj.zig +++ b/lib/std/math/complex/proj.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the projection of z onto the riemann sphere. -pub fn proj(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn proj(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); if (math.isInf(z.re) or math.isInf(z.im)) { return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re)); diff --git a/lib/std/math/complex/sin.zig b/lib/std/math/complex/sin.zig index 9ddc3a7a8..87dc57911 100644 --- a/lib/std/math/complex/sin.zig +++ b/lib/std/math/complex/sin.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the sine of z. -pub fn sin(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn sin(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const p = Complex(T).new(-z.im, z.re); const q = cmath.sinh(p); return Complex(T).new(q.im, -q.re); diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index 0b1294bb6..7dd880c71 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -14,8 +14,8 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; /// Returns the hyperbolic sine of z. -pub fn sinh(z: var) @typeOf(z) { - const T = @typeOf(z.re); +pub fn sinh(z: var) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => sinh32(z), f64 => sinh64(z), @@ -164,10 +164,6 @@ test "complex.csinh32" { } test "complex.csinh64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const a = Complex(f64).new(5, 3); const c = sinh(a); diff --git a/lib/std/math/complex/sqrt.zig b/lib/std/math/complex/sqrt.zig index 36f4c28e2..57e73f6cd 100644 --- a/lib/std/math/complex/sqrt.zig +++ b/lib/std/math/complex/sqrt.zig @@ -12,8 +12,8 @@ const Complex = cmath.Complex; /// Returns the square root of z. The real and imaginary parts of the result have the same sign /// as the imaginary part of z. -pub fn sqrt(z: var) @typeOf(z) { - const T = @typeOf(z.re); +pub fn sqrt(z: var) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => sqrt32(z), @@ -52,8 +52,8 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { // y = nan special case is handled fine below // double-precision avoids overflow with correct rounding. - const dx = f64(x); - const dy = f64(y); + const dx = @as(f64, x); + const dy = @as(f64, y); if (dx >= 0) { const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); diff --git a/lib/std/math/complex/tan.zig b/lib/std/math/complex/tan.zig index 398b8295c..70304803d 100644 --- a/lib/std/math/complex/tan.zig +++ b/lib/std/math/complex/tan.zig @@ -5,8 +5,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the tanget of z. -pub fn tan(z: var) Complex(@typeOf(z.re)) { - const T = @typeOf(z.re); +pub fn tan(z: var) Complex(@TypeOf(z.re)) { + const T = @TypeOf(z.re); const q = Complex(T).new(-z.im, z.re); const r = cmath.tanh(q); return Complex(T).new(r.im, -r.re); diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index 6895e8a76..afd2e6aee 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -12,8 +12,8 @@ const cmath = math.complex; const Complex = cmath.Complex; /// Returns the hyperbolic tangent of z. -pub fn tanh(z: var) @typeOf(z) { - const T = @typeOf(z.re); +pub fn tanh(z: var) @TypeOf(z) { + const T = @TypeOf(z.re); return switch (T) { f32 => tanh32(z), f64 => tanh64(z), @@ -76,7 +76,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { return Complex(f64).new(x, r); } - const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx); + const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); return Complex(f64).new(xx, math.copysign(f64, 0, r)); } @@ -113,10 +113,6 @@ test "complex.ctanh32" { } test "complex.ctanh64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const a = Complex(f64).new(5, 3); const c = tanh(a); diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig index e4d90c395..e874da0bb 100644 --- a/lib/std/math/copysign.zig +++ b/lib/std/math/copysign.zig @@ -24,7 +24,7 @@ fn copysign16(x: f16, y: f16) f16 { const uy = @bitCast(u16, y); const h1 = ux & (maxInt(u16) / 2); - const h2 = uy & (u16(1) << 15); + const h2 = uy & (@as(u16, 1) << 15); return @bitCast(f16, h1 | h2); } @@ -33,7 +33,7 @@ fn copysign32(x: f32, y: f32) f32 { const uy = @bitCast(u32, y); const h1 = ux & (maxInt(u32) / 2); - const h2 = uy & (u32(1) << 31); + const h2 = uy & (@as(u32, 1) << 31); return @bitCast(f32, h1 | h2); } @@ -42,7 +42,7 @@ fn copysign64(x: f64, y: f64) f64 { const uy = @bitCast(u64, y); const h1 = ux & (maxInt(u64) / 2); - const h2 = uy & (u64(1) << 63); + const h2 = uy & (@as(u64, 1) << 63); return @bitCast(f64, h1 | h2); } diff --git a/lib/std/math/cos.zig b/lib/std/math/cos.zig index 5261a25f8..248243e28 100644 --- a/lib/std/math/cos.zig +++ b/lib/std/math/cos.zig @@ -13,8 +13,8 @@ const expect = std.testing.expect; /// Special Cases: /// - cos(+-inf) = nan /// - cos(nan) = nan -pub fn cos(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn cos(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => cos_(f32, x), f64 => cos_(f64, x), @@ -83,8 +83,8 @@ fn cos_(comptime T: type, x_: T) T { } test "math.cos" { - expect(cos(f32(0.0)) == cos_(f32, 0.0)); - expect(cos(f64(0.0)) == cos_(f64, 0.0)); + expect(cos(@as(f32, 0.0)) == cos_(f32, 0.0)); + expect(cos(@as(f64, 0.0)) == cos_(f64, 0.0)); } test "math.cos32" { @@ -100,10 +100,6 @@ test "math.cos32" { } test "math.cos64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon)); diff --git a/lib/std/math/cosh.zig b/lib/std/math/cosh.zig index 75c5c15ec..17c291d94 100644 --- a/lib/std/math/cosh.zig +++ b/lib/std/math/cosh.zig @@ -17,8 +17,8 @@ const maxInt = std.math.maxInt; /// - cosh(+-0) = 1 /// - cosh(+-inf) = +inf /// - cosh(nan) = nan -pub fn cosh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn cosh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => cosh32(x), f64 => cosh64(x), @@ -88,8 +88,8 @@ fn cosh64(x: f64) f64 { } test "math.cosh" { - expect(cosh(f32(1.5)) == cosh32(1.5)); - expect(cosh(f64(1.5)) == cosh64(1.5)); + expect(cosh(@as(f32, 1.5)) == cosh32(1.5)); + expect(cosh(@as(f64, 1.5)) == cosh64(1.5)); } test "math.cosh32" { diff --git a/lib/std/math/exp.zig b/lib/std/math/exp.zig index 718bbcd47..da80b201c 100644 --- a/lib/std/math/exp.zig +++ b/lib/std/math/exp.zig @@ -14,8 +14,8 @@ const builtin = @import("builtin"); /// Special Cases: /// - exp(+inf) = +inf /// - exp(nan) = nan -pub fn exp(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn exp(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => exp32(x), f64 => exp64(x), @@ -134,7 +134,7 @@ fn exp64(x_: f64) f64 { } if (x < -708.39641853226410622) { // underflow if x != -inf - // math.forceEval(f32(-0x1.0p-149 / x)); + // math.forceEval(@as(f32, -0x1.0p-149 / x)); if (x < -745.13321910194110842) { return 0; } @@ -183,8 +183,8 @@ fn exp64(x_: f64) f64 { } test "math.exp" { - assert(exp(f32(0.0)) == exp32(0.0)); - assert(exp(f64(0.0)) == exp64(0.0)); + assert(exp(@as(f32, 0.0)) == exp32(0.0)); + assert(exp(@as(f64, 0.0)) == exp64(0.0)); } test "math.exp32" { diff --git a/lib/std/math/exp2.zig b/lib/std/math/exp2.zig index 57f6620d7..411f78918 100644 --- a/lib/std/math/exp2.zig +++ b/lib/std/math/exp2.zig @@ -13,8 +13,8 @@ const expect = std.testing.expect; /// Special Cases: /// - exp2(+inf) = +inf /// - exp2(nan) = nan -pub fn exp2(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn exp2(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => exp2_32(x), f64 => exp2_64(x), @@ -85,7 +85,7 @@ fn exp2_32(x: f32) f32 { const k = i_0 / tblsiz; // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the // intended result but should confirm how GCC/Clang handle this to ensure. - const uk = @bitCast(f64, u64(0x3FF + k) << 52); + const uk = @bitCast(f64, @as(u64, 0x3FF + k) << 52); i_0 &= tblsiz - 1; uf -= redux; @@ -421,8 +421,8 @@ fn exp2_64(x: f64) f64 { } test "math.exp2" { - expect(exp2(f32(0.8923)) == exp2_32(0.8923)); - expect(exp2(f64(0.8923)) == exp2_64(0.8923)); + expect(exp2(@as(f32, 0.8923)) == exp2_32(0.8923)); + expect(exp2(@as(f64, 0.8923)) == exp2_64(0.8923)); } test "math.exp2_32" { diff --git a/lib/std/math/expm1.zig b/lib/std/math/expm1.zig index 5e347f86f..91752e9f8 100644 --- a/lib/std/math/expm1.zig +++ b/lib/std/math/expm1.zig @@ -18,8 +18,8 @@ const expect = std.testing.expect; /// - expm1(+inf) = +inf /// - expm1(-inf) = -1 /// - expm1(nan) = nan -pub fn expm1(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn expm1(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => expm1_32(x), f64 => expm1_64(x), @@ -287,8 +287,8 @@ fn expm1_64(x_: f64) f64 { } test "math.exp1m" { - expect(expm1(f32(0.0)) == expm1_32(0.0)); - expect(expm1(f64(0.0)) == expm1_64(0.0)); + expect(expm1(@as(f32, 0.0)) == expm1_32(0.0)); + expect(expm1(@as(f64, 0.0)) == expm1_64(0.0)); } test "math.expm1_32" { diff --git a/lib/std/math/expo2.zig b/lib/std/math/expo2.zig index c00098a5a..e70e365f2 100644 --- a/lib/std/math/expo2.zig +++ b/lib/std/math/expo2.zig @@ -7,8 +7,8 @@ const math = @import("../math.zig"); /// Returns exp(x) / 2 for x >= log(maxFloat(T)). -pub fn expo2(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn expo2(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => expo2f(x), f64 => expo2d(x), @@ -30,6 +30,6 @@ fn expo2d(x: f64) f64 { const kln2 = 0x1.62066151ADD8BP+10; const u = (0x3FF + k / 2) << 20; - const scale = @bitCast(f64, u64(u) << 32); + const scale = @bitCast(f64, @as(u64, u) << 32); return math.exp(x - kln2) * scale * scale; } diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index 6469f3883..a659e35ca 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -14,8 +14,8 @@ const maxInt = std.math.maxInt; /// Special Cases: /// - fabs(+-inf) = +inf /// - fabs(nan) = nan -pub fn fabs(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn fabs(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f16 => fabs16(x), f32 => fabs32(x), @@ -50,10 +50,10 @@ fn fabs128(x: f128) f128 { } test "math.fabs" { - expect(fabs(f16(1.0)) == fabs16(1.0)); - expect(fabs(f32(1.0)) == fabs32(1.0)); - expect(fabs(f64(1.0)) == fabs64(1.0)); - expect(fabs(f128(1.0)) == fabs128(1.0)); + expect(fabs(@as(f16, 1.0)) == fabs16(1.0)); + expect(fabs(@as(f32, 1.0)) == fabs32(1.0)); + expect(fabs(@as(f64, 1.0)) == fabs64(1.0)); + expect(fabs(@as(f128, 1.0)) == fabs128(1.0)); } test "math.fabs16" { diff --git a/lib/std/math/floor.zig b/lib/std/math/floor.zig index e5ff2b1fc..1eda362e6 100644 --- a/lib/std/math/floor.zig +++ b/lib/std/math/floor.zig @@ -15,8 +15,8 @@ const math = std.math; /// - floor(+-0) = +-0 /// - floor(+-inf) = +-inf /// - floor(nan) = nan -pub fn floor(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn floor(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f16 => floor16(x), f32 => floor32(x), @@ -40,7 +40,7 @@ fn floor16(x: f16) f16 { } if (e >= 0) { - m = u16(1023) >> @intCast(u4, e); + m = @as(u16, 1023) >> @intCast(u4, e); if (u & m == 0) { return x; } @@ -74,7 +74,7 @@ fn floor32(x: f32) f32 { } if (e >= 0) { - m = u32(0x007FFFFF) >> @intCast(u5, e); + m = @as(u32, 0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } @@ -123,9 +123,9 @@ fn floor64(x: f64) f64 { } test "math.floor" { - expect(floor(f16(1.3)) == floor16(1.3)); - expect(floor(f32(1.3)) == floor32(1.3)); - expect(floor(f64(1.3)) == floor64(1.3)); + expect(floor(@as(f16, 1.3)) == floor16(1.3)); + expect(floor(@as(f32, 1.3)) == floor32(1.3)); + expect(floor(@as(f64, 1.3)) == floor64(1.3)); } test "math.floor16" { diff --git a/lib/std/math/fma.zig b/lib/std/math/fma.zig index 19c306fa2..014593cda 100644 --- a/lib/std/math/fma.zig +++ b/lib/std/math/fma.zig @@ -18,7 +18,7 @@ pub fn fma(comptime T: type, x: T, y: T, z: T) T { } fn fma32(x: f32, y: f32, z: f32) f32 { - const xy = f64(x) * y; + const xy = @as(f64, x) * y; const xy_z = xy + z; const u = @bitCast(u64, xy_z); const e = (u >> 52) & 0x7FF; diff --git a/lib/std/math/frexp.zig b/lib/std/math/frexp.zig index 2759cd649..cfdf9f838 100644 --- a/lib/std/math/frexp.zig +++ b/lib/std/math/frexp.zig @@ -24,8 +24,8 @@ pub const frexp64_result = frexp_result(f64); /// - frexp(+-0) = +-0, 0 /// - frexp(+-inf) = +-inf, 0 /// - frexp(nan) = nan, undefined -pub fn frexp(x: var) frexp_result(@typeOf(x)) { - const T = @typeOf(x); +pub fn frexp(x: var) frexp_result(@TypeOf(x)) { + const T = @TypeOf(x); return switch (T) { f32 => frexp32(x), f64 => frexp64(x), @@ -108,11 +108,11 @@ fn frexp64(x: f64) frexp64_result { } test "math.frexp" { - const a = frexp(f32(1.3)); + const a = frexp(@as(f32, 1.3)); const b = frexp32(1.3); expect(a.significand == b.significand and a.exponent == b.exponent); - const c = frexp(f64(1.3)); + const c = frexp(@as(f64, 1.3)); const d = frexp64(1.3); expect(c.significand == d.significand and c.exponent == d.exponent); } diff --git a/lib/std/math/hypot.zig b/lib/std/math/hypot.zig index c15da1495..59116014b 100644 --- a/lib/std/math/hypot.zig +++ b/lib/std/math/hypot.zig @@ -56,7 +56,7 @@ fn hypot32(x: f32, y: f32) f32 { yy *= 0x1.0p-90; } - return z * math.sqrt(@floatCast(f32, f64(x) * x + f64(y) * y)); + return z * math.sqrt(@floatCast(f32, @as(f64, x) * x + @as(f64, y) * y)); } fn sq(hi: *f64, lo: *f64, x: f64) void { diff --git a/lib/std/math/ilogb.zig b/lib/std/math/ilogb.zig index fe4158a6d..22e3fbaa9 100644 --- a/lib/std/math/ilogb.zig +++ b/lib/std/math/ilogb.zig @@ -17,7 +17,7 @@ const minInt = std.math.minInt; /// - ilogb(0) = maxInt(i32) /// - ilogb(nan) = maxInt(i32) pub fn ilogb(x: var) i32 { - const T = @typeOf(x); + const T = @TypeOf(x); return switch (T) { f32 => ilogb32(x), f64 => ilogb64(x), @@ -26,7 +26,7 @@ pub fn ilogb(x: var) i32 { } // NOTE: Should these be exposed publicly? -const fp_ilogbnan = -1 - i32(maxInt(u32) >> 1); +const fp_ilogbnan = -1 - @as(i32, maxInt(u32) >> 1); const fp_ilogb0 = fp_ilogbnan; fn ilogb32(x: f32) i32 { @@ -101,8 +101,8 @@ fn ilogb64(x: f64) i32 { } test "math.ilogb" { - expect(ilogb(f32(0.2)) == ilogb32(0.2)); - expect(ilogb(f64(0.2)) == ilogb64(0.2)); + expect(ilogb(@as(f32, 0.2)) == ilogb32(0.2)); + expect(ilogb(@as(f64, 0.2)) == ilogb64(0.2)); } test "math.ilogb32" { diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index 99eba668f..26b3ce54a 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; /// Returns whether x is a finite value. pub fn isFinite(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); switch (T) { f16 => { const bits = @bitCast(u16, x); @@ -26,12 +26,12 @@ pub fn isFinite(x: var) bool { } test "math.isFinite" { - expect(isFinite(f16(0.0))); - expect(isFinite(f16(-0.0))); - expect(isFinite(f32(0.0))); - expect(isFinite(f32(-0.0))); - expect(isFinite(f64(0.0))); - expect(isFinite(f64(-0.0))); + expect(isFinite(@as(f16, 0.0))); + expect(isFinite(@as(f16, -0.0))); + expect(isFinite(@as(f32, 0.0))); + expect(isFinite(@as(f32, -0.0))); + expect(isFinite(@as(f64, 0.0))); + expect(isFinite(@as(f64, -0.0))); expect(!isFinite(math.inf(f16))); expect(!isFinite(-math.inf(f16))); expect(!isFinite(math.inf(f32))); diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index 37934f4cf..6eacab52a 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; /// Returns whether x is an infinity, ignoring sign. pub fn isInf(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); switch (T) { f16 => { const bits = @bitCast(u16, x); @@ -31,7 +31,7 @@ pub fn isInf(x: var) bool { /// Returns whether x is an infinity with a positive sign. pub fn isPositiveInf(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); switch (T) { f16 => { return @bitCast(u16, x) == 0x7C00; @@ -53,7 +53,7 @@ pub fn isPositiveInf(x: var) bool { /// Returns whether x is an infinity with a negative sign. pub fn isNegativeInf(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); switch (T) { f16 => { return @bitCast(u16, x) == 0xFC00; @@ -74,14 +74,14 @@ pub fn isNegativeInf(x: var) bool { } test "math.isInf" { - expect(!isInf(f16(0.0))); - expect(!isInf(f16(-0.0))); - expect(!isInf(f32(0.0))); - expect(!isInf(f32(-0.0))); - expect(!isInf(f64(0.0))); - expect(!isInf(f64(-0.0))); - expect(!isInf(f128(0.0))); - expect(!isInf(f128(-0.0))); + expect(!isInf(@as(f16, 0.0))); + expect(!isInf(@as(f16, -0.0))); + expect(!isInf(@as(f32, 0.0))); + expect(!isInf(@as(f32, -0.0))); + expect(!isInf(@as(f64, 0.0))); + expect(!isInf(@as(f64, -0.0))); + expect(!isInf(@as(f128, 0.0))); + expect(!isInf(@as(f128, -0.0))); expect(isInf(math.inf(f16))); expect(isInf(-math.inf(f16))); expect(isInf(math.inf(f32))); @@ -93,14 +93,14 @@ test "math.isInf" { } test "math.isPositiveInf" { - expect(!isPositiveInf(f16(0.0))); - expect(!isPositiveInf(f16(-0.0))); - expect(!isPositiveInf(f32(0.0))); - expect(!isPositiveInf(f32(-0.0))); - expect(!isPositiveInf(f64(0.0))); - expect(!isPositiveInf(f64(-0.0))); - expect(!isPositiveInf(f128(0.0))); - expect(!isPositiveInf(f128(-0.0))); + expect(!isPositiveInf(@as(f16, 0.0))); + expect(!isPositiveInf(@as(f16, -0.0))); + expect(!isPositiveInf(@as(f32, 0.0))); + expect(!isPositiveInf(@as(f32, -0.0))); + expect(!isPositiveInf(@as(f64, 0.0))); + expect(!isPositiveInf(@as(f64, -0.0))); + expect(!isPositiveInf(@as(f128, 0.0))); + expect(!isPositiveInf(@as(f128, -0.0))); expect(isPositiveInf(math.inf(f16))); expect(!isPositiveInf(-math.inf(f16))); expect(isPositiveInf(math.inf(f32))); @@ -112,14 +112,14 @@ test "math.isPositiveInf" { } test "math.isNegativeInf" { - expect(!isNegativeInf(f16(0.0))); - expect(!isNegativeInf(f16(-0.0))); - expect(!isNegativeInf(f32(0.0))); - expect(!isNegativeInf(f32(-0.0))); - expect(!isNegativeInf(f64(0.0))); - expect(!isNegativeInf(f64(-0.0))); - expect(!isNegativeInf(f128(0.0))); - expect(!isNegativeInf(f128(-0.0))); + expect(!isNegativeInf(@as(f16, 0.0))); + expect(!isNegativeInf(@as(f16, -0.0))); + expect(!isNegativeInf(@as(f32, 0.0))); + expect(!isNegativeInf(@as(f32, -0.0))); + expect(!isNegativeInf(@as(f64, 0.0))); + expect(!isNegativeInf(@as(f64, -0.0))); + expect(!isNegativeInf(@as(f128, 0.0))); + expect(!isNegativeInf(@as(f128, -0.0))); expect(!isNegativeInf(math.inf(f16))); expect(isNegativeInf(-math.inf(f16))); expect(!isNegativeInf(math.inf(f32))); diff --git a/lib/std/math/isnan.zig b/lib/std/math/isnan.zig index cf8cd2e1c..ac865f0d0 100644 --- a/lib/std/math/isnan.zig +++ b/lib/std/math/isnan.zig @@ -20,8 +20,8 @@ test "math.isNan" { expect(isNan(math.nan(f32))); expect(isNan(math.nan(f64))); expect(isNan(math.nan(f128))); - expect(!isNan(f16(1.0))); - expect(!isNan(f32(1.0))); - expect(!isNan(f64(1.0))); - expect(!isNan(f128(1.0))); + expect(!isNan(@as(f16, 1.0))); + expect(!isNan(@as(f32, 1.0))); + expect(!isNan(@as(f64, 1.0))); + expect(!isNan(@as(f128, 1.0))); } diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index f8611ef80..917b4ebfc 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; // Returns whether x has a normalized representation (i.e. integer part of mantissa is 1). pub fn isNormal(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); switch (T) { f16 => { const bits = @bitCast(u16, x); @@ -29,10 +29,10 @@ test "math.isNormal" { expect(!isNormal(math.nan(f16))); expect(!isNormal(math.nan(f32))); expect(!isNormal(math.nan(f64))); - expect(!isNormal(f16(0))); - expect(!isNormal(f32(0))); - expect(!isNormal(f64(0))); - expect(isNormal(f16(1.0))); - expect(isNormal(f32(1.0))); - expect(isNormal(f64(1.0))); + expect(!isNormal(@as(f16, 0))); + expect(!isNormal(@as(f32, 0))); + expect(!isNormal(@as(f64, 0))); + expect(isNormal(@as(f16, 1.0))); + expect(isNormal(@as(f32, 1.0))); + expect(isNormal(@as(f64, 1.0))); } diff --git a/lib/std/math/ln.zig b/lib/std/math/ln.zig index c5d4c9ff2..da3e60202 100644 --- a/lib/std/math/ln.zig +++ b/lib/std/math/ln.zig @@ -17,11 +17,11 @@ const TypeId = builtin.TypeId; /// - ln(0) = -inf /// - ln(x) = nan if x < 0 /// - ln(nan) = nan -pub fn ln(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn ln(x: var) @TypeOf(x) { + const T = @TypeOf(x); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(ln_64(x)); + return @as(comptime_float, ln_64(x)); }, TypeId.Float => { return switch (T) { @@ -31,10 +31,10 @@ pub fn ln(x: var) @typeOf(x) { }; }, TypeId.ComptimeInt => { - return @typeOf(1)(math.floor(ln_64(f64(x)))); + return @as(comptime_int, math.floor(ln_64(@as(f64, x)))); }, TypeId.Int => { - return T(math.floor(ln_64(f64(x)))); + return @as(T, math.floor(ln_64(@as(f64, x)))); }, else => @compileError("ln not implemented for " ++ @typeName(T)), } @@ -132,7 +132,7 @@ pub fn ln_64(x_: f64) f64 { hx += 0x3FF00000 - 0x3FE6A09E; k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); const f = x - 1.0; @@ -149,8 +149,8 @@ pub fn ln_64(x_: f64) f64 { } test "math.ln" { - expect(ln(f32(0.2)) == ln_32(0.2)); - expect(ln(f64(0.2)) == ln_64(0.2)); + expect(ln(@as(f32, 0.2)) == ln_32(0.2)); + expect(ln(@as(f64, 0.2)) == ln_64(0.2)); } test "math.ln32" { diff --git a/lib/std/math/log.zig b/lib/std/math/log.zig index 77f3639fd..9d77397e1 100644 --- a/lib/std/math/log.zig +++ b/lib/std/math/log.zig @@ -23,10 +23,10 @@ pub fn log(comptime T: type, base: T, x: T) T { const float_base = math.lossyCast(f64, base); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(math.ln(f64(x)) / math.ln(float_base)); + return @as(comptime_float, math.ln(@as(f64, x)) / math.ln(float_base)); }, TypeId.ComptimeInt => { - return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(float_base))); + return @as(comptime_int, math.floor(math.ln(@as(f64, x)) / math.ln(float_base))); }, builtin.TypeId.Int => { // TODO implement integer log without using float math @@ -35,7 +35,7 @@ pub fn log(comptime T: type, base: T, x: T) T { builtin.TypeId.Float => { switch (T) { - f32 => return @floatCast(f32, math.ln(f64(x)) / math.ln(float_base)), + f32 => return @floatCast(f32, math.ln(@as(f64, x)) / math.ln(float_base)), f64 => return math.ln(x) / math.ln(float_base), else => @compileError("log not implemented for " ++ @typeName(T)), } @@ -64,9 +64,9 @@ test "math.log float" { } test "math.log float_special" { - expect(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974))); - expect(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974))); + expect(log(f32, 2, 0.2301974) == math.log2(@as(f32, 0.2301974))); + expect(log(f32, 10, 0.2301974) == math.log10(@as(f32, 0.2301974))); - expect(log(f64, 2, 213.23019799993) == math.log2(f64(213.23019799993))); - expect(log(f64, 10, 213.23019799993) == math.log10(f64(213.23019799993))); + expect(log(f64, 2, 213.23019799993) == math.log2(@as(f64, 213.23019799993))); + expect(log(f64, 10, 213.23019799993) == math.log10(@as(f64, 213.23019799993))); } diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig index 9b0bc3ac5..a0deee69d 100644 --- a/lib/std/math/log10.zig +++ b/lib/std/math/log10.zig @@ -18,11 +18,11 @@ const maxInt = std.math.maxInt; /// - log10(0) = -inf /// - log10(x) = nan if x < 0 /// - log10(nan) = nan -pub fn log10(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn log10(x: var) @TypeOf(x) { + const T = @TypeOf(x); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(log10_64(x)); + return @as(comptime_float, log10_64(x)); }, TypeId.Float => { return switch (T) { @@ -32,7 +32,7 @@ pub fn log10(x: var) @typeOf(x) { }; }, TypeId.ComptimeInt => { - return @typeOf(1)(math.floor(log10_64(f64(x)))); + return @as(comptime_int, math.floor(log10_64(@as(f64, x)))); }, TypeId.Int => { return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))); @@ -143,7 +143,7 @@ pub fn log10_64(x_: f64) f64 { hx += 0x3FF00000 - 0x3FE6A09E; k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); const f = x - 1.0; @@ -158,7 +158,7 @@ pub fn log10_64(x_: f64) f64 { // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) var hi = f - hfsq; var hii = @bitCast(u64, hi); - hii &= u64(maxInt(u64)) << 32; + hii &= @as(u64, maxInt(u64)) << 32; hi = @bitCast(f64, hii); const lo = f - hi - hfsq + s * (hfsq + R); @@ -177,8 +177,8 @@ pub fn log10_64(x_: f64) f64 { } test "math.log10" { - testing.expect(log10(f32(0.2)) == log10_32(0.2)); - testing.expect(log10(f64(0.2)) == log10_64(0.2)); + testing.expect(log10(@as(f32, 0.2)) == log10_32(0.2)); + testing.expect(log10(@as(f64, 0.2)) == log10_64(0.2)); } test "math.log10_32" { diff --git a/lib/std/math/log1p.zig b/lib/std/math/log1p.zig index bae6deb53..5e92cfdea 100644 --- a/lib/std/math/log1p.zig +++ b/lib/std/math/log1p.zig @@ -17,8 +17,8 @@ const expect = std.testing.expect; /// - log1p(-1) = -inf /// - log1p(x) = nan if x < -1 /// - log1p(nan) = nan -pub fn log1p(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn log1p(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => log1p_32(x), f64 => log1p_64(x), @@ -166,7 +166,7 @@ fn log1p_64(x: f64) f64 { // u into [sqrt(2)/2, sqrt(2)] iu = (iu & 0x000FFFFF) + 0x3FE6A09E; - const iq = (u64(iu) << 32) | (hu & 0xFFFFFFFF); + const iq = (@as(u64, iu) << 32) | (hu & 0xFFFFFFFF); f = @bitCast(f64, iq) - 1; } @@ -183,8 +183,8 @@ fn log1p_64(x: f64) f64 { } test "math.log1p" { - expect(log1p(f32(0.0)) == log1p_32(0.0)); - expect(log1p(f64(0.0)) == log1p_64(0.0)); + expect(log1p(@as(f32, 0.0)) == log1p_32(0.0)); + expect(log1p(@as(f64, 0.0)) == log1p_64(0.0)); } test "math.log1p_32" { diff --git a/lib/std/math/log2.zig b/lib/std/math/log2.zig index 88450a7ff..d3b655a56 100644 --- a/lib/std/math/log2.zig +++ b/lib/std/math/log2.zig @@ -18,11 +18,11 @@ const maxInt = std.math.maxInt; /// - log2(0) = -inf /// - log2(x) = nan if x < 0 /// - log2(nan) = nan -pub fn log2(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn log2(x: var) @TypeOf(x) { + const T = @TypeOf(x); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(log2_64(x)); + return @as(comptime_float, log2_64(x)); }, TypeId.Float => { return switch (T) { @@ -143,7 +143,7 @@ pub fn log2_64(x_: f64) f64 { hx += 0x3FF00000 - 0x3FE6A09E; k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); const f = x - 1.0; @@ -158,7 +158,7 @@ pub fn log2_64(x_: f64) f64 { // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) var hi = f - hfsq; var hii = @bitCast(u64, hi); - hii &= u64(maxInt(u64)) << 32; + hii &= @as(u64, maxInt(u64)) << 32; hi = @bitCast(f64, hii); const lo = f - hi - hfsq + s * (hfsq + R); @@ -175,8 +175,8 @@ pub fn log2_64(x_: f64) f64 { } test "math.log2" { - expect(log2(f32(0.2)) == log2_32(0.2)); - expect(log2(f64(0.2)) == log2_64(0.2)); + expect(log2(@as(f32, 0.2)) == log2_32(0.2)); + expect(log2(@as(f64, 0.2)) == log2_64(0.2)); } test "math.log2_32" { diff --git a/lib/std/math/modf.zig b/lib/std/math/modf.zig index 92194d4c7..6fd89e3dd 100644 --- a/lib/std/math/modf.zig +++ b/lib/std/math/modf.zig @@ -24,8 +24,8 @@ pub const modf64_result = modf_result(f64); /// Special Cases: /// - modf(+-inf) = +-inf, nan /// - modf(nan) = nan, nan -pub fn modf(x: var) modf_result(@typeOf(x)) { - const T = @typeOf(x); +pub fn modf(x: var) modf_result(@TypeOf(x)) { + const T = @TypeOf(x); return switch (T) { f32 => modf32(x), f64 => modf64(x), @@ -65,7 +65,7 @@ fn modf32(x: f32) modf32_result { return result; } - const mask = u32(0x007FFFFF) >> @intCast(u5, e); + const mask = @as(u32, 0x007FFFFF) >> @intCast(u5, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f32, us); @@ -109,7 +109,7 @@ fn modf64(x: f64) modf64_result { return result; } - const mask = u64(maxInt(u64) >> 12) >> @intCast(u6, e); + const mask = @as(u64, maxInt(u64) >> 12) >> @intCast(u6, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f64, us); @@ -123,12 +123,12 @@ fn modf64(x: f64) modf64_result { } test "math.modf" { - const a = modf(f32(1.0)); + const a = modf(@as(f32, 1.0)); const b = modf32(1.0); // NOTE: No struct comparison on generic return type function? non-named, makes sense, but still. expect(a.ipart == b.ipart and a.fpart == b.fpart); - const c = modf(f64(1.0)); + const c = modf(@as(f64, 1.0)); const d = modf64(1.0); expect(a.ipart == b.ipart and a.fpart == b.fpart); } diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig index c3a779213..18c9f8063 100644 --- a/lib/std/math/pow.zig +++ b/lib/std/math/pow.zig @@ -184,10 +184,6 @@ fn isOddInteger(x: f64) bool { } test "math.pow" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); @@ -206,10 +202,6 @@ test "math.pow" { } test "math.pow.special" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(pow(f32, 4, 0.0) == 1.0); @@ -252,10 +244,6 @@ test "math.pow.special" { } test "math.pow.overflow" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } expect(math.isPositiveInf(pow(f64, 2, 1 << 32))); expect(pow(f64, 2, -(1 << 32)) == 0); expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1))); diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig index 0b80a46ce..dceb3ed77 100644 --- a/lib/std/math/round.zig +++ b/lib/std/math/round.zig @@ -15,8 +15,8 @@ const math = std.math; /// - round(+-0) = +-0 /// - round(+-inf) = +-inf /// - round(nan) = nan -pub fn round(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn round(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => round32(x), f64 => round64(x), @@ -91,8 +91,8 @@ fn round64(x_: f64) f64 { } test "math.round" { - expect(round(f32(1.3)) == round32(1.3)); - expect(round(f64(1.3)) == round64(1.3)); + expect(round(@as(f32, 1.3)) == round32(1.3)); + expect(round(@as(f64, 1.3)) == round64(1.3)); } test "math.round32" { diff --git a/lib/std/math/scalbn.zig b/lib/std/math/scalbn.zig index d5716d621..bab109f33 100644 --- a/lib/std/math/scalbn.zig +++ b/lib/std/math/scalbn.zig @@ -9,8 +9,8 @@ const math = std.math; const expect = std.testing.expect; /// Returns x * 2^n. -pub fn scalbn(x: var, n: i32) @typeOf(x) { - const T = @typeOf(x); +pub fn scalbn(x: var, n: i32) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => scalbn32(x, n), f64 => scalbn64(x, n), @@ -79,8 +79,8 @@ fn scalbn64(x: f64, n_: i32) f64 { } test "math.scalbn" { - expect(scalbn(f32(1.5), 4) == scalbn32(1.5, 4)); - expect(scalbn(f64(1.5), 4) == scalbn64(1.5, 4)); + expect(scalbn(@as(f32, 1.5), 4) == scalbn32(1.5, 4)); + expect(scalbn(@as(f64, 1.5), 4) == scalbn64(1.5, 4)); } test "math.scalbn32" { diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index e5c590929..9cb62b504 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -4,7 +4,7 @@ const expect = std.testing.expect; /// Returns whether x is negative or negative 0. pub fn signbit(x: var) bool { - const T = @typeOf(x); + const T = @TypeOf(x); return switch (T) { f16 => signbit16(x), f32 => signbit32(x), @@ -29,9 +29,9 @@ fn signbit64(x: f64) bool { } test "math.signbit" { - expect(signbit(f16(4.0)) == signbit16(4.0)); - expect(signbit(f32(4.0)) == signbit32(4.0)); - expect(signbit(f64(4.0)) == signbit64(4.0)); + expect(signbit(@as(f16, 4.0)) == signbit16(4.0)); + expect(signbit(@as(f32, 4.0)) == signbit32(4.0)); + expect(signbit(@as(f64, 4.0)) == signbit64(4.0)); } test "math.signbit16" { diff --git a/lib/std/math/sin.zig b/lib/std/math/sin.zig index ee07b4f85..00225449b 100644 --- a/lib/std/math/sin.zig +++ b/lib/std/math/sin.zig @@ -14,8 +14,8 @@ const expect = std.testing.expect; /// - sin(+-0) = +-0 /// - sin(+-inf) = nan /// - sin(nan) = nan -pub fn sin(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn sin(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => sin_(T, x), f64 => sin_(T, x), @@ -84,20 +84,12 @@ fn sin_(comptime T: type, x_: T) T { } test "math.sin" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } - expect(sin(f32(0.0)) == sin_(f32, 0.0)); - expect(sin(f64(0.0)) == sin_(f64, 0.0)); - expect(comptime (math.sin(f64(2))) == math.sin(f64(2))); + expect(sin(@as(f32, 0.0)) == sin_(f32, 0.0)); + expect(sin(@as(f64, 0.0)) == sin_(f64, 0.0)); + expect(comptime (math.sin(@as(f64, 2))) == math.sin(@as(f64, 2))); } test "math.sin32" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon)); @@ -110,10 +102,6 @@ test "math.sin32" { } test "math.sin64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon)); @@ -126,10 +114,6 @@ test "math.sin64" { } test "math.sin32.special" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } expect(sin_(f32, 0.0) == 0.0); expect(sin_(f32, -0.0) == -0.0); expect(math.isNan(sin_(f32, math.inf(f32)))); @@ -138,10 +122,6 @@ test "math.sin32.special" { } test "math.sin64.special" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } expect(sin_(f64, 0.0) == 0.0); expect(sin_(f64, -0.0) == -0.0); expect(math.isNan(sin_(f64, math.inf(f64)))); diff --git a/lib/std/math/sinh.zig b/lib/std/math/sinh.zig index 73ee65ea6..e9c76cd50 100644 --- a/lib/std/math/sinh.zig +++ b/lib/std/math/sinh.zig @@ -17,8 +17,8 @@ const maxInt = std.math.maxInt; /// - sinh(+-0) = +-0 /// - sinh(+-inf) = +-inf /// - sinh(nan) = nan -pub fn sinh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn sinh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => sinh32(x), f64 => sinh64(x), @@ -93,8 +93,8 @@ fn sinh64(x: f64) f64 { } test "math.sinh" { - expect(sinh(f32(1.5)) == sinh32(1.5)); - expect(sinh(f64(1.5)) == sinh64(1.5)); + expect(sinh(@as(f32, 1.5)) == sinh32(1.5)); + expect(sinh(@as(f64, 1.5)) == sinh64(1.5)); } test "math.sinh32" { diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig index 30af5915d..800a7574a 100644 --- a/lib/std/math/sqrt.zig +++ b/lib/std/math/sqrt.zig @@ -12,97 +12,25 @@ const maxInt = std.math.maxInt; /// - sqrt(+-0) = +-0 /// - sqrt(x) = nan if x < 0 /// - sqrt(nan) = nan -pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { - const T = @typeOf(x); - switch (@typeId(T)) { - TypeId.ComptimeFloat => return T(@sqrt(f64, x)), // TODO upgrade to f128 - TypeId.Float => return @sqrt(T, x), - TypeId.ComptimeInt => comptime { +/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function. +pub fn sqrt(x: var) Sqrt(@TypeOf(x)) { + const T = @TypeOf(x); + switch (@typeInfo(T)) { + .Float, .ComptimeFloat => return @sqrt(x), + .ComptimeInt => comptime { if (x > maxInt(u128)) { @compileError("sqrt not implemented for comptime_int greater than 128 bits"); } if (x < 0) { @compileError("sqrt on negative number"); } - return T(sqrt_int(u128, x)); + return @as(T, sqrt_int(u128, x)); }, - TypeId.Int => return sqrt_int(T, x), + .Int => return sqrt_int(T, x), else => @compileError("sqrt not implemented for " ++ @typeName(T)), } } -test "math.sqrt" { - expect(sqrt(f16(0.0)) == @sqrt(f16, 0.0)); - expect(sqrt(f32(0.0)) == @sqrt(f32, 0.0)); - expect(sqrt(f64(0.0)) == @sqrt(f64, 0.0)); -} - -test "math.sqrt16" { - const epsilon = 0.000001; - - expect(@sqrt(f16, 0.0) == 0.0); - expect(math.approxEq(f16, @sqrt(f16, 2.0), 1.414214, epsilon)); - expect(math.approxEq(f16, @sqrt(f16, 3.6), 1.897367, epsilon)); - expect(@sqrt(f16, 4.0) == 2.0); - expect(math.approxEq(f16, @sqrt(f16, 7.539840), 2.745877, epsilon)); - expect(math.approxEq(f16, @sqrt(f16, 19.230934), 4.385309, epsilon)); - expect(@sqrt(f16, 64.0) == 8.0); - expect(math.approxEq(f16, @sqrt(f16, 64.1), 8.006248, epsilon)); - expect(math.approxEq(f16, @sqrt(f16, 8942.230469), 94.563370, epsilon)); -} - -test "math.sqrt32" { - const epsilon = 0.000001; - - expect(@sqrt(f32, 0.0) == 0.0); - expect(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon)); - expect(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon)); - expect(@sqrt(f32, 4.0) == 2.0); - expect(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon)); - expect(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon)); - expect(@sqrt(f32, 64.0) == 8.0); - expect(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon)); - expect(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon)); -} - -test "math.sqrt64" { - const epsilon = 0.000001; - - expect(@sqrt(f64, 0.0) == 0.0); - expect(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon)); - expect(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon)); - expect(@sqrt(f64, 4.0) == 2.0); - expect(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon)); - expect(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon)); - expect(@sqrt(f64, 64.0) == 8.0); - expect(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon)); - expect(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon)); -} - -test "math.sqrt16.special" { - expect(math.isPositiveInf(@sqrt(f16, math.inf(f16)))); - expect(@sqrt(f16, 0.0) == 0.0); - expect(@sqrt(f16, -0.0) == -0.0); - expect(math.isNan(@sqrt(f16, -1.0))); - expect(math.isNan(@sqrt(f16, math.nan(f16)))); -} - -test "math.sqrt32.special" { - expect(math.isPositiveInf(@sqrt(f32, math.inf(f32)))); - expect(@sqrt(f32, 0.0) == 0.0); - expect(@sqrt(f32, -0.0) == -0.0); - expect(math.isNan(@sqrt(f32, -1.0))); - expect(math.isNan(@sqrt(f32, math.nan(f32)))); -} - -test "math.sqrt64.special" { - expect(math.isPositiveInf(@sqrt(f64, math.inf(f64)))); - expect(@sqrt(f64, 0.0) == 0.0); - expect(@sqrt(f64, -0.0) == -0.0); - expect(math.isNan(@sqrt(f64, -1.0))); - expect(math.isNan(@sqrt(f64, math.nan(f64)))); -} - fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { var op = value; var res: T = 0; @@ -134,3 +62,11 @@ test "math.sqrt_int" { expect(sqrt_int(u32, 9) == 3); expect(sqrt_int(u32, 10) == 3); } + +/// Returns the return type `sqrt` will return given an operand of type `T`. +pub fn Sqrt(comptime T: type) type { + return switch (@typeInfo(T)) { + .Int => |int| @IntType(false, int.bits / 2), + else => T, + }; +} diff --git a/lib/std/math/tan.zig b/lib/std/math/tan.zig index 049c85df1..01a0314d7 100644 --- a/lib/std/math/tan.zig +++ b/lib/std/math/tan.zig @@ -14,8 +14,8 @@ const expect = std.testing.expect; /// - tan(+-0) = +-0 /// - tan(+-inf) = nan /// - tan(nan) = nan -pub fn tan(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn tan(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => tan_(f32, x), f64 => tan_(f64, x), @@ -75,8 +75,8 @@ fn tan_(comptime T: type, x_: T) T { } test "math.tan" { - expect(tan(f32(0.0)) == tan_(f32, 0.0)); - expect(tan(f64(0.0)) == tan_(f64, 0.0)); + expect(tan(@as(f32, 0.0)) == tan_(f32, 0.0)); + expect(tan(@as(f64, 0.0)) == tan_(f64, 0.0)); } test "math.tan32" { @@ -91,10 +91,6 @@ test "math.tan32" { } test "math.tan64" { - if (builtin.os == .linux and builtin.arch == .arm and builtin.abi == .musleabihf) { - // TODO https://github.com/ziglang/zig/issues/3289 - return error.SkipZigTest; - } const epsilon = 0.000001; expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon)); diff --git a/lib/std/math/tanh.zig b/lib/std/math/tanh.zig index 48d26d091..1cad39972 100644 --- a/lib/std/math/tanh.zig +++ b/lib/std/math/tanh.zig @@ -17,8 +17,8 @@ const maxInt = std.math.maxInt; /// - sinh(+-0) = +-0 /// - sinh(+-inf) = +-1 /// - sinh(nan) = nan -pub fn tanh(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn tanh(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => tanh32(x), f64 => tanh64(x), @@ -119,8 +119,8 @@ fn tanh64(x: f64) f64 { } test "math.tanh" { - expect(tanh(f32(1.5)) == tanh32(1.5)); - expect(tanh(f64(1.5)) == tanh64(1.5)); + expect(tanh(@as(f32, 1.5)) == tanh32(1.5)); + expect(tanh(@as(f64, 1.5)) == tanh64(1.5)); } test "math.tanh32" { diff --git a/lib/std/math/trunc.zig b/lib/std/math/trunc.zig index 219bcd491..b70f0c6be 100644 --- a/lib/std/math/trunc.zig +++ b/lib/std/math/trunc.zig @@ -15,8 +15,8 @@ const maxInt = std.math.maxInt; /// - trunc(+-0) = +-0 /// - trunc(+-inf) = +-inf /// - trunc(nan) = nan -pub fn trunc(x: var) @typeOf(x) { - const T = @typeOf(x); +pub fn trunc(x: var) @TypeOf(x) { + const T = @TypeOf(x); return switch (T) { f32 => trunc32(x), f64 => trunc64(x), @@ -36,7 +36,7 @@ fn trunc32(x: f32) f32 { e = 1; } - m = u32(maxInt(u32)) >> @intCast(u5, e); + m = @as(u32, maxInt(u32)) >> @intCast(u5, e); if (u & m == 0) { return x; } else { @@ -57,7 +57,7 @@ fn trunc64(x: f64) f64 { e = 1; } - m = u64(maxInt(u64)) >> @intCast(u6, e); + m = @as(u64, maxInt(u64)) >> @intCast(u6, e); if (u & m == 0) { return x; } else { @@ -67,8 +67,8 @@ fn trunc64(x: f64) f64 { } test "math.trunc" { - expect(trunc(f32(1.3)) == trunc32(1.3)); - expect(trunc(f64(1.3)) == trunc64(1.3)); + expect(trunc(@as(f32, 1.3)) == trunc32(1.3)); + expect(trunc(@as(f64, 1.3)) == trunc64(1.3)); } test "math.trunc32" { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 2091eb480..f7b2bf261 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -86,13 +86,21 @@ pub const Allocator = struct { /// `ptr` should be the return value of `create`, or otherwise /// have the same address and alignment property. pub fn destroy(self: *Allocator, ptr: var) void { - const T = @typeOf(ptr).Child; + const T = @TypeOf(ptr).Child; if (@sizeOf(T) == 0) return; const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); const shrink_result = self.shrinkFn(self, non_const_ptr[0..@sizeOf(T)], @alignOf(T), 0, 1); assert(shrink_result.len == 0); } + /// Allocates an array of `n` items of type `T` and sets all the + /// items to `undefined`. Depending on the Allocator + /// implementation, it may be required to call `free` once the + /// memory is no longer needed, to avoid a resource leak. If the + /// `Allocator` implementation is unknown, then correct code will + /// call `free` when done. + /// + /// For allocating a single item, see `create`. pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T { return self.alignedAlloc(T, null, n); } @@ -110,11 +118,11 @@ pub const Allocator = struct { } else @alignOf(T); if (n == 0) { - return ([*]align(a) T)(undefined)[0..0]; + return @as([*]align(a) T, undefined)[0..0]; } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; - const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, a); + const byte_slice = try self.reallocFn(self, &[0]u8{}, undefined, byte_count, a); assert(byte_slice.len == byte_count); @memset(byte_slice.ptr, undefined, byte_slice.len); if (alignment == null) { @@ -139,10 +147,10 @@ pub const Allocator = struct { /// If you need guaranteed success, call `shrink`. /// If `new_n` is 0, this is the same as `free` and it always succeeds. pub fn realloc(self: *Allocator, old_mem: var, new_n: usize) t: { - const Slice = @typeInfo(@typeOf(old_mem)).Pointer; + const Slice = @typeInfo(@TypeOf(old_mem)).Pointer; break :t Error![]align(Slice.alignment) Slice.child; } { - const old_alignment = @typeInfo(@typeOf(old_mem)).Pointer.alignment; + const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment; return self.alignedRealloc(old_mem, old_alignment, new_n); } @@ -154,15 +162,15 @@ pub const Allocator = struct { old_mem: var, comptime new_alignment: u29, new_n: usize, - ) Error![]align(new_alignment) @typeInfo(@typeOf(old_mem)).Pointer.child { - const Slice = @typeInfo(@typeOf(old_mem)).Pointer; + ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child { + const Slice = @typeInfo(@TypeOf(old_mem)).Pointer; const T = Slice.child; if (old_mem.len == 0) { return self.alignedAlloc(T, new_alignment, new_n); } if (new_n == 0) { self.free(old_mem); - return ([*]align(new_alignment) T)(undefined)[0..0]; + return @as([*]align(new_alignment) T, undefined)[0..0]; } const old_byte_slice = @sliceToBytes(old_mem); @@ -181,10 +189,10 @@ pub const Allocator = struct { /// Returned slice has same alignment as old_mem. /// Shrinking to 0 is the same as calling `free`. pub fn shrink(self: *Allocator, old_mem: var, new_n: usize) t: { - const Slice = @typeInfo(@typeOf(old_mem)).Pointer; + const Slice = @typeInfo(@TypeOf(old_mem)).Pointer; break :t []align(Slice.alignment) Slice.child; } { - const old_alignment = @typeInfo(@typeOf(old_mem)).Pointer.alignment; + const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment; return self.alignedShrink(old_mem, old_alignment, new_n); } @@ -196,8 +204,8 @@ pub const Allocator = struct { old_mem: var, comptime new_alignment: u29, new_n: usize, - ) []align(new_alignment) @typeInfo(@typeOf(old_mem)).Pointer.child { - const Slice = @typeInfo(@typeOf(old_mem)).Pointer; + ) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child { + const Slice = @typeInfo(@TypeOf(old_mem)).Pointer; const T = Slice.child; if (new_n == 0) { @@ -218,22 +226,19 @@ pub const Allocator = struct { return @bytesToSlice(T, @alignCast(new_alignment, byte_slice)); } + /// Free an array allocated with `alloc`. To free a single item, + /// see `destroy`. pub fn free(self: *Allocator, memory: var) void { - const Slice = @typeInfo(@typeOf(memory)).Pointer; + const Slice = @typeInfo(@TypeOf(memory)).Pointer; const bytes = @sliceToBytes(memory); - if (bytes.len == 0) return; + const bytes_len = bytes.len + @boolToInt(Slice.sentinel != null); + if (bytes_len == 0) return; const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); - const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes.len], Slice.alignment, 0, 1); + const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1); assert(shrink_result.len == 0); } }; -pub const Compare = enum { - LessThan, - Equal, - GreaterThan, -}; - /// Copy all of source into dest at position 0. /// dest.len must be >= source.len. /// dest.ptr must be <= src.ptr. @@ -268,6 +273,33 @@ pub fn set(comptime T: type, dest: []T, value: T) void { d.* = value; } +/// Zero initializes the type. +/// This can be used to zero initialize a C-struct. +pub fn zeroes(comptime T: type) T { + if (@sizeOf(T) == 0) return T{}; + + if (comptime meta.containerLayout(T) != .Extern) { + @compileError("TODO: Currently this only works for extern types"); + } + + var item: T = undefined; + @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T)); + return item; +} + +test "mem.zeroes" { + const C_struct = extern struct { + x: u32, + y: u32, + }; + + var a = zeroes(C_struct); + a.y += 10; + + testing.expect(a.x == 0); + testing.expect(a.y == 10); +} + pub fn secureZero(comptime T: type, s: []T) void { // NOTE: We do not use a volatile slice cast here since LLVM cannot // see that it can be replaced by a memset. @@ -286,46 +318,30 @@ test "mem.secureZero" { testing.expectEqualSlices(u8, a[0..], b[0..]); } -pub fn compare(comptime T: type, lhs: []const T, rhs: []const T) Compare { +pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order { const n = math.min(lhs.len, rhs.len); var i: usize = 0; while (i < n) : (i += 1) { - if (lhs[i] == rhs[i]) { - continue; - } else if (lhs[i] < rhs[i]) { - return Compare.LessThan; - } else if (lhs[i] > rhs[i]) { - return Compare.GreaterThan; - } else { - unreachable; + switch (math.order(lhs[i], rhs[i])) { + .eq => continue, + .lt => return .lt, + .gt => return .gt, } } - - if (lhs.len == rhs.len) { - return Compare.Equal; - } else if (lhs.len < rhs.len) { - return Compare.LessThan; - } else if (lhs.len > rhs.len) { - return Compare.GreaterThan; - } - unreachable; + return math.order(lhs.len, rhs.len); } -test "mem.compare" { - testing.expect(compare(u8, "abcd", "bee") == Compare.LessThan); - testing.expect(compare(u8, "abc", "abc") == Compare.Equal); - testing.expect(compare(u8, "abc", "abc0") == Compare.LessThan); - testing.expect(compare(u8, "", "") == Compare.Equal); - testing.expect(compare(u8, "", "a") == Compare.LessThan); +test "order" { + testing.expect(order(u8, "abcd", "bee") == .lt); + testing.expect(order(u8, "abc", "abc") == .eq); + testing.expect(order(u8, "abc", "abc0") == .lt); + testing.expect(order(u8, "", "") == .eq); + testing.expect(order(u8, "", "a") == .lt); } /// Returns true if lhs < rhs, false otherwise pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool { - var result = compare(T, lhs, rhs); - if (result == Compare.LessThan) { - return true; - } else - return false; + return order(T, lhs, rhs) == .lt; } test "mem.lessThan" { @@ -346,18 +362,18 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } -pub fn len(comptime T: type, ptr: [*]const T) usize { +pub fn len(comptime T: type, ptr: [*:0]const T) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } -pub fn toSliceConst(comptime T: type, ptr: [*]const T) []const T { - return ptr[0..len(T, ptr)]; +pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { + return ptr[0..len(T, ptr) :0]; } -pub fn toSlice(comptime T: type, ptr: [*]T) []T { - return ptr[0..len(T, ptr)]; +pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { + return ptr[0..len(T, ptr) :0]; } /// Returns true if all elements in a slice are equal to the scalar value provided @@ -513,7 +529,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin. builtin.Endian.Little => { const ShiftType = math.Log2Int(ReturnType); for (bytes) |b, index| { - result = result | (ReturnType(b) << @intCast(ShiftType, index * 8)); + result = result | (@as(ReturnType, b) << @intCast(ShiftType, index * 8)); } }, } @@ -614,23 +630,23 @@ test "comptime read/write int" { } test "readIntBig and readIntLittle" { - testing.expect(readIntSliceBig(u0, [_]u8{}) == 0x0); - testing.expect(readIntSliceLittle(u0, [_]u8{}) == 0x0); + testing.expect(readIntSliceBig(u0, &[_]u8{}) == 0x0); + testing.expect(readIntSliceLittle(u0, &[_]u8{}) == 0x0); - testing.expect(readIntSliceBig(u8, [_]u8{0x32}) == 0x32); - testing.expect(readIntSliceLittle(u8, [_]u8{0x12}) == 0x12); + testing.expect(readIntSliceBig(u8, &[_]u8{0x32}) == 0x32); + testing.expect(readIntSliceLittle(u8, &[_]u8{0x12}) == 0x12); - testing.expect(readIntSliceBig(u16, [_]u8{ 0x12, 0x34 }) == 0x1234); - testing.expect(readIntSliceLittle(u16, [_]u8{ 0x12, 0x34 }) == 0x3412); + testing.expect(readIntSliceBig(u16, &[_]u8{ 0x12, 0x34 }) == 0x1234); + testing.expect(readIntSliceLittle(u16, &[_]u8{ 0x12, 0x34 }) == 0x3412); - testing.expect(readIntSliceBig(u72, [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }) == 0x123456789abcdef024); - testing.expect(readIntSliceLittle(u72, [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }) == 0xfedcba9876543210ec); + testing.expect(readIntSliceBig(u72, &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }) == 0x123456789abcdef024); + testing.expect(readIntSliceLittle(u72, &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }) == 0xfedcba9876543210ec); - testing.expect(readIntSliceBig(i8, [_]u8{0xff}) == -1); - testing.expect(readIntSliceLittle(i8, [_]u8{0xfe}) == -2); + testing.expect(readIntSliceBig(i8, &[_]u8{0xff}) == -1); + testing.expect(readIntSliceLittle(i8, &[_]u8{0xfe}) == -2); - testing.expect(readIntSliceBig(i16, [_]u8{ 0xff, 0xfd }) == -3); - testing.expect(readIntSliceLittle(i16, [_]u8{ 0xfc, 0xff }) == -4); + testing.expect(readIntSliceBig(i16, &[_]u8{ 0xff, 0xfd }) == -3); + testing.expect(readIntSliceLittle(i16, &[_]u8{ 0xfc, 0xff }) == -4); } /// Writes an integer to memory, storing it in twos-complement. @@ -739,34 +755,34 @@ test "writeIntBig and writeIntLittle" { var buf9: [9]u8 = undefined; writeIntBig(u0, &buf0, 0x0); - testing.expect(eql(u8, buf0[0..], [_]u8{})); + testing.expect(eql(u8, buf0[0..], &[_]u8{})); writeIntLittle(u0, &buf0, 0x0); - testing.expect(eql(u8, buf0[0..], [_]u8{})); + testing.expect(eql(u8, buf0[0..], &[_]u8{})); writeIntBig(u8, &buf1, 0x12); - testing.expect(eql(u8, buf1[0..], [_]u8{0x12})); + testing.expect(eql(u8, buf1[0..], &[_]u8{0x12})); writeIntLittle(u8, &buf1, 0x34); - testing.expect(eql(u8, buf1[0..], [_]u8{0x34})); + testing.expect(eql(u8, buf1[0..], &[_]u8{0x34})); writeIntBig(u16, &buf2, 0x1234); - testing.expect(eql(u8, buf2[0..], [_]u8{ 0x12, 0x34 })); + testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x12, 0x34 })); writeIntLittle(u16, &buf2, 0x5678); - testing.expect(eql(u8, buf2[0..], [_]u8{ 0x78, 0x56 })); + testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x78, 0x56 })); writeIntBig(u72, &buf9, 0x123456789abcdef024); - testing.expect(eql(u8, buf9[0..], [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); + testing.expect(eql(u8, buf9[0..], &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); writeIntLittle(u72, &buf9, 0xfedcba9876543210ec); - testing.expect(eql(u8, buf9[0..], [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); + testing.expect(eql(u8, buf9[0..], &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); writeIntBig(i8, &buf1, -1); - testing.expect(eql(u8, buf1[0..], [_]u8{0xff})); + testing.expect(eql(u8, buf1[0..], &[_]u8{0xff})); writeIntLittle(i8, &buf1, -2); - testing.expect(eql(u8, buf1[0..], [_]u8{0xfe})); + testing.expect(eql(u8, buf1[0..], &[_]u8{0xfe})); writeIntBig(i16, &buf2, -3); - testing.expect(eql(u8, buf2[0..], [_]u8{ 0xff, 0xfd })); + testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xff, 0xfd })); writeIntLittle(i16, &buf2, -4); - testing.expect(eql(u8, buf2[0..], [_]u8{ 0xfc, 0xff })); + testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xfc, 0xff })); } /// Returns an iterator that iterates over the slices of `buffer` that are not @@ -966,7 +982,7 @@ pub const SplitIterator = struct { /// Naively combines a series of slices with a separator. /// Allocates memory for the result, which must be freed by the caller. pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { - if (slices.len == 0) return (([*]u8)(undefined))[0..0]; + if (slices.len == 0) return &[0]u8{}; const total_len = blk: { var sum: usize = separator.len * (slices.len - 1); @@ -994,14 +1010,14 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons test "mem.join" { var buf: [1024]u8 = undefined; const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; - testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "b", "c" }), "a,b,c")); - testing.expect(eql(u8, try join(a, ",", [_][]const u8{"a"}), "a")); - testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c")); + testing.expect(eql(u8, try join(a, ",", &[_][]const u8{ "a", "b", "c" }), "a,b,c")); + testing.expect(eql(u8, try join(a, ",", &[_][]const u8{"a"}), "a")); + testing.expect(eql(u8, try join(a, ",", &[_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c")); } /// Copies each T from slices into a new slice that exactly holds all the elements. pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T { - if (slices.len == 0) return (([*]T)(undefined))[0..0]; + if (slices.len == 0) return &[0]T{}; const total_len = blk: { var sum: usize = 0; @@ -1027,13 +1043,13 @@ pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T test "concat" { var buf: [1024]u8 = undefined; const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; - testing.expect(eql(u8, try concat(a, u8, [_][]const u8{ "abc", "def", "ghi" }), "abcdefghi")); - testing.expect(eql(u32, try concat(a, u32, [_][]const u32{ - [_]u32{ 0, 1 }, - [_]u32{ 2, 3, 4 }, - [_]u32{}, - [_]u32{5}, - }), [_]u32{ 0, 1, 2, 3, 4, 5 })); + testing.expect(eql(u8, try concat(a, u8, &[_][]const u8{ "abc", "def", "ghi" }), "abcdefghi")); + testing.expect(eql(u32, try concat(a, u32, &[_][]const u32{ + &[_]u32{ 0, 1 }, + &[_]u32{ 2, 3, 4 }, + &[_]u32{}, + &[_]u32{5}, + }), &[_]u32{ 0, 1, 2, 3, 4, 5 })); } test "testStringEquality" { @@ -1101,19 +1117,19 @@ fn testWriteIntImpl() void { var bytes: [8]u8 = undefined; writeIntSlice(u0, bytes[0..], 0, builtin.Endian.Big); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, })); writeIntSlice(u0, bytes[0..], 0, builtin.Endian.Little); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, })); writeIntSlice(u64, bytes[0..], 0x12345678CAFEBABE, builtin.Endian.Big); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x12, 0x34, 0x56, @@ -1125,7 +1141,7 @@ fn testWriteIntImpl() void { })); writeIntSlice(u64, bytes[0..], 0xBEBAFECA78563412, builtin.Endian.Little); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x12, 0x34, 0x56, @@ -1137,7 +1153,7 @@ fn testWriteIntImpl() void { })); writeIntSlice(u32, bytes[0..], 0x12345678, builtin.Endian.Big); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x00, 0x00, 0x00, @@ -1149,7 +1165,7 @@ fn testWriteIntImpl() void { })); writeIntSlice(u32, bytes[0..], 0x78563412, builtin.Endian.Little); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x12, 0x34, 0x56, @@ -1161,7 +1177,7 @@ fn testWriteIntImpl() void { })); writeIntSlice(u16, bytes[0..], 0x1234, builtin.Endian.Big); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x00, 0x00, 0x00, @@ -1173,7 +1189,7 @@ fn testWriteIntImpl() void { })); writeIntSlice(u16, bytes[0..], 0x1234, builtin.Endian.Little); - testing.expect(eql(u8, bytes, [_]u8{ + testing.expect(eql(u8, &bytes, &[_]u8{ 0x34, 0x12, 0x00, @@ -1225,22 +1241,10 @@ pub fn reverse(comptime T: type, items: []T) void { } test "reverse" { - var arr = [_]i32{ - 5, - 3, - 1, - 2, - 4, - }; + var arr = [_]i32{ 5, 3, 1, 2, 4 }; reverse(i32, arr[0..]); - testing.expect(eql(i32, arr, [_]i32{ - 4, - 2, - 1, - 3, - 5, - })); + testing.expect(eql(i32, &arr, &[_]i32{ 4, 2, 1, 3, 5 })); } /// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1) @@ -1252,22 +1256,10 @@ pub fn rotate(comptime T: type, items: []T, amount: usize) void { } test "rotate" { - var arr = [_]i32{ - 5, - 3, - 1, - 2, - 4, - }; + var arr = [_]i32{ 5, 3, 1, 2, 4 }; rotate(i32, arr[0..], 2); - testing.expect(eql(i32, arr, [_]i32{ - 1, - 2, - 4, - 5, - 3, - })); + testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 })); } /// Converts a little-endian integer to host endianness. @@ -1322,22 +1314,28 @@ fn AsBytesReturnType(comptime P: type) type { if (comptime !trait.isSingleItemPtr(P)) @compileError("expected single item " ++ "pointer, passed " ++ @typeName(P)); - const size = usize(@sizeOf(meta.Child(P))); + const size = @as(usize, @sizeOf(meta.Child(P))); const alignment = comptime meta.alignment(P); + if (alignment == 0) { + if (comptime trait.isConstPtr(P)) + return *const [size]u8; + return *[size]u8; + } + if (comptime trait.isConstPtr(P)) return *align(alignment) const [size]u8; return *align(alignment) [size]u8; } ///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. -pub fn asBytes(ptr: var) AsBytesReturnType(@typeOf(ptr)) { - const P = @typeOf(ptr); +pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) { + const P = @TypeOf(ptr); return @ptrCast(AsBytesReturnType(P), ptr); } test "asBytes" { - const deadbeef = u32(0xDEADBEEF); + const deadbeef = @as(u32, 0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xDE\xAD\xBE\xEF", builtin.Endian.Little => "\xEF\xBE\xAD\xDE", @@ -1345,7 +1343,7 @@ test "asBytes" { testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes)); - var codeface = u32(0xC0DEFACE); + var codeface = @as(u32, 0xC0DEFACE); for (asBytes(&codeface).*) |*b| b.* = 0; testing.expect(codeface == 0); @@ -1364,31 +1362,37 @@ test "asBytes" { .d = 0xA1, }; testing.expect(eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); + + const ZST = struct {}; + const zero = ZST{}; + testing.expect(eql(u8, asBytes(&zero), "")); } ///Given any value, returns a copy of its bytes in an array. -pub fn toBytes(value: var) [@sizeOf(@typeOf(value))]u8 { +pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 { return asBytes(&value).*; } test "toBytes" { - var my_bytes = toBytes(u32(0x12345678)); + var my_bytes = toBytes(@as(u32, 0x12345678)); switch (builtin.endian) { - builtin.Endian.Big => testing.expect(eql(u8, my_bytes, "\x12\x34\x56\x78")), - builtin.Endian.Little => testing.expect(eql(u8, my_bytes, "\x78\x56\x34\x12")), + builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")), + builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")), } my_bytes[0] = '\x99'; switch (builtin.endian) { - builtin.Endian.Big => testing.expect(eql(u8, my_bytes, "\x99\x34\x56\x78")), - builtin.Endian.Little => testing.expect(eql(u8, my_bytes, "\x99\x56\x34\x12")), + builtin.Endian.Big => testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")), + builtin.Endian.Little => testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")), } } fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { - const size = usize(@sizeOf(T)); + const size = @as(usize, @sizeOf(T)); - if (comptime !trait.is(builtin.TypeId.Pointer)(B) or meta.Child(B) != [size]u8) { + if (comptime !trait.is(builtin.TypeId.Pointer)(B) or + (meta.Child(B) != [size]u8 and meta.Child(B) != [size:0]u8)) + { @compileError("expected *[N]u8 " ++ ", passed " ++ @typeName(B)); } @@ -1399,23 +1403,23 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { ///Given a pointer to an array of bytes, returns a pointer to a value of the specified type /// backed by those bytes, preserving constness. -pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @typeOf(bytes)) { - return @ptrCast(BytesAsValueReturnType(T, @typeOf(bytes)), bytes); +pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @TypeOf(bytes)) { + return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes); } test "bytesAsValue" { - const deadbeef = u32(0xDEADBEEF); + const deadbeef = @as(u32, 0xDEADBEEF); const deadbeef_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xDE\xAD\xBE\xEF", builtin.Endian.Little => "\xEF\xBE\xAD\xDE", }; - testing.expect(deadbeef == bytesAsValue(u32, &deadbeef_bytes).*); + testing.expect(deadbeef == bytesAsValue(u32, deadbeef_bytes).*); - var codeface_bytes = switch (builtin.endian) { + var codeface_bytes: [4]u8 = switch (builtin.endian) { builtin.Endian.Big => "\xC0\xDE\xFA\xCE", builtin.Endian.Little => "\xCE\xFA\xDE\xC0", - }; + }.*; var codeface = bytesAsValue(u32, &codeface_bytes); testing.expect(codeface.* == 0xC0DEFACE); codeface.* = 0; @@ -1436,14 +1440,14 @@ test "bytesAsValue" { .d = 0xA1, }; const inst_bytes = "\xBE\xEF\xDE\xA1"; - const inst2 = bytesAsValue(S, &inst_bytes); + const inst2 = bytesAsValue(S, inst_bytes); testing.expect(meta.eql(inst, inst2.*)); } ///Given a pointer to an array of bytes, returns a value of the specified type backed by a /// copy of those bytes. pub fn bytesToValue(comptime T: type, bytes: var) T { - return bytesAsValue(T, &bytes).*; + return bytesAsValue(T, bytes).*; } test "bytesToValue" { const deadbeef_bytes = switch (builtin.endian) { @@ -1452,7 +1456,7 @@ test "bytesToValue" { }; const deadbeef = bytesToValue(u32, deadbeef_bytes); - testing.expect(deadbeef == u32(0xDEADBEEF)); + testing.expect(deadbeef == @as(u32, 0xDEADBEEF)); } fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { @@ -1462,25 +1466,25 @@ fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { } ///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. -pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubArrayPtrReturnType(@typeOf(ptr), length) { +pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubArrayPtrReturnType(@TypeOf(ptr), length) { assert(start + length <= ptr.*.len); - const ReturnType = SubArrayPtrReturnType(@typeOf(ptr), length); - const T = meta.Child(meta.Child(@typeOf(ptr))); + const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length); + const T = meta.Child(meta.Child(@TypeOf(ptr))); return @ptrCast(ReturnType, &ptr[start]); } test "subArrayPtr" { - const a1 = "abcdef"; + const a1: [6]u8 = "abcdef".*; const sub1 = subArrayPtr(&a1, 2, 3); - testing.expect(eql(u8, sub1.*, "cde")); + testing.expect(eql(u8, sub1, "cde")); - var a2 = "abcdef"; + var a2: [6]u8 = "abcdef".*; var sub2 = subArrayPtr(&a2, 2, 3); testing.expect(eql(u8, sub2, "cde")); sub2[1] = 'X'; - testing.expect(eql(u8, a2, "abcXef")); + testing.expect(eql(u8, &a2, "abcXef")); } /// Round an address up to the nearest aligned address diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 52d8b54ec..5e5850e39 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -11,7 +11,7 @@ const TypeId = builtin.TypeId; const TypeInfo = builtin.TypeInfo; pub fn tagName(v: var) []const u8 { - const T = @typeOf(v); + const T = @TypeOf(v); switch (@typeInfo(T)) { TypeId.ErrorSet => return @errorName(v), else => return @tagName(v), @@ -339,9 +339,9 @@ test "std.meta.TagType" { } ///Returns the active tag of a tagged union -pub fn activeTag(u: var) @TagType(@typeOf(u)) { - const T = @typeOf(u); - return @TagType(T)(u); +pub fn activeTag(u: var) @TagType(@TypeOf(u)) { + const T = @TypeOf(u); + return @as(@TagType(T), u); } test "std.meta.activeTag" { @@ -364,10 +364,8 @@ test "std.meta.activeTag" { ///Given a tagged union type, and an enum, return the type of the union /// field corresponding to the enum tag. -pub fn TagPayloadType(comptime U: type, tag: var) type { - const Tag = @typeOf(tag); +pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type { testing.expect(trait.is(builtin.TypeId.Union)(U)); - testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); const info = @typeInfo(U).Union; @@ -386,13 +384,13 @@ test "std.meta.TagPayloadType" { }; const MovedEvent = TagPayloadType(Event, Event.Moved); var e: Event = undefined; - testing.expect(MovedEvent == @typeOf(e.Moved)); + testing.expect(MovedEvent == @TypeOf(e.Moved)); } ///Compares two of any type for equality. Containers are compared on a field-by-field basis, /// where possible. Pointers are not followed. -pub fn eql(a: var, b: @typeOf(a)) bool { - const T = @typeOf(a); +pub fn eql(a: var, b: @TypeOf(a)) bool { + const T = @TypeOf(a); switch (@typeId(T)) { builtin.TypeId.Struct => { @@ -469,19 +467,19 @@ test "std.meta.eql" { const s_1 = S{ .a = 134, .b = 123.3, - .c = "12345", + .c = "12345".*, }; const s_2 = S{ .a = 1, .b = 123.3, - .c = "54321", + .c = "54321".*, }; const s_3 = S{ .a = 134, .b = 123.3, - .c = "12345", + .c = "12345".*, }; const u_1 = U{ .f = 24 }; @@ -494,9 +492,9 @@ test "std.meta.eql" { testing.expect(eql(u_1, u_3)); testing.expect(!eql(u_1, u_2)); - var a1 = "abcdef"; - var a2 = "abcdef"; - var a3 = "ghijkl"; + var a1 = "abcdef".*; + var a2 = "abcdef".*; + var a3 = "ghijkl".*; testing.expect(eql(a1, a2)); testing.expect(!eql(a1, a3)); @@ -505,7 +503,7 @@ test "std.meta.eql" { const EU = struct { fn tst(err: bool) !u8 { if (err) return error.Error; - return u8(5); + return @as(u8, 5); } }; @@ -542,3 +540,19 @@ pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { } return error.InvalidEnumTag; } + +/// Given a type and a name, return the field index according to source order. +/// Returns `null` if the field is not found. +pub fn fieldIndex(comptime T: type, comptime name: []const u8) ?comptime_int { + inline for (fields(T)) |field, i| { + if (mem.eql(u8, field.name, name)) + return i; + } + return null; +} + +/// Given a type, reference all the declarations inside, so that the semantic analyzer sees them. +pub fn refAllDecls(comptime T: type) void { + if (!builtin.is_test) return; + _ = declarations(T); +} diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index 43be7f3df..d17f2deda 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -13,8 +13,7 @@ fn traitFnWorkaround(comptime T: type) bool { return false; } -pub const TraitFn = @typeOf(traitFnWorkaround); -/// +pub const TraitFn = @TypeOf(traitFnWorkaround); //////Trait generators @@ -46,7 +45,7 @@ test "std.meta.trait.multiTrait" { } }; - const isVector = multiTrait([_]TraitFn{ + const isVector = multiTrait(&[_]TraitFn{ hasFn("add"), hasField("x"), hasField("y"), @@ -55,13 +54,12 @@ test "std.meta.trait.multiTrait" { testing.expect(!isVector(u8)); } -/// pub fn hasFn(comptime name: []const u8) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { if (!comptime isContainer(T)) return false; if (!comptime @hasDecl(T, name)) return false; - const DeclType = @typeOf(@field(T, name)); + const DeclType = @TypeOf(@field(T, name)); const decl_type_id = @typeId(DeclType); return decl_type_id == builtin.TypeId.Fn; } @@ -79,7 +77,6 @@ test "std.meta.trait.hasFn" { testing.expect(!hasFn("useless")(u8)); } -/// pub fn hasField(comptime name: []const u8) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { @@ -113,7 +110,6 @@ test "std.meta.trait.hasField" { testing.expect(!hasField("value")(u8)); } -/// pub fn is(comptime id: builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { @@ -131,7 +127,6 @@ test "std.meta.trait.is" { testing.expect(!is(builtin.TypeId.Optional)(anyerror)); } -/// pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { @@ -173,7 +168,6 @@ test "std.meta.trait.isExtern" { testing.expect(!isExtern(u8)); } -/// pub fn isPacked(comptime T: type) bool { const Packed = builtin.TypeInfo.ContainerLayout.Packed; const info = @typeInfo(T); @@ -194,7 +188,6 @@ test "std.meta.trait.isPacked" { testing.expect(!isPacked(u8)); } -/// pub fn isUnsignedInt(comptime T: type) bool { return switch (@typeId(T)) { builtin.TypeId.Int => !@typeInfo(T).Int.is_signed, @@ -209,7 +202,6 @@ test "isUnsignedInt" { testing.expect(isUnsignedInt(f64) == false); } -/// pub fn isSignedInt(comptime T: type) bool { return switch (@typeId(T)) { builtin.TypeId.ComptimeInt => true, @@ -225,7 +217,6 @@ test "isSignedInt" { testing.expect(isSignedInt(f64) == false); } -/// pub fn isSingleItemPtr(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); @@ -236,12 +227,11 @@ pub fn isSingleItemPtr(comptime T: type) bool { test "std.meta.trait.isSingleItemPtr" { const array = [_]u8{0} ** 10; - testing.expect(isSingleItemPtr(@typeOf(&array[0]))); - testing.expect(!isSingleItemPtr(@typeOf(array))); - testing.expect(!isSingleItemPtr(@typeOf(array[0..1]))); + testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); + testing.expect(!isSingleItemPtr(@TypeOf(array))); + testing.expect(!isSingleItemPtr(@TypeOf(array[0..1]))); } -/// pub fn isManyItemPtr(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); @@ -253,12 +243,11 @@ pub fn isManyItemPtr(comptime T: type) bool { test "std.meta.trait.isManyItemPtr" { const array = [_]u8{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); - testing.expect(isManyItemPtr(@typeOf(mip))); - testing.expect(!isManyItemPtr(@typeOf(array))); - testing.expect(!isManyItemPtr(@typeOf(array[0..1]))); + testing.expect(isManyItemPtr(@TypeOf(mip))); + testing.expect(!isManyItemPtr(@TypeOf(array))); + testing.expect(!isManyItemPtr(@TypeOf(array[0..1]))); } -/// pub fn isSlice(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); @@ -269,12 +258,11 @@ pub fn isSlice(comptime T: type) bool { test "std.meta.trait.isSlice" { const array = [_]u8{0} ** 10; - testing.expect(isSlice(@typeOf(array[0..]))); - testing.expect(!isSlice(@typeOf(array))); - testing.expect(!isSlice(@typeOf(&array[0]))); + testing.expect(isSlice(@TypeOf(array[0..]))); + testing.expect(!isSlice(@TypeOf(array))); + testing.expect(!isSlice(@TypeOf(&array[0]))); } -/// pub fn isIndexable(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { const info = @typeInfo(T); @@ -291,13 +279,12 @@ test "std.meta.trait.isIndexable" { const array = [_]u8{0} ** 10; const slice = array[0..]; - testing.expect(isIndexable(@typeOf(array))); - testing.expect(isIndexable(@typeOf(&array))); - testing.expect(isIndexable(@typeOf(slice))); - testing.expect(!isIndexable(meta.Child(@typeOf(slice)))); + testing.expect(isIndexable(@TypeOf(array))); + testing.expect(isIndexable(@TypeOf(&array))); + testing.expect(isIndexable(@TypeOf(slice))); + testing.expect(!isIndexable(meta.Child(@TypeOf(slice)))); } -/// pub fn isNumber(comptime T: type) bool { return switch (@typeId(T)) { builtin.TypeId.Int, builtin.TypeId.Float, builtin.TypeId.ComptimeInt, builtin.TypeId.ComptimeFloat => true, @@ -313,13 +300,12 @@ test "std.meta.trait.isNumber" { testing.expect(isNumber(u32)); testing.expect(isNumber(f32)); testing.expect(isNumber(u64)); - testing.expect(isNumber(@typeOf(102))); - testing.expect(isNumber(@typeOf(102.123))); + testing.expect(isNumber(@TypeOf(102))); + testing.expect(isNumber(@TypeOf(102.123))); testing.expect(!isNumber([]u8)); testing.expect(!isNumber(NotANumber)); } -/// pub fn isConstPtr(comptime T: type) bool { if (!comptime is(builtin.TypeId.Pointer)(T)) return false; const info = @typeInfo(T); @@ -327,15 +313,14 @@ pub fn isConstPtr(comptime T: type) bool { } test "std.meta.trait.isConstPtr" { - var t = u8(0); - const c = u8(0); - testing.expect(isConstPtr(*const @typeOf(t))); - testing.expect(isConstPtr(@typeOf(&c))); - testing.expect(!isConstPtr(*@typeOf(t))); - testing.expect(!isConstPtr(@typeOf(6))); + var t = @as(u8, 0); + const c = @as(u8, 0); + testing.expect(isConstPtr(*const @TypeOf(t))); + testing.expect(isConstPtr(@TypeOf(&c))); + testing.expect(!isConstPtr(*@TypeOf(t))); + testing.expect(!isConstPtr(@TypeOf(6))); } -/// pub fn isContainer(comptime T: type) bool { const info = @typeInfo(T); return switch (info) { diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 5f3b9272d..7fbe4fde1 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -1,24 +1,37 @@ const std = @import("std.zig"); const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; +const os = std.os; +const assert = std.debug.assert; +const windows = os.windows; const testing = std.testing; const SpinLock = std.SpinLock; -const linux = std.os.linux; -const windows = std.os.windows; +const ResetEvent = std.ResetEvent; -/// Lock may be held only once. If the same thread -/// tries to acquire the same mutex twice, it deadlocks. -/// This type must be initialized at runtime, and then deinitialized when no -/// longer needed, to free resources. -/// If you need static initialization, use std.StaticallyInitializedMutex. -/// The Linux implementation is based on mutex3 from -/// https://www.akkadia.org/drepper/futex.pdf -/// When an application is built in single threaded release mode, all the functions are -/// no-ops. In single threaded debug mode, there is deadlock detection. +/// Lock may be held only once. If the same thread tries to acquire +/// the same mutex twice, it deadlocks. This type supports static +/// initialization and is at most `@sizeOf(usize)` in size. When an +/// application is built in single threaded release mode, all the +/// functions are no-ops. In single threaded debug mode, there is +/// deadlock detection. +/// +/// Example usage: +/// var m = Mutex.init(); +/// defer m.deinit(); +/// +/// const lock = m.acquire(); +/// defer lock.release(); +/// ... critical code +/// +/// Non-blocking: +/// if (m.tryAcquire) |lock| { +/// defer lock.release(); +/// // ... critical section +/// } else { +/// // ... lock not acquired +/// } pub const Mutex = if (builtin.single_threaded) struct { - lock: @typeOf(lock_init), + lock: @TypeOf(lock_init), const lock_init = if (std.debug.runtime_safety) false else {}; @@ -31,96 +44,259 @@ pub const Mutex = if (builtin.single_threaded) } } }; + + /// Create a new mutex in unlocked state. pub fn init() Mutex { return Mutex{ .lock = lock_init }; } - pub fn deinit(self: *Mutex) void {} - pub fn acquire(self: *Mutex) Held { - if (std.debug.runtime_safety and self.lock) { - @panic("deadlock detected"); + /// Free a mutex created with init. Calling this while the + /// mutex is held is illegal behavior. + pub fn deinit(self: *Mutex) void { + self.* = undefined; + } + + /// Try to acquire the mutex without blocking. Returns null if + /// the mutex is unavailable. Otherwise returns Held. Call + /// release on Held. + pub fn tryAcquire(self: *Mutex) ?Held { + if (std.debug.runtime_safety) { + if (self.lock) return null; + self.lock = true; } return Held{ .mutex = self }; } + + /// Acquire the mutex. Will deadlock if the mutex is already + /// held by the calling thread. + pub fn acquire(self: *Mutex) Held { + return self.tryAcquire() orelse @panic("deadlock detected"); + } } -else switch (builtin.os) { - builtin.Os.linux => struct { - /// 0: unlocked - /// 1: locked, no waiters - /// 2: locked, one or more waiters - lock: i32, +else if (builtin.os == .windows) +// https://locklessinc.com/articles/keyed_events/ + extern union { + locked: u8, + waiters: u32, + + const WAKE = 1 << 8; + const WAIT = 1 << 9; + + pub fn init() Mutex { + return Mutex{ .waiters = 0 }; + } + + pub fn deinit(self: *Mutex) void { + self.* = undefined; + } + + pub fn tryAcquire(self: *Mutex) ?Held { + if (@atomicRmw(u8, &self.locked, .Xchg, 1, .Acquire) != 0) + return null; + return Held{ .mutex = self }; + } + + pub fn acquire(self: *Mutex) Held { + return self.tryAcquire() orelse self.acquireSlow(); + } + + fn acquireSpinning(self: *Mutex) Held { + @setCold(true); + while (true) : (SpinLock.yield()) { + return self.tryAcquire() orelse continue; + } + } + + fn acquireSlow(self: *Mutex) Held { + // try to use NT keyed events for blocking, falling back to spinlock if unavailable + @setCold(true); + const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return self.acquireSpinning(); + const key = @ptrCast(*const c_void, &self.waiters); + + while (true) : (SpinLock.loopHint(1)) { + const waiters = @atomicLoad(u32, &self.waiters, .Monotonic); + + // try and take lock if unlocked + if ((waiters & 1) == 0) { + if (@atomicRmw(u8, &self.locked, .Xchg, 1, .Acquire) == 0) { + return Held{ .mutex = self }; + } + + // otherwise, try and update the waiting count. + // then unset the WAKE bit so that another unlocker can wake up a thread. + } else if (@cmpxchgWeak(u32, &self.waiters, waiters, (waiters + WAIT) | 1, .Monotonic, .Monotonic) == null) { + const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == 0); + _ = @atomicRmw(u32, &self.waiters, .Sub, WAKE, .Monotonic); + } + } + } pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); - if (c != 1) { - _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); - switch (linux.getErrno(rc)) { - 0 => {}, - linux.EINVAL => unreachable, - else => unreachable, + // unlock without a rmw/cmpxchg instruction + @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release); + const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return; + const key = @ptrCast(*const c_void, &self.mutex.waiters); + + while (true) : (SpinLock.loopHint(1)) { + const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic); + + // no one is waiting + if (waiters < WAIT) return; + // someone grabbed the lock and will do the wake instead + if (waiters & 1 != 0) return; + // someone else is currently waking up + if (waiters & WAKE != 0) return; + + // try to decrease the waiter count & set the WAKE bit meaning a thread is waking up + if (@cmpxchgWeak(u32, &self.mutex.waiters, waiters, waiters - WAIT + WAKE, .Release, .Monotonic) == null) { + const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == 0); + return; } } } }; + } +else if (builtin.link_libc or builtin.os == .linux) +// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs + struct { + state: usize, - pub fn init() Mutex { - return Mutex{ .lock = 0 }; - } + /// number of times to spin trying to acquire the lock. + /// https://webkit.org/blog/6161/locking-in-webkit/ + const SPIN_COUNT = 40; - pub fn deinit(self: *Mutex) void {} + const MUTEX_LOCK: usize = 1 << 0; + const QUEUE_LOCK: usize = 1 << 1; + const QUEUE_MASK: usize = ~(MUTEX_LOCK | QUEUE_LOCK); - pub fn acquire(self: *Mutex) Held { - var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse - return Held{ .mutex = self }; - if (c != 2) - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - while (c != 0) { - const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); - switch (linux.getErrno(rc)) { - 0, linux.EINTR, linux.EAGAIN => {}, - linux.EINVAL => unreachable, - else => unreachable, - } - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - } - return Held{ .mutex = self }; - } - }, - // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a - // better implementation of this. The problem is we need the init() function to have access to - // the address of the CRITICAL_SECTION, and then have it not move. - builtin.Os.windows => std.StaticallyInitializedMutex, - else => struct { - /// TODO better implementation than spin lock. - /// When changing this, one must also change the corresponding - /// std.StaticallyInitializedMutex code, since it aliases this type, - /// under the assumption that it works both statically and at runtime. - lock: SpinLock, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock }); - } + const Node = struct { + next: ?*Node, + event: ResetEvent, }; pub fn init() Mutex { - return Mutex{ .lock = SpinLock.init() }; + return Mutex{ .state = 0 }; } - pub fn deinit(self: *Mutex) void {} + pub fn deinit(self: *Mutex) void { + self.* = undefined; + } - pub fn acquire(self: *Mutex) Held { - _ = self.lock.acquire(); + pub fn tryAcquire(self: *Mutex) ?Held { + if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null) + return null; return Held{ .mutex = self }; } - }, -}; + + pub fn acquire(self: *Mutex) Held { + return self.tryAcquire() orelse { + self.acquireSlow(); + return Held{ .mutex = self }; + }; + } + + fn acquireSlow(self: *Mutex) void { + // inlining the fast path and hiding *Slow() + // calls behind a @setCold(true) appears to + // improve performance in release builds. + @setCold(true); + while (true) { + + // try and spin for a bit to acquire the mutex if theres currently no queue + var spin_count: u32 = SPIN_COUNT; + var state = @atomicLoad(usize, &self.state, .Monotonic); + while (spin_count != 0) : (spin_count -= 1) { + if (state & MUTEX_LOCK == 0) { + _ = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return; + } else if (state & QUEUE_MASK == 0) { + break; + } + SpinLock.yield(); + state = @atomicLoad(usize, &self.state, .Monotonic); + } + + // create the ResetEvent node on the stack + // (faster than threadlocal on platforms like OSX) + var node: Node = undefined; + node.event = ResetEvent.init(); + defer node.event.deinit(); + + // we've spun too long, try and add our node to the LIFO queue. + // if the mutex becomes available in the process, try and grab it instead. + while (true) { + if (state & MUTEX_LOCK == 0) { + _ = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return; + } else { + node.next = @intToPtr(?*Node, state & QUEUE_MASK); + const new_state = @ptrToInt(&node) | (state & ~QUEUE_MASK); + _ = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse { + node.event.wait(); + break; + }; + } + SpinLock.yield(); + state = @atomicLoad(usize, &self.state, .Monotonic); + } + } + } + + /// Returned when the lock is acquired. Call release to + /// release. + pub const Held = struct { + mutex: *Mutex, + + /// Release the held lock. + pub fn release(self: Held) void { + // first, remove the lock bit so another possibly parallel acquire() can succeed. + // use .Sub since it can be usually compiled down more efficiency + // (`lock sub` on x86) vs .And ~MUTEX_LOCK (`lock cmpxchg` loop on x86) + const state = @atomicRmw(usize, &self.mutex.state, .Sub, MUTEX_LOCK, .Release); + + // if the LIFO queue isnt locked and it has a node, try and wake up the node. + if ((state & QUEUE_LOCK) == 0 and (state & QUEUE_MASK) != 0) + self.mutex.releaseSlow(); + } + }; + + fn releaseSlow(self: *Mutex) void { + @setCold(true); + + // try and lock the LFIO queue to pop a node off, + // stopping altogether if its already locked or the queue is empty + var state = @atomicLoad(usize, &self.state, .Monotonic); + while (true) : (SpinLock.loopHint(1)) { + if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0) + return; + state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break; + } + + // acquired the QUEUE_LOCK, try and pop a node to wake it. + // if the mutex is locked, then unset QUEUE_LOCK and let + // the thread who holds the mutex do the wake-up on unlock() + while (true) : (SpinLock.loopHint(1)) { + if ((state & MUTEX_LOCK) != 0) { + state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return; + } else { + const node = @intToPtr(*Node, state & QUEUE_MASK); + const new_state = @ptrToInt(node.next); + state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Acquire) orelse { + node.event.set(); + return; + }; + } + } + } + } + + // for platforms without a known OS blocking + // primitive, default to SpinLock for correctness +else + SpinLock; const TestContext = struct { mutex: *Mutex, @@ -130,8 +306,8 @@ const TestContext = struct { }; test "std.Mutex" { - var plenty_of_memory = try std.heap.direct_allocator.alloc(u8, 300 * 1024); - defer std.heap.direct_allocator.free(plenty_of_memory); + var plenty_of_memory = try std.heap.page_allocator.alloc(u8, 300 * 1024); + defer std.heap.page_allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); var a = &fixed_buffer_allocator.allocator; diff --git a/lib/std/net.zig b/lib/std/net.zig index be9d18056..47ce95c99 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -4,220 +4,364 @@ const assert = std.debug.assert; const net = @This(); const mem = std.mem; const os = std.os; +const fs = std.fs; -pub const TmpWinAddr = struct { - family: u8, - data: [14]u8, -}; - -pub const OsAddress = switch (builtin.os) { - builtin.Os.windows => TmpWinAddr, - else => os.sockaddr, -}; - -pub const Address = struct { - os_addr: OsAddress, - - pub fn initIp4(ip4: u32, _port: u16) Address { - return Address{ - .os_addr = os.sockaddr{ - .in = os.sockaddr_in{ - .family = os.AF_INET, - .port = mem.nativeToBig(u16, _port), - .addr = ip4, - .zero = [_]u8{0} ** 8, - }, - }, - }; - } - - pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { - return Address{ - .os_addr = os.sockaddr{ - .in6 = os.sockaddr_in6{ - .family = os.AF_INET6, - .port = mem.nativeToBig(u16, _port), - .flowinfo = 0, - .addr = ip6.addr, - .scope_id = ip6.scope_id, - }, - }, - }; - } - - pub fn port(self: Address) u16 { - return mem.bigToNative(u16, self.os_addr.in.port); - } - - pub fn initPosix(addr: os.sockaddr) Address { - return Address{ .os_addr = addr }; - } - - pub fn format(self: *const Address, out_stream: var) !void { - switch (self.os_addr.in.family) { - os.AF_INET => { - const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); - const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); - try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); - }, - os.AF_INET6 => { - const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); - try out_stream.print("[TODO render ip6 address]:{}", native_endian_port); - }, - else => try out_stream.write("(unrecognized address family)"), - } - } -}; - -pub fn parseIp4(buf: []const u8) !u32 { - var result: u32 = undefined; - const out_ptr = @sliceToBytes((*[1]u32)(&result)[0..]); - - var x: u8 = 0; - var index: u8 = 0; - var saw_any_digits = false; - for (buf) |c| { - if (c == '.') { - if (!saw_any_digits) { - return error.InvalidCharacter; - } - if (index == 3) { - return error.InvalidEnd; - } - out_ptr[index] = x; - index += 1; - x = 0; - saw_any_digits = false; - } else if (c >= '0' and c <= '9') { - saw_any_digits = true; - const digit = c - '0'; - if (@mulWithOverflow(u8, x, 10, &x)) { - return error.Overflow; - } - if (@addWithOverflow(u8, x, digit, &x)) { - return error.Overflow; - } - } else { - return error.InvalidCharacter; - } - } - if (index == 3 and saw_any_digits) { - out_ptr[index] = x; - return result; - } - - return error.Incomplete; +test "" { + _ = @import("net/test.zig"); } -pub const Ip6Addr = struct { - scope_id: u32, - addr: [16]u8, -}; +const has_unix_sockets = @hasDecl(os, "sockaddr_un"); -pub fn parseIp6(buf: []const u8) !Ip6Addr { - var result: Ip6Addr = undefined; - result.scope_id = 0; - const ip_slice = result.addr[0..]; +pub const Address = extern union { + any: os.sockaddr, + in: os.sockaddr_in, + in6: os.sockaddr_in6, + un: if (has_unix_sockets) os.sockaddr_un else void, - var x: u16 = 0; - var saw_any_digits = false; - var index: u8 = 0; - var scope_id = false; - for (buf) |c| { - if (scope_id) { - if (c >= '0' and c <= '9') { - const digit = c - '0'; - if (@mulWithOverflow(u32, result.scope_id, 10, &result.scope_id)) { - return error.Overflow; + // TODO this crashed the compiler + //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); + + pub fn parseIp(name: []const u8, port: u16) !Address { + if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + => {}, + } + + if (parseIp6(name, port)) |ip6| return ip6 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + error.InvalidIpv4Mapping, + => {}, + } + + return error.InvalidIPAddressFormat; + } + + pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { + switch (family) { + os.AF_INET => return parseIp4(name, port), + os.AF_INET6 => return parseIp6(name, port), + os.AF_UNSPEC => return parseIp(name, port), + else => unreachable, + } + } + + pub fn parseIp6(buf: []const u8, port: u16) !Address { + var result = Address{ + .in6 = os.sockaddr_in6{ + .scope_id = 0, + .port = mem.nativeToBig(u16, port), + .flowinfo = 0, + .addr = undefined, + }, + }; + var ip_slice = result.in6.addr[0..]; + + var tail: [16]u8 = undefined; + + var x: u16 = 0; + var saw_any_digits = false; + var index: u8 = 0; + var scope_id = false; + var abbrv = false; + for (buf) |c, i| { + if (scope_id) { + if (c >= '0' and c <= '9') { + const digit = c - '0'; + if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) { + return error.Overflow; + } + if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) { + return error.Overflow; + } + } else { + return error.InvalidCharacter; } - if (@addWithOverflow(u32, result.scope_id, digit, &result.scope_id)) { - return error.Overflow; + } else if (c == ':') { + if (!saw_any_digits) { + if (abbrv) return error.InvalidCharacter; // ':::' + if (i != 0) abbrv = true; + mem.set(u8, ip_slice[index..], 0); + ip_slice = tail[0..]; + index = 0; + continue; + } + if (index == 14) { + return error.InvalidEnd; } - } else { - return error.InvalidCharacter; - } - } else if (c == ':') { - if (!saw_any_digits) { - return error.InvalidCharacter; - } - if (index == 14) { - return error.InvalidEnd; - } - ip_slice[index] = @truncate(u8, x >> 8); - index += 1; - ip_slice[index] = @truncate(u8, x); - index += 1; - - x = 0; - saw_any_digits = false; - } else if (c == '%') { - if (!saw_any_digits) { - return error.InvalidCharacter; - } - if (index == 14) { ip_slice[index] = @truncate(u8, x >> 8); index += 1; ip_slice[index] = @truncate(u8, x); index += 1; + + x = 0; + saw_any_digits = false; + } else if (c == '%') { + if (!saw_any_digits) { + return error.InvalidCharacter; + } + scope_id = true; + saw_any_digits = false; + } else if (c == '.') { + if (!abbrv or ip_slice[0] != 0xff or ip_slice[1] != 0xff) { + // must start with '::ffff:' + return error.InvalidIpv4Mapping; + } + const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1; + const addr = (parseIp4(buf[start_index..], 0) catch { + return error.InvalidIpv4Mapping; + }).in.addr; + ip_slice = result.in6.addr[0..]; + ip_slice[10] = 0xff; + ip_slice[11] = 0xff; + + const ptr = @sliceToBytes(@as(*const [1]u32, &addr)[0..]); + + ip_slice[12] = ptr[0]; + ip_slice[13] = ptr[1]; + ip_slice[14] = ptr[2]; + ip_slice[15] = ptr[3]; + return result; + } else { + const digit = try std.fmt.charToDigit(c, 16); + if (@mulWithOverflow(u16, x, 16, &x)) { + return error.Overflow; + } + if (@addWithOverflow(u16, x, digit, &x)) { + return error.Overflow; + } + saw_any_digits = true; } - scope_id = true; - saw_any_digits = false; + } + + if (!saw_any_digits and !abbrv) { + return error.Incomplete; + } + + if (index == 14) { + ip_slice[14] = @truncate(u8, x >> 8); + ip_slice[15] = @truncate(u8, x); + return result; } else { - const digit = try std.fmt.charToDigit(c, 16); - if (@mulWithOverflow(u16, x, 16, &x)) { - return error.Overflow; - } - if (@addWithOverflow(u16, x, digit, &x)) { - return error.Overflow; - } - saw_any_digits = true; + ip_slice[index] = @truncate(u8, x >> 8); + index += 1; + ip_slice[index] = @truncate(u8, x); + index += 1; + mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]); + return result; } } - if (!saw_any_digits) { + pub fn parseIp4(buf: []const u8, port: u16) !Address { + var result = Address{ + .in = os.sockaddr_in{ + .port = mem.nativeToBig(u16, port), + .addr = undefined, + }, + }; + const out_ptr = @sliceToBytes(@as(*[1]u32, &result.in.addr)[0..]); + + var x: u8 = 0; + var index: u8 = 0; + var saw_any_digits = false; + for (buf) |c| { + if (c == '.') { + if (!saw_any_digits) { + return error.InvalidCharacter; + } + if (index == 3) { + return error.InvalidEnd; + } + out_ptr[index] = x; + index += 1; + x = 0; + saw_any_digits = false; + } else if (c >= '0' and c <= '9') { + saw_any_digits = true; + x = try std.math.mul(u8, x, 10); + x = try std.math.add(u8, x, c - '0'); + } else { + return error.InvalidCharacter; + } + } + if (index == 3 and saw_any_digits) { + out_ptr[index] = x; + return result; + } + return error.Incomplete; } - if (scope_id) { - return result; + pub fn initIp4(addr: [4]u8, port: u16) Address { + return Address{ + .in = os.sockaddr_in{ + .port = mem.nativeToBig(u16, port), + .addr = @ptrCast(*align(1) const u32, &addr).*, + }, + }; } - if (index == 14) { - ip_slice[14] = @truncate(u8, x >> 8); - ip_slice[15] = @truncate(u8, x); - return result; + pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address { + return Address{ + .in6 = os.sockaddr_in6{ + .addr = addr, + .port = mem.nativeToBig(u16, port), + .flowinfo = flowinfo, + .scope_id = scope_id, + }, + }; } - return error.Incomplete; -} + pub fn initUnix(path: []const u8) !Address { + var sock_addr = os.sockaddr_un{ + .family = os.AF_UNIX, + .path = undefined, + }; -test "std.net.parseIp4" { - assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001)); + // this enables us to have the proper length of the socket in getOsSockLen + mem.set(u8, &sock_addr.path, 0); - testParseIp4Fail("256.0.0.1", error.Overflow); - testParseIp4Fail("x.0.0.1", error.InvalidCharacter); - testParseIp4Fail("127.0.0.1.1", error.InvalidEnd); - testParseIp4Fail("127.0.0.", error.Incomplete); - testParseIp4Fail("100..0.1", error.InvalidCharacter); -} + if (path.len > sock_addr.path.len) return error.NameTooLong; + mem.copy(u8, &sock_addr.path, path); -fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { - if (parseIp4(buf)) |_| { - @panic("expected error"); - } else |e| { - assert(e == expected_err); + return Address{ .un = sock_addr }; } -} -test "std.net.parseIp6" { - const addr = try parseIp6("FF01:0:0:0:0:0:0:FB"); - assert(addr.addr[0] == 0xff); - assert(addr.addr[1] == 0x01); - assert(addr.addr[2] == 0x00); -} + /// Returns the port in native endian. + /// Asserts that the address is ip4 or ip6. + pub fn getPort(self: Address) u16 { + const big_endian_port = switch (self.any.family) { + os.AF_INET => self.in.port, + os.AF_INET6 => self.in6.port, + else => unreachable, + }; + return mem.bigToNative(u16, big_endian_port); + } -pub fn connectUnixSocket(path: []const u8) !std.fs.File { - const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0; + /// `port` is native-endian. + /// Asserts that the address is ip4 or ip6. + pub fn setPort(self: *Address, port: u16) void { + const ptr = switch (self.any.family) { + os.AF_INET => &self.in.port, + os.AF_INET6 => &self.in6.port, + else => unreachable, + }; + ptr.* = mem.nativeToBig(u16, port); + } + + /// Asserts that `addr` is an IP address. + /// This function will read past the end of the pointer, with a size depending + /// on the address family. + pub fn initPosix(addr: *align(4) const os.sockaddr) Address { + switch (addr.family) { + os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* }, + os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* }, + else => unreachable, + } + } + + pub fn format( + self: Address, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + context: var, + comptime Errors: type, + output: fn (@TypeOf(context), []const u8) Errors!void, + ) !void { + switch (self.any.family) { + os.AF_INET => { + const port = mem.bigToNative(u16, self.in.port); + const bytes = @ptrCast(*const [4]u8, &self.in.addr); + try std.fmt.format(context, Errors, output, "{}.{}.{}.{}:{}", .{ + bytes[0], + bytes[1], + bytes[2], + bytes[3], + port, + }); + }, + os.AF_INET6 => { + const port = mem.bigToNative(u16, self.in6.port); + if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { + try std.fmt.format(context, Errors, output, "[::ffff:{}.{}.{}.{}]:{}", .{ + self.in6.addr[12], + self.in6.addr[13], + self.in6.addr[14], + self.in6.addr[15], + port, + }); + return; + } + const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr); + const native_endian_parts = switch (builtin.endian) { + .Big => big_endian_parts.*, + .Little => blk: { + var buf: [8]u16 = undefined; + for (big_endian_parts) |part, i| { + buf[i] = mem.bigToNative(u16, part); + } + break :blk buf; + }, + }; + try output(context, "["); + var i: usize = 0; + var abbrv = false; + while (i < native_endian_parts.len) : (i += 1) { + if (native_endian_parts[i] == 0) { + if (!abbrv) { + try output(context, if (i == 0) "::" else ":"); + abbrv = true; + } + continue; + } + try std.fmt.format(context, Errors, output, "{x}", .{native_endian_parts[i]}); + if (i != native_endian_parts.len - 1) { + try output(context, ":"); + } + } + try std.fmt.format(context, Errors, output, "]:{}", .{port}); + }, + os.AF_UNIX => { + if (!has_unix_sockets) { + unreachable; + } + + try std.fmt.format(context, Errors, output, "{}", .{&self.un.path}); + }, + else => unreachable, + } + } + + pub fn eql(a: Address, b: Address) bool { + const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()]; + const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()]; + return mem.eql(u8, a_bytes, b_bytes); + } + + fn getOsSockLen(self: Address) os.socklen_t { + switch (self.any.family) { + os.AF_INET => return @sizeOf(os.sockaddr_in), + os.AF_INET6 => return @sizeOf(os.sockaddr_in6), + os.AF_UNIX => { + if (!has_unix_sockets) { + unreachable; + } + + const path_len = std.mem.len(u8, @ptrCast([*:0]const u8, &self.un.path)); + return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); + }, + else => unreachable, + } + } +}; + +pub fn connectUnixSocket(path: []const u8) !fs.File { + const opt_non_block = if (std.io.mode == .evented) os.SOCK_NONBLOCK else 0; const sockfd = try os.socket( os.AF_UNIX, os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block, @@ -225,23 +369,1024 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { ); errdefer os.close(sockfd); - var sock_addr = os.sockaddr{ - .un = os.sockaddr_un{ - .family = os.AF_UNIX, - .path = undefined, - }, - }; + var addr = try std.net.Address.initUnix(path); - if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong; - mem.copy(u8, sock_addr.un.path[0..], path); - const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len); - if (std.event.Loop.instance) |loop| { - try os.connect_async(sockfd, &sock_addr, size); - try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); - try os.getsockoptError(sockfd); + try os.connect( + sockfd, + &addr.any, + addr.getOsSockLen(), + ); + + return fs.File.openHandle(sockfd); +} + +pub const AddressList = struct { + arena: std.heap.ArenaAllocator, + addrs: []Address, + canon_name: ?[]u8, + + fn deinit(self: *AddressList) void { + // Here we copy the arena allocator into stack memory, because + // otherwise it would destroy itself while it was still working. + var arena = self.arena; + arena.deinit(); + // self is destroyed + } +}; + +/// All memory allocated with `allocator` will be freed before this function returns. +pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) !fs.File { + const list = getAddressList(allocator, name, port); + defer list.deinit(); + + const addrs = list.addrs.toSliceConst(); + if (addrs.len == 0) return error.UnknownHostName; + + return tcpConnectToAddress(addrs[0], port); +} + +pub fn tcpConnectToAddress(address: Address) !fs.File { + const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; + const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; + const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); + errdefer os.close(sockfd); + try os.connect(sockfd, &address.any, address.getOsSockLen()); + + return fs.File{ .handle = sockfd }; +} + +/// Call `AddressList.deinit` on the result. +pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { + const result = blk: { + var arena = std.heap.ArenaAllocator.init(allocator); + errdefer arena.deinit(); + + const result = try arena.allocator.create(AddressList); + result.* = AddressList{ + .arena = arena, + .addrs = undefined, + .canon_name = null, + }; + break :blk result; + }; + const arena = &result.arena.allocator; + errdefer result.arena.deinit(); + + if (builtin.link_libc) { + const c = std.c; + const name_c = try std.cstr.addNullByte(allocator, name); + defer allocator.free(name_c); + + const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port}); + defer allocator.free(port_c); + + const hints = os.addrinfo{ + .flags = c.AI_NUMERICSERV, + .family = os.AF_UNSPEC, + .socktype = os.SOCK_STREAM, + .protocol = os.IPPROTO_TCP, + .canonname = null, + .addr = null, + .addrlen = 0, + .next = null, + }; + var res: *os.addrinfo = undefined; + switch (os.system.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res)) { + 0 => {}, + c.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses, + c.EAI_AGAIN => return error.TemporaryNameServerFailure, + c.EAI_BADFLAGS => unreachable, // Invalid hints + c.EAI_FAIL => return error.NameServerFailure, + c.EAI_FAMILY => return error.AddressFamilyNotSupported, + c.EAI_MEMORY => return error.OutOfMemory, + c.EAI_NODATA => return error.HostLacksNetworkAddresses, + c.EAI_NONAME => return error.UnknownHostName, + c.EAI_SERVICE => return error.ServiceUnavailable, + c.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints + c.EAI_SYSTEM => switch (os.errno(-1)) { + else => |e| return os.unexpectedErrno(e), + }, + else => unreachable, + } + defer os.system.freeaddrinfo(res); + + const addr_count = blk: { + var count: usize = 0; + var it: ?*os.addrinfo = res; + while (it) |info| : (it = info.next) { + if (info.addr != null) { + count += 1; + } + } + break :blk count; + }; + result.addrs = try arena.alloc(Address, addr_count); + + var it: ?*os.addrinfo = res; + var i: usize = 0; + while (it) |info| : (it = info.next) { + const addr = info.addr orelse continue; + result.addrs[i] = Address.initPosix(@alignCast(4, addr)); + + if (info.canonname) |n| { + if (result.canon_name == null) { + result.canon_name = try mem.dupe(arena, u8, mem.toSliceConst(u8, n)); + } + } + i += 1; + } + + return result; + } + if (builtin.os == .linux) { + const flags = std.c.AI_NUMERICSERV; + const family = os.AF_UNSPEC; + var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); + defer lookup_addrs.deinit(); + + var canon = std.Buffer.initNull(arena); + defer canon.deinit(); + + try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port); + + result.addrs = try arena.alloc(Address, lookup_addrs.len); + if (!canon.isNull()) { + result.canon_name = canon.toOwnedSlice(); + } + + for (lookup_addrs.toSliceConst()) |lookup_addr, i| { + result.addrs[i] = lookup_addr.addr; + assert(result.addrs[i].getPort() == port); + } + + return result; + } + @compileError("std.net.getAddresses unimplemented for this OS"); +} + +const LookupAddr = struct { + addr: Address, + sortkey: i32 = 0, +}; + +const DAS_USABLE = 0x40000000; +const DAS_MATCHINGSCOPE = 0x20000000; +const DAS_MATCHINGLABEL = 0x10000000; +const DAS_PREC_SHIFT = 20; +const DAS_SCOPE_SHIFT = 16; +const DAS_PREFIX_SHIFT = 8; +const DAS_ORDER_SHIFT = 0; + +fn linuxLookupName( + addrs: *std.ArrayList(LookupAddr), + canon: *std.Buffer, + opt_name: ?[]const u8, + family: os.sa_family_t, + flags: u32, + port: u16, +) !void { + if (opt_name) |name| { + // reject empty name and check len so it fits into temp bufs + try canon.replaceContents(name); + if (Address.parseExpectingFamily(name, family, port)) |addr| { + try addrs.append(LookupAddr{ .addr = addr }); + } else |name_err| if ((flags & std.c.AI_NUMERICHOST) != 0) { + return name_err; + } else { + try linuxLookupNameFromHosts(addrs, canon, name, family, port); + if (addrs.len == 0) { + try linuxLookupNameFromDnsSearch(addrs, canon, name, family, port); + } + } } else { - try os.connect(sockfd, &sock_addr, size); + try canon.resize(0); + try linuxLookupNameFromNull(addrs, family, flags, port); + } + if (addrs.len == 0) return error.UnknownHostName; + + // No further processing is needed if there are fewer than 2 + // results or if there are only IPv4 results. + if (addrs.len == 1 or family == os.AF_INET) return; + const all_ip4 = for (addrs.toSliceConst()) |addr| { + if (addr.addr.any.family != os.AF_INET) break false; + } else true; + if (all_ip4) return; + + // The following implements a subset of RFC 3484/6724 destination + // address selection by generating a single 31-bit sort key for + // each address. Rules 3, 4, and 7 are omitted for having + // excessive runtime and code size cost and dubious benefit. + // So far the label/precedence table cannot be customized. + // This implementation is ported from musl libc. + // A more idiomatic "ziggy" implementation would be welcome. + for (addrs.toSlice()) |*addr, i| { + var key: i32 = 0; + var sa6: os.sockaddr_in6 = undefined; + @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6)); + var da6 = os.sockaddr_in6{ + .family = os.AF_INET6, + .scope_id = addr.addr.in6.scope_id, + .port = 65535, + .flowinfo = 0, + .addr = [1]u8{0} ** 16, + }; + var sa4: os.sockaddr_in = undefined; + @memset(@ptrCast([*]u8, &sa4), 0, @sizeOf(os.sockaddr_in)); + var da4 = os.sockaddr_in{ + .family = os.AF_INET, + .port = 65535, + .addr = 0, + .zero = [1]u8{0} ** 8, + }; + var sa: *align(4) os.sockaddr = undefined; + var da: *align(4) os.sockaddr = undefined; + var salen: os.socklen_t = undefined; + var dalen: os.socklen_t = undefined; + if (addr.addr.any.family == os.AF_INET6) { + mem.copy(u8, &da6.addr, &addr.addr.in6.addr); + da = @ptrCast(*os.sockaddr, &da6); + dalen = @sizeOf(os.sockaddr_in6); + sa = @ptrCast(*os.sockaddr, &sa6); + salen = @sizeOf(os.sockaddr_in6); + } else { + mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); + mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr); + da4.addr = addr.addr.in.addr; + da = @ptrCast(*os.sockaddr, &da4); + dalen = @sizeOf(os.sockaddr_in); + sa = @ptrCast(*os.sockaddr, &sa4); + salen = @sizeOf(os.sockaddr_in); + } + const dpolicy = policyOf(da6.addr); + const dscope: i32 = scopeOf(da6.addr); + const dlabel = dpolicy.label; + const dprec: i32 = dpolicy.prec; + const MAXADDRS = 3; + var prefixlen: i32 = 0; + const sock_flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC; + if (os.socket(addr.addr.any.family, sock_flags, os.IPPROTO_UDP)) |fd| syscalls: { + defer os.close(fd); + os.connect(fd, da, dalen) catch break :syscalls; + key |= DAS_USABLE; + os.getsockname(fd, sa, &salen) catch break :syscalls; + if (addr.addr.any.family == os.AF_INET) { + // TODO sa6.addr[12..16] should return *[4]u8, making this cast unnecessary. + mem.writeIntNative(u32, @ptrCast(*[4]u8, &sa6.addr[12]), sa4.addr); + } + if (dscope == @as(i32, scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE; + if (dlabel == labelOf(sa6.addr)) key |= DAS_MATCHINGLABEL; + prefixlen = prefixMatch(sa6.addr, da6.addr); + } else |_| {} + key |= dprec << DAS_PREC_SHIFT; + key |= (15 - dscope) << DAS_SCOPE_SHIFT; + key |= prefixlen << DAS_PREFIX_SHIFT; + key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT; + addr.sortkey = key; + } + std.sort.sort(LookupAddr, addrs.toSlice(), addrCmpLessThan); +} + +const Policy = struct { + addr: [16]u8, + len: u8, + mask: u8, + prec: u8, + label: u8, +}; + +const defined_policies = [_]Policy{ + Policy{ + .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".*, + .len = 15, + .mask = 0xff, + .prec = 50, + .label = 0, + }, + Policy{ + .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00".*, + .len = 11, + .mask = 0xff, + .prec = 35, + .label = 4, + }, + Policy{ + .addr = "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, + .len = 1, + .mask = 0xff, + .prec = 30, + .label = 2, + }, + Policy{ + .addr = "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, + .len = 3, + .mask = 0xff, + .prec = 5, + .label = 5, + }, + Policy{ + .addr = "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, + .len = 0, + .mask = 0xfe, + .prec = 3, + .label = 13, + }, + // These are deprecated and/or returned to the address + // pool, so despite the RFC, treating them as special + // is probably wrong. + // { "", 11, 0xff, 1, 3 }, + // { "\xfe\xc0", 1, 0xc0, 1, 11 }, + // { "\x3f\xfe", 1, 0xff, 1, 12 }, + // Last rule must match all addresses to stop loop. + Policy{ + .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*, + .len = 0, + .mask = 0, + .prec = 40, + .label = 1, + }, +}; + +fn policyOf(a: [16]u8) *const Policy { + for (defined_policies) |*policy| { + if (!mem.eql(u8, a[0..policy.len], policy.addr[0..policy.len])) continue; + if ((a[policy.len] & policy.mask) != policy.addr[policy.len]) continue; + return policy; + } + unreachable; +} + +fn scopeOf(a: [16]u8) u8 { + if (IN6_IS_ADDR_MULTICAST(a)) return a[1] & 15; + if (IN6_IS_ADDR_LINKLOCAL(a)) return 2; + if (IN6_IS_ADDR_LOOPBACK(a)) return 2; + if (IN6_IS_ADDR_SITELOCAL(a)) return 5; + return 14; +} + +fn prefixMatch(s: [16]u8, d: [16]u8) u8 { + // TODO: This FIXME inherited from porting from musl libc. + // I don't want this to go into zig std lib 1.0.0. + + // FIXME: The common prefix length should be limited to no greater + // than the nominal length of the prefix portion of the source + // address. However the definition of the source prefix length is + // not clear and thus this limiting is not yet implemented. + var i: u8 = 0; + while (i < 128 and ((s[i / 8] ^ d[i / 8]) & (@as(u8, 128) >> @intCast(u3, i % 8))) == 0) : (i += 1) {} + return i; +} + +fn labelOf(a: [16]u8) u8 { + return policyOf(a).label; +} + +fn IN6_IS_ADDR_MULTICAST(a: [16]u8) bool { + return a[0] == 0xff; +} + +fn IN6_IS_ADDR_LINKLOCAL(a: [16]u8) bool { + return a[0] == 0xfe and (a[1] & 0xc0) == 0x80; +} + +fn IN6_IS_ADDR_LOOPBACK(a: [16]u8) bool { + return a[0] == 0 and a[1] == 0 and + a[2] == 0 and + a[12] == 0 and a[13] == 0 and + a[14] == 0 and a[15] == 1; +} + +fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool { + return a[0] == 0xfe and (a[1] & 0xc0) == 0xc0; +} + +// Parameters `b` and `a` swapped to make this descending. +fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool { + return a.sortkey < b.sortkey; +} + +fn linuxLookupNameFromNull( + addrs: *std.ArrayList(LookupAddr), + family: os.sa_family_t, + flags: u32, + port: u16, +) !void { + if ((flags & std.c.AI_PASSIVE) != 0) { + if (family != os.AF_INET6) { + (try addrs.addOne()).* = LookupAddr{ + .addr = Address.initIp4([1]u8{0} ** 4, port), + }; + } + if (family != os.AF_INET) { + (try addrs.addOne()).* = LookupAddr{ + .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0), + }; + } + } else { + if (family != os.AF_INET6) { + (try addrs.addOne()).* = LookupAddr{ + .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port), + }; + } + if (family != os.AF_INET) { + (try addrs.addOne()).* = LookupAddr{ + .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), + }; + } + } +} + +fn linuxLookupNameFromHosts( + addrs: *std.ArrayList(LookupAddr), + canon: *std.Buffer, + name: []const u8, + family: os.sa_family_t, + port: u16, +) !void { + const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.AccessDenied, + => return, + else => |e| return e, + }; + defer file.close(); + + const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; + var line_buf: [512]u8 = undefined; + while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { + error.StreamTooLong => blk: { + // Skip to the delimiter in the stream, to fix parsing + try stream.skipUntilDelimiterOrEof('\n'); + // Use the truncated line. A truncated comment or hostname will be handled correctly. + break :blk line_buf[0..]; + }, + else => |e| return e, + }) |line| { + const no_comment_line = mem.separate(line, "#").next().?; + + var line_it = mem.tokenize(no_comment_line, " \t"); + const ip_text = line_it.next() orelse continue; + var first_name_text: ?[]const u8 = null; + while (line_it.next()) |name_text| { + if (first_name_text == null) first_name_text = name_text; + if (mem.eql(u8, name_text, name)) { + break; + } + } else continue; + + const addr = Address.parseExpectingFamily(ip_text, family, port) catch |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + error.InvalidIPAddressFormat, + error.InvalidIpv4Mapping, + => continue, + }; + try addrs.append(LookupAddr{ .addr = addr }); + + // first name is canonical name + const name_text = first_name_text.?; + if (isValidHostName(name_text)) { + try canon.replaceContents(name_text); + } + } +} + +pub fn isValidHostName(hostname: []const u8) bool { + if (hostname.len >= 254) return false; + if (!std.unicode.utf8ValidateSlice(hostname)) return false; + for (hostname) |byte| { + if (byte >= 0x80 or byte == '.' or byte == '-' or std.ascii.isAlNum(byte)) { + continue; + } + return false; + } + return true; +} + +fn linuxLookupNameFromDnsSearch( + addrs: *std.ArrayList(LookupAddr), + canon: *std.Buffer, + name: []const u8, + family: os.sa_family_t, + port: u16, +) !void { + var rc: ResolvConf = undefined; + try getResolvConf(addrs.allocator, &rc); + defer rc.deinit(); + + // Count dots, suppress search when >=ndots or name ends in + // a dot, which is an explicit request for global scope. + var dots: usize = 0; + for (name) |byte| { + if (byte == '.') dots += 1; } - return std.fs.File.openHandle(sockfd); + const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, ".")) + &[_]u8{} + else + rc.search.toSliceConst(); + + var canon_name = name; + + // Strip final dot for canon, fail if multiple trailing dots. + if (mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1; + if (mem.endsWith(u8, canon_name, ".")) return error.UnknownHostName; + + // Name with search domain appended is setup in canon[]. This both + // provides the desired default canonical name (if the requested + // name is not a CNAME record) and serves as a buffer for passing + // the full requested name to name_from_dns. + try canon.resize(canon_name.len); + mem.copy(u8, canon.toSlice(), canon_name); + try canon.appendByte('.'); + + var tok_it = mem.tokenize(search, " \t"); + while (tok_it.next()) |tok| { + canon.shrink(canon_name.len + 1); + try canon.append(tok); + try linuxLookupNameFromDns(addrs, canon, canon.toSliceConst(), family, rc, port); + if (addrs.len != 0) return; + } + + canon.shrink(canon_name.len); + return linuxLookupNameFromDns(addrs, canon, name, family, rc, port); } + +const dpc_ctx = struct { + addrs: *std.ArrayList(LookupAddr), + canon: *std.Buffer, + port: u16, +}; + +fn linuxLookupNameFromDns( + addrs: *std.ArrayList(LookupAddr), + canon: *std.Buffer, + name: []const u8, + family: os.sa_family_t, + rc: ResolvConf, + port: u16, +) !void { + var ctx = dpc_ctx{ + .addrs = addrs, + .canon = canon, + .port = port, + }; + const AfRr = struct { + af: os.sa_family_t, + rr: u8, + }; + const afrrs = [_]AfRr{ + AfRr{ .af = os.AF_INET6, .rr = os.RR_A }, + AfRr{ .af = os.AF_INET, .rr = os.RR_AAAA }, + }; + var qbuf: [2][280]u8 = undefined; + var abuf: [2][512]u8 = undefined; + var qp: [2][]const u8 = undefined; + const apbuf = [2][]u8{ &abuf[0], &abuf[1] }; + var nq: usize = 0; + + for (afrrs) |afrr| { + if (family != afrr.af) { + const len = os.res_mkquery(0, name, 1, afrr.rr, &[_]u8{}, null, &qbuf[nq]); + qp[nq] = qbuf[nq][0..len]; + nq += 1; + } + } + + var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] }; + try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); + + var i: usize = 0; + while (i < nq) : (i += 1) { + dnsParse(ap[i], ctx, dnsParseCallback) catch {}; + } + + if (addrs.len != 0) return; + if (ap[0].len < 4 or (ap[0][3] & 15) == 2) return error.TemporaryNameServerFailure; + if ((ap[0][3] & 15) == 0) return error.UnknownHostName; + if ((ap[0][3] & 15) == 3) return; + return error.NameServerFailure; +} + +const ResolvConf = struct { + attempts: u32, + ndots: u32, + timeout: u32, + search: std.Buffer, + ns: std.ArrayList(LookupAddr), + + fn deinit(rc: *ResolvConf) void { + rc.ns.deinit(); + rc.search.deinit(); + rc.* = undefined; + } +}; + +/// Ignores lines longer than 512 bytes. +/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 +fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { + rc.* = ResolvConf{ + .ns = std.ArrayList(LookupAddr).init(allocator), + .search = std.Buffer.initNull(allocator), + .ndots = 1, + .timeout = 5, + .attempts = 2, + }; + errdefer rc.deinit(); + + const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.AccessDenied, + => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53), + else => |e| return e, + }; + defer file.close(); + + const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; + var line_buf: [512]u8 = undefined; + while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { + error.StreamTooLong => blk: { + // Skip to the delimiter in the stream, to fix parsing + try stream.skipUntilDelimiterOrEof('\n'); + // Give an empty line to the while loop, which will be skipped. + break :blk line_buf[0..0]; + }, + else => |e| return e, + }) |line| { + const no_comment_line = mem.separate(line, "#").next().?; + var line_it = mem.tokenize(no_comment_line, " \t"); + + const token = line_it.next() orelse continue; + if (mem.eql(u8, token, "options")) { + while (line_it.next()) |sub_tok| { + var colon_it = mem.separate(sub_tok, ":"); + const name = colon_it.next().?; + const value_txt = colon_it.next() orelse continue; + const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { + error.Overflow => 255, + error.InvalidCharacter => continue, + }; + if (mem.eql(u8, name, "ndots")) { + rc.ndots = std.math.min(value, 15); + } else if (mem.eql(u8, name, "attempts")) { + rc.attempts = std.math.min(value, 10); + } else if (mem.eql(u8, name, "timeout")) { + rc.timeout = std.math.min(value, 60); + } + } + } else if (mem.eql(u8, token, "nameserver")) { + const ip_txt = line_it.next() orelse continue; + try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53); + } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) { + try rc.search.replaceContents(line_it.rest()); + } + } + + if (rc.ns.len == 0) { + return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53); + } +} + +fn linuxLookupNameFromNumericUnspec( + addrs: *std.ArrayList(LookupAddr), + name: []const u8, + port: u16, +) !void { + const addr = try Address.parseIp(name, port); + (try addrs.addOne()).* = LookupAddr{ .addr = addr }; +} + +fn resMSendRc( + queries: []const []const u8, + answers: [][]u8, + answer_bufs: []const []u8, + rc: ResolvConf, +) !void { + const timeout = 1000 * rc.timeout; + const attempts = rc.attempts; + + var sl: os.socklen_t = @sizeOf(os.sockaddr_in); + var family: os.sa_family_t = os.AF_INET; + + var ns_list = std.ArrayList(Address).init(rc.ns.allocator); + defer ns_list.deinit(); + + try ns_list.resize(rc.ns.len); + const ns = ns_list.toSlice(); + + for (rc.ns.toSliceConst()) |iplit, i| { + ns[i] = iplit.addr; + assert(ns[i].getPort() == 53); + if (iplit.addr.any.family != os.AF_INET) { + sl = @sizeOf(os.sockaddr_in6); + family = os.AF_INET6; + } + } + + // Get local address and open/bind a socket + var sa: Address = undefined; + @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address)); + sa.any.family = family; + const flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK; + const fd = os.socket(family, flags, 0) catch |err| switch (err) { + error.AddressFamilyNotSupported => blk: { + // Handle case where system lacks IPv6 support + if (family == os.AF_INET6) { + family = os.AF_INET; + break :blk try os.socket(os.AF_INET, flags, 0); + } + return err; + }, + else => |e| return e, + }; + defer os.close(fd); + try os.bind(fd, &sa.any, sl); + + // Past this point, there are no errors. Each individual query will + // yield either no reply (indicated by zero length) or an answer + // packet which is up to the caller to interpret. + + // Convert any IPv4 addresses in a mixed environment to v4-mapped + // TODO + //if (family == AF_INET6) { + // setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0); + // for (i=0; i= retry_interval) { + // Query all configured nameservers in parallel + var i: usize = 0; + while (i < queries.len) : (i += 1) { + if (answers[i].len == 0) { + var j: usize = 0; + while (j < ns.len) : (j += 1) { + _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + } + } + } + t1 = t2; + servfail_retry = 2 * queries.len; + } + + // Wait for a response, or until time to retry + const clamped_timeout = std.math.min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2); + const nevents = os.poll(&pfd, clamped_timeout) catch 0; + if (nevents == 0) continue; + + while (true) { + var sl_copy = sl; + const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; + + // Ignore non-identifiable packets + if (rlen < 4) continue; + + // Ignore replies from addresses we didn't send to + var j: usize = 0; + while (j < ns.len and !ns[j].eql(sa)) : (j += 1) {} + if (j == ns.len) continue; + + // Find which query this answer goes with, if any + var i: usize = next; + while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or + answer_bufs[next][1] != queries[i][1])) : (i += 1) + {} + + if (i == queries.len) continue; + if (answers[i].len != 0) continue; + + // Only accept positive or negative responses; + // retry immediately on server failure, and ignore + // all other codes such as refusal. + switch (answer_bufs[next][3] & 15) { + 0, 3 => {}, + 2 => if (servfail_retry != 0) { + servfail_retry -= 1; + _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + }, + else => continue, + } + + // Store answer in the right slot, or update next + // available temp slot if it's already in place. + answers[i].len = rlen; + if (i == next) { + while (next < queries.len and answers[next].len != 0) : (next += 1) {} + } else { + mem.copy(u8, answer_bufs[i], answer_bufs[next][0..rlen]); + } + + if (next == queries.len) break :outer; + } + } +} + +fn dnsParse( + r: []const u8, + ctx: var, + comptime callback: var, +) !void { + // This implementation is ported from musl libc. + // A more idiomatic "ziggy" implementation would be welcome. + if (r.len < 12) return error.InvalidDnsPacket; + if ((r[3] & 15) != 0) return; + var p = r.ptr + 12; + var qdcount = r[4] * @as(usize, 256) + r[5]; + var ancount = r[6] * @as(usize, 256) + r[7]; + if (qdcount + ancount > 64) return error.InvalidDnsPacket; + while (qdcount != 0) { + qdcount -= 1; + while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1; + if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6) + return error.InvalidDnsPacket; + p += @as(usize, 5) + @boolToInt(p[0] != 0); + } + while (ancount != 0) { + ancount -= 1; + while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1; + if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6) + return error.InvalidDnsPacket; + p += @as(usize, 1) + @boolToInt(p[0] != 0); + const len = p[8] * @as(usize, 256) + p[9]; + if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket; + try callback(ctx, p[1], p[10 .. 10 + len], r); + p += 10 + len; + } +} + +fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void { + switch (rr) { + os.RR_A => { + if (data.len != 4) return error.InvalidDnsARecord; + const new_addr = try ctx.addrs.addOne(); + new_addr.* = LookupAddr{ + // TODO slice [0..4] to make this *[4]u8 without @ptrCast + .addr = Address.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), + }; + }, + os.RR_AAAA => { + if (data.len != 16) return error.InvalidDnsAAAARecord; + const new_addr = try ctx.addrs.addOne(); + new_addr.* = LookupAddr{ + // TODO slice [0..16] to make this *[16]u8 without @ptrCast + .addr = Address.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), + }; + }, + os.RR_CNAME => { + var tmp: [256]u8 = undefined; + // Returns len of compressed name. strlen to get canon name. + _ = try os.dn_expand(packet, data, &tmp); + const canon_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &tmp)); + if (isValidHostName(canon_name)) { + try ctx.canon.replaceContents(canon_name); + } + }, + else => return, + } +} + +pub const StreamServer = struct { + /// Copied from `Options` on `init`. + kernel_backlog: u32, + reuse_address: bool, + + /// `undefined` until `listen` returns successfully. + listen_address: Address, + + sockfd: ?os.fd_t, + + pub const Options = struct { + /// How many connections the kernel will accept on the application's behalf. + /// If more than this many connections pool in the kernel, clients will start + /// seeing "Connection refused". + kernel_backlog: u32 = 128, + + /// Enable SO_REUSEADDR on the socket. + reuse_address: bool = false, + }; + + /// After this call succeeds, resources have been acquired and must + /// be released with `deinit`. + pub fn init(options: Options) StreamServer { + return StreamServer{ + .sockfd = null, + .kernel_backlog = options.kernel_backlog, + .reuse_address = options.reuse_address, + .listen_address = undefined, + }; + } + + /// Release all resources. The `StreamServer` memory becomes `undefined`. + pub fn deinit(self: *StreamServer) void { + self.close(); + self.* = undefined; + } + + pub fn listen(self: *StreamServer, address: Address) !void { + const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; + const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; + const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP; + + const sockfd = try os.socket(address.any.family, sock_flags, proto); + self.sockfd = sockfd; + errdefer { + os.close(sockfd); + self.sockfd = null; + } + + if (self.reuse_address) { + try os.setsockopt( + self.sockfd.?, + os.SOL_SOCKET, + os.SO_REUSEADDR, + &mem.toBytes(@as(c_int, 1)), + ); + } + + var socklen = address.getOsSockLen(); + try os.bind(sockfd, &address.any, socklen); + try os.listen(sockfd, self.kernel_backlog); + try os.getsockname(sockfd, &self.listen_address.any, &socklen); + } + + /// Stop listening. It is still necessary to call `deinit` after stopping listening. + /// Calling `deinit` will automatically call `close`. It is safe to call `close` when + /// not listening. + pub fn close(self: *StreamServer) void { + if (self.sockfd) |fd| { + os.close(fd); + self.sockfd = null; + self.listen_address = undefined; + } + } + + pub const AcceptError = error{ + ConnectionAborted, + + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// Not enough free memory. This often means that the memory allocation is limited + /// by the socket buffer limits, not by the system memory. + SystemResources, + + ProtocolFailure, + + /// Firewall rules forbid connection. + BlockedByFirewall, + } || os.UnexpectedError; + + pub const Connection = struct { + file: fs.File, + address: Address, + }; + + /// If this function succeeds, the returned `Connection` is a caller-managed resource. + pub fn accept(self: *StreamServer) AcceptError!Connection { + const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; + const accept_flags = nonblock | os.SOCK_CLOEXEC; + var accepted_addr: Address = undefined; + var adr_len: os.socklen_t = @sizeOf(Address); + if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { + return Connection{ + .file = fs.File.openHandle(fd), + .address = accepted_addr, + }; + } else |err| switch (err) { + // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error + // is handled by os.accept4. + error.WouldBlock => unreachable, + else => |e| return e, + } + } +}; diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig new file mode 100644 index 000000000..2dd11b391 --- /dev/null +++ b/lib/std/net/test.zig @@ -0,0 +1,122 @@ +const std = @import("../std.zig"); +const net = std.net; +const mem = std.mem; +const testing = std.testing; + +test "parse and render IPv6 addresses" { + var buffer: [100]u8 = undefined; + const ips = [_][]const u8{ + "FF01:0:0:0:0:0:0:FB", + "FF01::Fb", + "::1", + "::", + "2001:db8::", + "::1234:5678", + "2001:db8::1234:5678", + "FF01::FB%1234", + "::ffff:123.5.123.5", + }; + const printed = [_][]const u8{ + "ff01::fb", + "ff01::fb", + "::1", + "::", + "2001:db8::", + "::1234:5678", + "2001:db8::1234:5678", + "ff01::fb", + "::ffff:123.5.123.5", + }; + for (ips) |ip, i| { + var addr = net.Address.parseIp6(ip, 0) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); + } + + testing.expectError(error.InvalidCharacter, net.Address.parseIp6(":::", 0)); + testing.expectError(error.Overflow, net.Address.parseIp6("FF001::FB", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp6("FF01::Fb:zig", 0)); + testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); + testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); + testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); +} + +test "parse and render IPv4 addresses" { + var buffer: [18]u8 = undefined; + for ([_][]const u8{ + "0.0.0.0", + "255.255.255.255", + "1.2.3.4", + "123.255.0.91", + "127.0.0.1", + }) |ip| { + var addr = net.Address.parseIp4(ip, 0) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); + } + + testing.expectError(error.Overflow, net.Address.parseIp4("256.0.0.1", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp4("x.0.0.1", 0)); + testing.expectError(error.InvalidEnd, net.Address.parseIp4("127.0.0.1.1", 0)); + testing.expectError(error.Incomplete, net.Address.parseIp4("127.0.0.", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp4("100..0.1", 0)); +} + +test "resolve DNS" { + if (std.builtin.os == .windows) { + // DNS resolution not implemented on Windows yet. + return error.SkipZigTest; + } + var buf: [1000 * 10]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + + const address_list = net.getAddressList(a, "example.com", 80) catch |err| switch (err) { + // The tests are required to work even when there is no Internet connection, + // so some of these errors we must accept and skip the test. + error.UnknownHostName => return error.SkipZigTest, + error.TemporaryNameServerFailure => return error.SkipZigTest, + else => return err, + }; + address_list.deinit(); +} + +test "listen on a port, send bytes, receive bytes" { + if (std.builtin.os != .linux) { + // TODO build abstractions for other operating systems + return error.SkipZigTest; + } + if (std.io.mode != .evented) { + // TODO add ability to run tests in non-blocking I/O mode + return error.SkipZigTest; + } + + // TODO doing this at comptime crashed the compiler + const localhost = net.Address.parseIp("127.0.0.1", 0); + + var server = net.StreamServer.init(net.StreamServer.Options{}); + defer server.deinit(); + try server.listen(localhost); + + var server_frame = async testServer(&server); + var client_frame = async testClient(server.listen_address); + + try await server_frame; + try await client_frame; +} + +fn testClient(addr: net.Address) anyerror!void { + const socket_file = try net.tcpConnectToAddress(addr); + defer socket_file.close(); + + var buf: [100]u8 = undefined; + const len = try socket_file.read(&buf); + const msg = buf[0..len]; + testing.expect(mem.eql(u8, msg, "hello from server\n")); +} + +fn testServer(server: *net.StreamServer) anyerror!void { + var client = try server.accept(); + + const stream = &client.file.outStream().stream; + try stream.print("hello from server\n", .{}); +} diff --git a/lib/std/os.zig b/lib/std/os.zig index c520e1d63..c39891c6b 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -14,6 +14,7 @@ // Note: The Zig standard library does not support POSIX thread cancellation, and // in general EINTR is handled by trying again. +const root = @import("root"); const std = @import("std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; @@ -23,40 +24,58 @@ const elf = std.elf; const dl = @import("dynamic_library.zig"); const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; +pub const darwin = @import("os/darwin.zig"); +pub const dragonfly = @import("os/dragonfly.zig"); +pub const freebsd = @import("os/freebsd.zig"); +pub const netbsd = @import("os/netbsd.zig"); +pub const linux = @import("os/linux.zig"); +pub const uefi = @import("os/uefi.zig"); +pub const wasi = @import("os/wasi.zig"); +pub const windows = @import("os/windows.zig"); + comptime { assert(@import("std") == std); // std lib tests require --override-lib-dir } -pub const darwin = @import("os/darwin.zig"); -pub const freebsd = @import("os/freebsd.zig"); -pub const linux = @import("os/linux.zig"); -pub const netbsd = @import("os/netbsd.zig"); -pub const uefi = @import("os/uefi.zig"); -pub const wasi = @import("os/wasi.zig"); -pub const windows = @import("os/windows.zig"); -pub const zen = @import("os/zen.zig"); +test "" { + _ = darwin; + _ = freebsd; + _ = linux; + _ = netbsd; + _ = uefi; + _ = wasi; + _ = windows; -/// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface. -pub const system = if (builtin.link_libc) std.c else switch (builtin.os) { + _ = @import("os/test.zig"); +} + +/// Applications can override the `system` API layer in their root source file. +/// Otherwise, when linking libc, this is the C API. +/// When not linking libc, it is the OS-specific system interface. +pub const system = if (@hasDecl(root, "os") and root.os != @This()) + root.os.system +else if (builtin.link_libc) + std.c +else switch (builtin.os) { .macosx, .ios, .watchos, .tvos => darwin, .freebsd => freebsd, .linux => linux, .netbsd => netbsd, + .dragonfly => dragonfly, .wasi => wasi, .windows => windows, - .zen => zen, else => struct {}, }; pub usingnamespace @import("os/bits.zig"); /// See also `getenv`. Populated by startup code before main(). -pub var environ: [][*]u8 = undefined; +pub var environ: [][*:0]u8 = undefined; /// Populated by startup code before main(). /// Not available on Windows. See `std.process.args` /// for obtaining the process arguments. -pub var argv: [][*]u8 = undefined; +pub var argv: [][*:0]u8 = undefined; /// To obtain errno, call this function with the return value of the /// system function call. For some systems this will obtain the value directly @@ -72,13 +91,13 @@ pub const errno = system.getErrno; /// must call `fsync` before `close`. /// Note: The Zig standard library does not support POSIX thread cancellation. pub fn close(fd: fd_t) void { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.CloseHandle(fd); } - if (wasi.is_the_target) { + if (builtin.os == .wasi) { _ = wasi.fd_close(fd); } - if (darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { // This avoids the EINTR problem. switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { EBADF => unreachable, // Always a race condition. @@ -100,12 +119,12 @@ pub const GetRandomError = OpenError; /// appropriate OS-specific library call. Otherwise it uses the zig standard /// library implementation. pub fn getrandom(buffer: []u8) GetRandomError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.RtlGenRandom(buffer); } - if (linux.is_the_target or freebsd.is_the_target) { + if (builtin.os == .linux or builtin.os == .freebsd) { var buf = buffer; - const use_c = !linux.is_the_target or + const use_c = builtin.os != .linux or std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; while (buf.len != 0) { @@ -132,7 +151,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { } return; } - if (wasi.is_the_target) { + if (builtin.os == .wasi) { switch (wasi.random_get(buffer.ptr, buffer.len)) { 0 => return, else => |err| return unexpectedErrno(err), @@ -142,7 +161,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { } fn getRandomBytesDevURandom(buf: []u8) !void { - const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); + const fd = try openC("/dev/urandom", O_RDONLY | O_CLOEXEC, 0); defer close(fd); const st = try fstat(fd); @@ -162,28 +181,32 @@ pub fn abort() noreturn { // MSVCRT abort() sometimes opens a popup window which is undesirable, so // even when linking libc on Windows we use our own abort implementation. // See https://github.com/ziglang/zig/issues/2071 for more details. - if (windows.is_the_target) { + if (builtin.os == .windows) { if (builtin.mode == .Debug) { @breakpoint(); } windows.kernel32.ExitProcess(3); } - if (builtin.link_libc) { - system.abort(); + if (!builtin.link_libc and builtin.os == .linux) { + raise(SIGABRT) catch {}; + + // TODO the rest of the implementation of abort() from musl libc here + + raise(SIGKILL) catch {}; + exit(127); } if (builtin.os == .uefi) { exit(0); // TODO choose appropriate exit code } + if (builtin.os == .wasi) { + @breakpoint(); + exit(1); + } - raise(SIGABRT) catch {}; - - // TODO the rest of the implementation of abort() from musl libc here - - raise(SIGKILL) catch {}; - exit(127); + system.abort(); } -pub const RaiseError = error{Unexpected}; +pub const RaiseError = UnexpectedError; pub fn raise(sig: u8) RaiseError!void { if (builtin.link_libc) { @@ -193,19 +216,17 @@ pub fn raise(sig: u8) RaiseError!void { } } - if (wasi.is_the_target) { - switch (wasi.proc_raise(SIGABRT)) { - 0 => return, - else => |err| return unexpectedErrno(err), - } - } - - if (linux.is_the_target) { + if (builtin.os == .linux) { var set: linux.sigset_t = undefined; - linux.blockAppSignals(&set); - const tid = linux.syscall0(linux.SYS_gettid); - const rc = linux.syscall2(linux.SYS_tkill, tid, sig); - linux.restoreSignals(&set); + // block application signals + _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set); + + const tid = linux.gettid(); + const rc = linux.tkill(tid, sig); + + // restore signal mask + _ = linux.sigprocmask(SIG_SETMASK, &set, null); + switch (errno(rc)) { 0 => return, else => |err| return unexpectedErrno(err), @@ -215,10 +236,7 @@ pub fn raise(sig: u8) RaiseError!void { @compileError("std.os.raise unimplemented for this target"); } -pub const KillError = error{ - PermissionDenied, - Unexpected, -}; +pub const KillError = error{PermissionDenied} || UnexpectedError; pub fn kill(pid: pid_t, sig: u8) KillError!void { switch (errno(system.kill(pid, sig))) { @@ -235,16 +253,16 @@ pub fn exit(status: u8) noreturn { if (builtin.link_libc) { system.exit(status); } - if (windows.is_the_target) { + if (builtin.os == .windows) { windows.kernel32.ExitProcess(status); } - if (wasi.is_the_target) { + if (builtin.os == .wasi) { wasi.proc_exit(status); } - if (linux.is_the_target and !builtin.single_threaded) { + if (builtin.os == .linux and !builtin.single_threaded) { linux.exit_group(status); } - if (uefi.is_the_target) { + if (builtin.os == .uefi) { // exit() is only avaliable if exitBootServices() has not been called yet. // This call to exit should not fail, so we don't care about its return value. if (uefi.system_table.boot_services) |bs| { @@ -262,24 +280,23 @@ pub const ReadError = error{ IsDir, OperationAborted, BrokenPipe, + ConnectionResetByPeer, /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, - - Unexpected, -}; +} || UnexpectedError; /// Returns the number of bytes that were read, which can be less than /// buf.len. If 0 bytes were read, that means EOF. /// If the application has a global event loop enabled, EAGAIN is handled /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. pub fn read(fd: fd_t, buf: []u8) ReadError!usize { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.ReadFile(fd, buf); } - if (wasi.is_the_target and !builtin.link_libc) { + if (builtin.os == .wasi and !builtin.link_libc) { const iovs = [1]iovec{iovec{ .iov_base = buf.ptr, .iov_len = buf.len, @@ -300,7 +317,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { EINVAL => unreachable, EFAULT => unreachable, EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(fd) catch return error.WouldBlock; + loop.waitUntilFdReadable(fd); continue; } else { return error.WouldBlock; @@ -310,6 +327,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { EISDIR => return error.IsDir, ENOBUFS => return error.SystemResources, ENOMEM => return error.SystemResources, + ECONNRESET => return error.ConnectionResetByPeer, else => |err| return unexpectedErrno(err), } } @@ -317,9 +335,38 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { } /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. -/// This function is for blocking file descriptors only. +/// If the application has a global event loop enabled, EAGAIN is handled +/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. +pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { + while (true) { + // TODO handle the case when iov_len is too large and get rid of this @intCast + const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len)); + switch (errno(rc)) { + 0 => return @bitCast(usize, rc), + EINTR => continue, + EINVAL => unreachable, + EFAULT => unreachable, + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(fd); + continue; + } else { + return error.WouldBlock; + }, + EBADF => unreachable, // always a race condition + EIO => return error.InputOutput, + EISDIR => return error.IsDir, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } + } +} + +/// Number of bytes read is returned. Upon reading end-of-file, zero is returned. +/// If the application has a global event loop enabled, EAGAIN is handled +/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { - if (darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { // Darwin does not have preadv but it does have pread. var off: usize = 0; var iov_i: usize = 0; @@ -347,7 +394,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { EINVAL => unreachable, EFAULT => unreachable, ESPIPE => unreachable, // fd is not seekable - EAGAIN => unreachable, // This function is for blocking reads. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(fd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // always a race condition EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -365,7 +417,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => unreachable, // This function is for blocking reads. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(fd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // always a race condition EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -385,17 +442,23 @@ pub const WriteError = error{ BrokenPipe, SystemResources, OperationAborted, - Unexpected, -}; + + /// This error occurs when no global event loop is configured, + /// and reading from the file descriptor would block. + WouldBlock, +} || UnexpectedError; /// Write to a file descriptor. Keeps trying if it gets interrupted. -/// This function is for blocking file descriptors only. +/// If the application has a global event loop enabled, EAGAIN is handled +/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. +/// TODO evented I/O integration is disabled until +/// https://github.com/ziglang/zig/issues/3557 is solved. pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.WriteFile(fd, bytes); } - if (wasi.is_the_target and !builtin.link_libc) { + if (builtin.os == .wasi and !builtin.link_libc) { const ciovs = [1]iovec_const{iovec_const{ .iov_base = bytes.ptr, .iov_len = bytes.len, @@ -415,7 +478,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { var index: usize = 0; while (index < bytes.len) { - const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); + const amt_to_write = math.min(bytes.len - index, @as(usize, max_bytes_len)); const rc = system.write(fd, bytes.ptr + index, amt_to_write); switch (errno(rc)) { 0 => { @@ -425,7 +488,14 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => unreachable, // This function is for blocking writes. + // TODO https://github.com/ziglang/zig/issues/3557 + EAGAIN => return error.WouldBlock, + //EAGAIN => if (std.event.Loop.instance) |loop| { + // loop.waitUntilFdWritable(fd); + // continue; + //} else { + // return error.WouldBlock; + //}, EBADF => unreachable, // Always a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -439,9 +509,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { } } -/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted. -/// This function is for blocking file descriptors only. For non-blocking, see -/// `writevAsync`. +/// Write multiple buffers to a file descriptor. +/// If the application has a global event loop enabled, EAGAIN is handled +/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { while (true) { // TODO handle the case when iov_len is too large and get rid of this @intCast @@ -451,7 +521,12 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => unreachable, // This function is for blocking writes. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdWritable(fd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // Always a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -467,10 +542,8 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { /// Write multiple buffers to a file descriptor, with a position offset. /// Keeps trying if it gets interrupted. -/// This function is for blocking file descriptors only. For non-blocking, see -/// `pwritevAsync`. pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { - if (darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { // Darwin does not have pwritev but it does have pwrite. var off: usize = 0; var iov_i: usize = 0; @@ -497,7 +570,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void ESPIPE => unreachable, // `fd` is not seekable. EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => unreachable, // This function is for blocking writes. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdWritable(fd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // Always a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -519,7 +597,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => unreachable, // This function is for blocking writes. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdWritable(fd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // Always a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -535,21 +618,39 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void pub const OpenError = error{ AccessDenied, - FileTooBig, - IsDir, SymLinkLoop, ProcessFdQuotaExceeded, - NameTooLong, SystemFdQuotaExceeded, NoDevice, FileNotFound, + + /// The path exceeded `MAX_PATH_BYTES` bytes. + NameTooLong, + + /// Insufficient kernel memory was available, or + /// the named file is a FIFO and per-user hard limit on + /// memory allocation for pipes has been reached. SystemResources, + + /// The file is too large to be opened. This error is unreachable + /// for 64-bit targets, as well as when opening directories. + FileTooBig, + + /// The path refers to directory but the `O_DIRECTORY` flag was not provided. + IsDir, + + /// A new path cannot be created because the device has no room for the new file. + /// This error is only reachable when the `O_CREAT` flag is provided. NoSpaceLeft, + + /// A component used as a directory in the path was not, in fact, a directory, or + /// `O_DIRECTORY` was specified and the path was not a directory. NotDir, + + /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. PathAlreadyExists, DeviceBusy, - Unexpected, -}; +} || UnexpectedError; /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `openC`. @@ -560,8 +661,7 @@ pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `open`. -/// TODO https://github.com/ziglang/zig/issues/265 -pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t { +pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t { while (true) { const rc = system.open(file_path, flags, perm); switch (errno(rc)) { @@ -602,7 +702,7 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: usize) Open /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openat`. -pub fn openatC(dir_fd: fd_t, file_path: [*]const u8, flags: u32, mode: usize) OpenError!fd_t { +pub fn openatC(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: usize) OpenError!fd_t { while (true) { const rc = system.openat(dir_fd, file_path, flags, mode); switch (errno(rc)) { @@ -638,104 +738,13 @@ pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { 0 => return, EBUSY, EINTR => continue, EMFILE => return error.ProcessFdQuotaExceeded, - EINVAL => unreachable, + EINVAL => unreachable, // invalid parameters passed to dup2 + EBADF => unreachable, // always a race condition else => |err| return unexpectedErrno(err), } } } -/// This function must allocate memory to add a null terminating bytes on path and each arg. -/// It must also convert to KEY=VALUE\0 format for environment variables, and include null -/// pointers after the args and after the environment variables. -/// `argv[0]` is the executable path. -/// This function also uses the PATH environment variable to get the full path to the executable. -/// TODO provide execveC which does not take an allocator -pub fn execve(allocator: *mem.Allocator, argv_slice: []const []const u8, env_map: *const std.BufMap) !void { - const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1); - mem.set(?[*]u8, argv_buf, null); - defer { - for (argv_buf) |arg| { - const arg_buf = if (arg) |ptr| mem.toSlice(u8, ptr) else break; - allocator.free(arg_buf); - } - allocator.free(argv_buf); - } - for (argv_slice) |arg, i| { - const arg_buf = try allocator.alloc(u8, arg.len + 1); - @memcpy(arg_buf.ptr, arg.ptr, arg.len); - arg_buf[arg.len] = 0; - - argv_buf[i] = arg_buf.ptr; - } - argv_buf[argv_slice.len] = null; - - const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); - defer freeNullDelimitedEnvMap(allocator, envp_buf); - - const exe_path = argv_slice[0]; - if (mem.indexOfScalar(u8, exe_path, '/') != null) { - return execveErrnoToErr(errno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); - } - - const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; - // PATH.len because it is >= the largest search_path - // +1 for the / to join the search path and exe_path - // +1 for the null terminating byte - const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); - defer allocator.free(path_buf); - var it = mem.tokenize(PATH, ":"); - var seen_eacces = false; - var err: usize = undefined; - while (it.next()) |search_path| { - mem.copy(u8, path_buf, search_path); - path_buf[search_path.len] = '/'; - mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); - path_buf[search_path.len + exe_path.len + 1] = 0; - err = errno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); - assert(err > 0); - if (err == EACCES) { - seen_eacces = true; - } else if (err != ENOENT) { - return execveErrnoToErr(err); - } - } - if (seen_eacces) { - err = EACCES; - } - return execveErrnoToErr(err); -} - -pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 { - const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); - mem.set(?[*]u8, envp_buf, null); - errdefer freeNullDelimitedEnvMap(allocator, envp_buf); - { - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| : (i += 1) { - const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); - env_buf[pair.key.len] = '='; - @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); - env_buf[env_buf.len - 1] = 0; - - envp_buf[i] = env_buf.ptr; - } - assert(i == envp_count); - } - assert(envp_buf[envp_count] == null); - return envp_buf; -} - -pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) void { - for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; - allocator.free(env_buf); - } - allocator.free(envp_buf); -} - pub const ExecveError = error{ SystemResources, AccessDenied, @@ -748,13 +757,14 @@ pub const ExecveError = error{ ProcessFdQuotaExceeded, SystemFdQuotaExceeded, NameTooLong, +} || UnexpectedError; - Unexpected, -}; - -fn execveErrnoToErr(err: usize) ExecveError { - assert(err > 0); - switch (err) { +/// Like `execve` except the parameters are null-terminated, +/// matching the syscall API on all targets. This removes the need for an allocator. +/// This function ignores PATH environment variable. See `execvpeC` for that. +pub fn execveC(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { + switch (errno(system.execve(path, child_argv, envp))) { + 0 => unreachable, EFAULT => unreachable, E2BIG => return error.SystemResources, EMFILE => return error.ProcessFdQuotaExceeded, @@ -771,10 +781,105 @@ fn execveErrnoToErr(err: usize) ExecveError { ENOENT => return error.FileNotFound, ENOTDIR => return error.NotDir, ETXTBSY => return error.FileBusy, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } +/// Like `execvpe` except the parameters are null-terminated, +/// matching the syscall API on all targets. This removes the need for an allocator. +/// This function also uses the PATH environment variable to get the full path to the executable. +/// If `file` is an absolute path, this is the same as `execveC`. +pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { + const file_slice = mem.toSliceConst(u8, file); + if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp); + + const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; + var path_buf: [MAX_PATH_BYTES]u8 = undefined; + var it = mem.tokenize(PATH, ":"); + var seen_eacces = false; + var err: ExecveError = undefined; + while (it.next()) |search_path| { + if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong; + mem.copy(u8, &path_buf, search_path); + path_buf[search_path.len] = '/'; + mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); + const path_len = search_path.len + file_slice.len + 1; + path_buf[path_len] = 0; + err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp); + switch (err) { + error.AccessDenied => seen_eacces = true, + error.FileNotFound, error.NotDir => {}, + else => |e| return e, + } + } + if (seen_eacces) return error.AccessDenied; + return err; +} + +/// This function must allocate memory to add a null terminating bytes on path and each arg. +/// It must also convert to KEY=VALUE\0 format for environment variables, and include null +/// pointers after the args and after the environment variables. +/// `argv_slice[0]` is the executable path. +/// This function also uses the PATH environment variable to get the full path to the executable. +pub fn execvpe( + allocator: *mem.Allocator, + argv_slice: []const []const u8, + env_map: *const std.BufMap, +) (ExecveError || error{OutOfMemory}) { + const argv_buf = try allocator.alloc(?[*:0]u8, argv_slice.len + 1); + mem.set(?[*:0]u8, argv_buf, null); + defer { + for (argv_buf) |arg| { + const arg_buf = if (arg) |ptr| mem.toSlice(u8, ptr) else break; + allocator.free(arg_buf); + } + allocator.free(argv_buf); + } + for (argv_slice) |arg, i| { + const arg_buf = try allocator.alloc(u8, arg.len + 1); + @memcpy(arg_buf.ptr, arg.ptr, arg.len); + arg_buf[arg.len] = 0; + argv_buf[i] = arg_buf[0..arg.len :0].ptr; + } + argv_buf[argv_slice.len] = null; + const argv_ptr = argv_buf[0..argv_slice.len :null].ptr; + + const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); + defer freeNullDelimitedEnvMap(allocator, envp_buf); + + return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); +} + +pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 { + const envp_count = env_map.count(); + const envp_buf = try allocator.alloc(?[*:0]u8, envp_count + 1); + mem.set(?[*:0]u8, envp_buf, null); + errdefer freeNullDelimitedEnvMap(allocator, envp_buf); + { + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); + @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); + env_buf[pair.key.len] = '='; + @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); + const len = env_buf.len - 1; + env_buf[len] = 0; + envp_buf[i] = env_buf[0..len :0].ptr; + } + assert(i == envp_count); + } + return envp_buf[0..envp_count :null]; +} + +pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { + for (envp_buf) |env| { + const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; + allocator.free(env_buf); + } + allocator.free(envp_buf); +} + /// Get an environment variable. /// See also `getenvC`. /// TODO make this go through libc when we have it @@ -796,8 +901,7 @@ pub fn getenv(key: []const u8) ?[]const u8 { /// Get an environment variable with a null-terminated name. /// See also `getenv`. -/// TODO https://github.com/ziglang/zig/issues/265 -pub fn getenvC(key: [*]const u8) ?[]const u8 { +pub fn getenvC(key: [*:0]const u8) ?[]const u8 { if (builtin.link_libc) { const value = system.getenv(key) orelse return null; return mem.toSliceConst(u8, value); @@ -808,12 +912,11 @@ pub fn getenvC(key: [*]const u8) ?[]const u8 { pub const GetCwdError = error{ NameTooLong, CurrentWorkingDirectoryUnlinked, - Unexpected, -}; +} || UnexpectedError; /// The result is a slice of out_buffer, indexed from 0. pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.GetCurrentDirectory(out_buffer); } @@ -823,7 +926,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len)); }; switch (err) { - 0 => return mem.toSlice(u8, out_buffer.ptr), + 0 => return mem.toSlice(u8, @ptrCast([*:0]u8, out_buffer.ptr)), EFAULT => unreachable, EINVAL => unreachable, ENOENT => return error.CurrentWorkingDirectoryUnlinked, @@ -846,8 +949,7 @@ pub const SymLinkError = error{ NameTooLong, InvalidUtf8, BadPathName, - Unexpected, -}; +} || UnexpectedError; /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent @@ -855,7 +957,7 @@ pub const SymLinkError = error{ /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkC` and `symlinkW`. pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const target_path_w = try windows.sliceToPrefixedFileW(target_path); const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); @@ -868,8 +970,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! /// This is the same as `symlink` except the parameters are null-terminated pointers. /// See also `symlink`. -pub fn symlinkC(target_path: [*]const u8, sym_link_path: [*]const u8) SymLinkError!void { - if (windows.is_the_target) { +pub fn symlinkC(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { + if (builtin.os == .windows) { const target_path_w = try windows.cStrToPrefixedFileW(target_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); @@ -900,7 +1002,7 @@ pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const return symlinkatC(target_path_c, newdirfd, sym_link_path_c); } -pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, sym_link_path: [*]const u8) SymLinkError!void { +pub fn symlinkatC(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { 0 => return, EFAULT => unreachable, @@ -932,7 +1034,6 @@ pub const UnlinkError = error{ NotDir, SystemResources, ReadOnlyFileSystem, - Unexpected, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -940,12 +1041,12 @@ pub const UnlinkError = error{ /// On Windows, file paths cannot contain these characters: /// '/', '*', '?', '"', '<', '>', '|' BadPathName, -}; +} || UnexpectedError; /// Delete a name and possibly the file it refers to. /// See also `unlinkC`. pub fn unlink(file_path: []const u8) UnlinkError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); return windows.DeleteFileW(&file_path_w); } else { @@ -955,8 +1056,8 @@ pub fn unlink(file_path: []const u8) UnlinkError!void { } /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. -pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { - if (windows.is_the_target) { +pub fn unlinkC(file_path: [*:0]const u8) UnlinkError!void { + if (builtin.os == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); return windows.DeleteFileW(&file_path_w); } @@ -979,6 +1080,115 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { } } +pub const UnlinkatError = UnlinkError || error{ + /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. + DirNotEmpty, +}; + +/// Delete a file name and possibly the file it refers to, based on an open directory handle. +/// Asserts that the path parameter has no null bytes. +pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { + if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); + if (builtin.os == .windows) { + const file_path_w = try windows.sliceToPrefixedFileW(file_path); + return unlinkatW(dirfd, &file_path_w, flags); + } + const file_path_c = try toPosixPath(file_path); + return unlinkatC(dirfd, &file_path_c, flags); +} + +/// Same as `unlinkat` but `file_path` is a null-terminated string. +pub fn unlinkatC(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { + if (builtin.os == .windows) { + const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); + return unlinkatW(dirfd, &file_path_w, flags); + } + switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { + 0 => return, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EFAULT => unreachable, + EIO => return error.FileSystem, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + EROFS => return error.ReadOnlyFileSystem, + ENOTEMPTY => return error.DirNotEmpty, + + EINVAL => unreachable, // invalid flags, or pathname has . as last component + EBADF => unreachable, // always a race condition + + else => |err| return unexpectedErrno(err), + } +} + +/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only. +pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatError!void { + const w = windows; + + const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0; + const create_options_flags = if (want_rmdir_behavior) + @as(w.ULONG, w.FILE_DELETE_ON_CLOSE) + else + @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE); + + const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2); + var nt_name = w.UNICODE_STRING{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + // The Windows API makes this mutable, but it will not mutate here. + .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), + }; + + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + // Windows does not recognize this, but it does work with empty string. + nt_name.Length = 0; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + // Can't remove the parent directory with an open handle. + return error.FileBusy; + } + + var attr = w.OBJECT_ATTRIBUTES{ + .Length = @sizeOf(w.OBJECT_ATTRIBUTES), + .RootDirectory = dirfd, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }; + var io: w.IO_STATUS_BLOCK = undefined; + var tmp_handle: w.HANDLE = undefined; + var rc = w.ntdll.NtCreateFile( + &tmp_handle, + w.SYNCHRONIZE | w.DELETE, + &attr, + &io, + null, + 0, + w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE, + w.FILE_OPEN, + create_options_flags, + null, + 0, + ); + if (rc == w.STATUS.SUCCESS) { + rc = w.ntdll.NtClose(tmp_handle); + } + switch (rc) { + w.STATUS.SUCCESS => return, + w.STATUS.OBJECT_NAME_INVALID => unreachable, + w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + w.STATUS.INVALID_PARAMETER => unreachable, + w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir, + else => return w.unexpectedStatus(rc), + } +} + const RenameError = error{ AccessDenied, FileBusy, @@ -996,12 +1206,11 @@ const RenameError = error{ RenameAcrossMountPoints, InvalidUtf8, BadPathName, - Unexpected, -}; +} || UnexpectedError; /// Change the name or location of a file. pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const old_path_w = try windows.sliceToPrefixedFileW(old_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); @@ -1013,8 +1222,8 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { } /// Same as `rename` except the parameters are null-terminated byte arrays. -pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { - if (windows.is_the_target) { +pub fn renameC(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void { + if (builtin.os == .windows) { const old_path_w = try windows.cStrToPrefixedFileW(old_path); const new_path_w = try windows.cStrToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); @@ -1045,7 +1254,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays. /// Assumes target is Windows. -pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void { +pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void { const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; return windows.MoveFileExW(old_path, new_path, flags); } @@ -1064,13 +1273,12 @@ pub const MakeDirError = error{ ReadOnlyFileSystem, InvalidUtf8, BadPathName, - Unexpected, -}; +} || UnexpectedError; /// Create a directory. /// `mode` is ignored on Windows. pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); return windows.CreateDirectoryW(&dir_path_w, null); } else { @@ -1080,8 +1288,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { } /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. -pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { - if (windows.is_the_target) { +pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { + if (builtin.os == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); return windows.CreateDirectoryW(&dir_path_w, null); } @@ -1116,12 +1324,11 @@ pub const DeleteDirError = error{ ReadOnlyFileSystem, InvalidUtf8, BadPathName, - Unexpected, -}; +} || UnexpectedError; /// Deletes an empty directory. pub fn rmdir(dir_path: []const u8) DeleteDirError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); return windows.RemoveDirectoryW(&dir_path_w); } else { @@ -1131,8 +1338,8 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void { } /// Same as `rmdir` except the parameter is null-terminated. -pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { - if (windows.is_the_target) { +pub fn rmdirC(dir_path: [*:0]const u8) DeleteDirError!void { + if (builtin.os == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); return windows.RemoveDirectoryW(&dir_path_w); } @@ -1163,13 +1370,12 @@ pub const ChangeCurDirError = error{ FileNotFound, SystemResources, NotDir, - Unexpected, -}; +} || UnexpectedError; /// Changes the current working directory of the calling process. /// `dir_path` is recommended to be a UTF-8 encoded string. pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); @compileError("TODO implement chdir for Windows"); } else { @@ -1179,8 +1385,8 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { } /// Same as `chdir` except the parameter is null-terminated. -pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void { - if (windows.is_the_target) { +pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void { + if (builtin.os == .windows) { const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); @compileError("TODO implement chdir for Windows"); } @@ -1206,13 +1412,12 @@ pub const ReadLinkError = error{ FileNotFound, SystemResources, NotDir, - Unexpected, -}; +} || UnexpectedError; /// Read value of a symbolic link. /// The return value is a slice of `out_buffer` from index 0. pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); @compileError("TODO implement readlink for Windows"); } else { @@ -1222,8 +1427,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { } /// Same as `readlink` except `file_path` is null-terminated. -pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (windows.is_the_target) { +pub fn readlinkC(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { + if (builtin.os == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); @compileError("TODO implement readlink for Windows"); } @@ -1243,12 +1448,32 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { } } +pub fn readlinkatC(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { + if (builtin.os == .windows) { + const file_path_w = try windows.cStrToPrefixedFileW(file_path); + @compileError("TODO implement readlink for Windows"); + } + const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); + switch (errno(rc)) { + 0 => return out_buffer[0..@bitCast(usize, rc)], + EACCES => return error.AccessDenied, + EFAULT => unreachable, + EINVAL => unreachable, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOTDIR => return error.NotDir, + else => |err| return unexpectedErrno(err), + } +} + pub const SetIdError = error{ ResourceLimitReached, InvalidUserId, PermissionDenied, - Unexpected, -}; +} || UnexpectedError; pub fn setuid(uid: u32) SetIdError!void { switch (errno(system.setuid(uid))) { @@ -1292,7 +1517,7 @@ pub fn setregid(rgid: u32, egid: u32) SetIdError!void { /// Test whether a file descriptor refers to a terminal. pub fn isatty(handle: fd_t) bool { - if (windows.is_the_target) { + if (builtin.os == .windows) { if (isCygwinPty(handle)) return true; @@ -1302,18 +1527,33 @@ pub fn isatty(handle: fd_t) bool { if (builtin.link_libc) { return system.isatty(handle) != 0; } - if (wasi.is_the_target) { - @compileError("TODO implement std.os.isatty for WASI"); + if (builtin.os == .wasi) { + var statbuf: fdstat_t = undefined; + const err = system.fd_fdstat_get(handle, &statbuf); + if (err != 0) { + // errno = err; + return false; + } + + // A tty is a character device that we can't seek or tell on. + if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or + (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) + { + // errno = ENOTTY; + return false; + } + + return true; } - if (linux.is_the_target) { + if (builtin.os == .linux) { var wsz: linux.winsize = undefined; - return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, isize(handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } unreachable; } pub fn isCygwinPty(handle: fd_t) bool { - if (!windows.is_the_target) return false; + if (builtin.os != .windows) return false; const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH); @@ -1328,10 +1568,10 @@ pub fn isCygwinPty(handle: fd_t) bool { } const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); - const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; + const name_bytes = name_info_bytes[size .. size + @as(usize, name_info.FileNameLength)]; const name_wide = @bytesToSlice(u16, name_bytes); - return mem.indexOf(u16, name_wide, [_]u16{ 'm', 's', 'y', 's', '-' }) != null or - mem.indexOf(u16, name_wide, [_]u16{ '-', 'p', 't', 'y' }) != null; + return mem.indexOf(u16, name_wide, &[_]u16{ 'm', 's', 'y', 's', '-' }) != null or + mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null; } pub const SocketError = error{ @@ -1357,20 +1597,19 @@ pub const SocketError = error{ /// The protocol type or the specified protocol is not supported within this domain. ProtocolNotSupported, +} || UnexpectedError; - Unexpected, -}; - -pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 { +pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t { const rc = system.socket(domain, socket_type, protocol); switch (errno(rc)) { - 0 => return @intCast(i32, rc), + 0 => return @intCast(fd_t, rc), EACCES => return error.PermissionDenied, EAFNOSUPPORT => return error.AddressFamilyNotSupported, EINVAL => return error.ProtocolFamilyNotAvailable, EMFILE => return error.ProcessFdQuotaExceeded, ENFILE => return error.SystemFdQuotaExceeded, - ENOBUFS, ENOMEM => return error.SystemResources, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, EPROTONOSUPPORT => return error.ProtocolNotSupported, else => |err| return unexpectedErrno(err), } @@ -1409,22 +1648,20 @@ pub const BindError = error{ /// The socket inode would reside on a read-only filesystem. ReadOnlyFileSystem, - - Unexpected, -}; +} || UnexpectedError; /// addr is `*const T` where T is one of the sockaddr -pub fn bind(fd: i32, addr: *const sockaddr) BindError!void { - const rc = system.bind(fd, addr, @sizeOf(sockaddr)); +pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void { + const rc = system.bind(sockfd, addr, len); switch (errno(rc)) { 0 => return, EACCES => return error.AccessDenied, EADDRINUSE => return error.AddressInUse, EBADF => unreachable, // always a race condition if this error is returned - EINVAL => unreachable, - ENOTSOCK => unreachable, + EINVAL => unreachable, // invalid parameters + ENOTSOCK => unreachable, // invalid `sockfd` EADDRNOTAVAIL => return error.AddressNotAvailable, - EFAULT => unreachable, + EFAULT => unreachable, // invalid `addr` pointer ELOOP => return error.SymLinkLoop, ENAMETOOLONG => return error.NameTooLong, ENOENT => return error.FileNotFound, @@ -1448,9 +1685,7 @@ const ListenError = error{ /// The socket is not of a type that supports the listen() operation. OperationNotSupported, - - Unexpected, -}; +} || UnexpectedError; pub fn listen(sockfd: i32, backlog: u32) ListenError!void { const rc = system.listen(sockfd, backlog); @@ -1477,72 +1712,70 @@ pub const AcceptError = error{ /// by the socket buffer limits, not by the system memory. SystemResources, - /// The file descriptor sockfd does not refer to a socket. - FileDescriptorNotASocket, - - /// The referenced socket is not of type SOCK_STREAM. - OperationNotSupported, - ProtocolFailure, /// Firewall rules forbid connection. BlockedByFirewall, - Unexpected, -}; + /// This error occurs when no global event loop is configured, + /// and accepting from the socket would block. + WouldBlock, +} || UnexpectedError; -/// Accept a connection on a socket. `fd` must be opened in blocking mode. -/// See also `accept4_async`. -pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { +/// Accept a connection on a socket. +/// If the application has a global event loop enabled, EAGAIN is handled +/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. +pub fn accept4( + /// This argument is a socket that has been created with `socket`, bound to a local address + /// with `bind`, and is listening for connections after a `listen`. + sockfd: fd_t, + /// This argument is a pointer to a sockaddr structure. This structure is filled in with the + /// address of the peer socket, as known to the communications layer. The exact format of the + /// address returned addr is determined by the socket's address family (see `socket` and the + /// respective protocol man pages). + addr: *sockaddr, + /// This argument is a value-result argument: the caller must initialize it to contain the + /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size + /// of the peer address. + /// + /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` + /// will return a value greater than was supplied to the call. + addr_size: *socklen_t, + /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise + /// ORed in flags to obtain different behavior: + /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`) + /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve + /// the same result. + /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the + /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. + flags: u32, +) AcceptError!fd_t { while (true) { - var sockaddr_size = u32(@sizeOf(sockaddr)); - const rc = system.accept4(fd, addr, &sockaddr_size, flags); + const rc = system.accept4(sockfd, addr, addr_size, flags); switch (errno(rc)) { - 0 => return @intCast(i32, rc), + 0 => return @intCast(fd_t, rc), EINTR => continue, - else => |err| return unexpectedErrno(err), - EAGAIN => unreachable, // This function is for blocking only. + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(sockfd); + continue; + } else { + return error.WouldBlock; + }, EBADF => unreachable, // always a race condition ECONNABORTED => return error.ConnectionAborted, EFAULT => unreachable, EINVAL => unreachable, + ENOTSOCK => unreachable, EMFILE => return error.ProcessFdQuotaExceeded, ENFILE => return error.SystemFdQuotaExceeded, ENOBUFS => return error.SystemResources, ENOMEM => return error.SystemResources, - ENOTSOCK => return error.FileDescriptorNotASocket, - EOPNOTSUPP => return error.OperationNotSupported, + EOPNOTSUPP => unreachable, EPROTO => return error.ProtocolFailure, EPERM => return error.BlockedByFirewall, - } - } -} -/// This is the same as `accept4` except `fd` is expected to be non-blocking. -/// Returns -1 if would block. -pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { - while (true) { - var sockaddr_size = u32(@sizeOf(sockaddr)); - const rc = system.accept4(fd, addr, &sockaddr_size, flags); - switch (errno(rc)) { - 0 => return @intCast(i32, rc), - EINTR => continue, else => |err| return unexpectedErrno(err), - - EAGAIN => return -1, - EBADF => unreachable, // always a race condition - ECONNABORTED => return error.ConnectionAborted, - EFAULT => unreachable, - EINVAL => unreachable, - EMFILE => return error.ProcessFdQuotaExceeded, - ENFILE => return error.SystemFdQuotaExceeded, - ENOBUFS => return error.SystemResources, - ENOMEM => return error.SystemResources, - ENOTSOCK => return error.FileDescriptorNotASocket, - EOPNOTSUPP => return error.OperationNotSupported, - EPROTO => return error.ProtocolFailure, - EPERM => return error.BlockedByFirewall, } } } @@ -1559,9 +1792,7 @@ pub const EpollCreateError = error{ /// There was insufficient memory to create the kernel object. SystemResources, - - Unexpected, -}; +} || UnexpectedError; pub fn epoll_create1(flags: u32) EpollCreateError!i32 { const rc = system.epoll_create1(flags); @@ -1600,9 +1831,7 @@ pub const EpollCtlError = error{ /// The target file fd does not support epoll. This error can occur if fd refers to, /// for example, a regular file or a directory. FileDescriptorIncompatibleWithEpoll, - - Unexpected, -}; +} || UnexpectedError; pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*epoll_event) EpollCtlError!void { const rc = system.epoll_ctl(epfd, op, fd, event); @@ -1643,8 +1872,7 @@ pub const EventFdError = error{ SystemResources, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, - Unexpected, -}; +} || UnexpectedError; pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { const rc = system.eventfd(initval, flags); @@ -1663,15 +1891,11 @@ pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { pub const GetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, +} || UnexpectedError; - Unexpected, -}; - -pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr { - var addr: sockaddr = undefined; - var addrlen: socklen_t = @sizeOf(sockaddr); - switch (errno(system.getsockname(sockfd, &addr, &addrlen))) { - 0 => return addr, +pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { + switch (errno(system.getsockname(sockfd, addr, addrlen))) { + 0 => return, else => |err| return unexpectedErrno(err), EBADF => unreachable, // always a race condition @@ -1715,13 +1939,16 @@ pub const ConnectError = error{ /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. ConnectionTimedOut, - Unexpected, -}; + /// This error occurs when no global event loop is configured, + /// and connecting to the socket would block. + WouldBlock, + + /// The given path for the unix socket does not exist. + FileNotFound, +} || UnexpectedError; /// Initiate a connection on a socket. -/// This is for blocking file descriptors only. -/// For non-blocking, see `connect_async`. -pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { +pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { while (true) { switch (errno(system.connect(sockfd, sock_addr, len))) { 0 => return, @@ -1730,46 +1957,22 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v EADDRINUSE => return error.AddressInUse, EADDRNOTAVAIL => return error.AddressNotAvailable, EAFNOSUPPORT => return error.AddressFamilyNotSupported, - EAGAIN => return error.SystemResources, + EAGAIN, EINPROGRESS => { + const loop = std.event.Loop.instance orelse return error.WouldBlock; + loop.waitUntilFdWritableOrReadable(sockfd); + return getsockoptError(sockfd); + }, EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. EBADF => unreachable, // sockfd is not a valid open file descriptor. ECONNREFUSED => return error.ConnectionRefused, EFAULT => unreachable, // The socket structure address is outside the user's address space. - EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. EINTR => continue, EISCONN => unreachable, // The socket is already connected. ENETUNREACH => return error.NetworkUnreachable, ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. ETIMEDOUT => return error.ConnectionTimedOut, - else => |err| return unexpectedErrno(err), - } - } -} - -/// Same as `connect` except it is for blocking socket file descriptors. -/// It expects to receive EINPROGRESS`. -pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { - while (true) { - switch (errno(system.connect(sockfd, sock_addr, len))) { - EINVAL => unreachable, - EINTR => continue, - 0, EINPROGRESS => return, - EACCES => return error.PermissionDenied, - EPERM => return error.PermissionDenied, - EADDRINUSE => return error.AddressInUse, - EADDRNOTAVAIL => return error.AddressNotAvailable, - EAFNOSUPPORT => return error.AddressFamilyNotSupported, - EAGAIN => return error.SystemResources, - EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - EBADF => unreachable, // sockfd is not a valid open file descriptor. - ECONNREFUSED => return error.ConnectionRefused, - EFAULT => unreachable, // The socket structure address is outside the user's address space. - EISCONN => unreachable, // The socket is already connected. - ENETUNREACH => return error.NetworkUnreachable, - ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - ETIMEDOUT => return error.ConnectionTimedOut, + ENOENT => return error.FileNotFound, // Returned when socket is AF_UNIX and the given path does not exist. else => |err| return unexpectedErrno(err), } } @@ -1826,17 +2029,18 @@ pub fn waitpid(pid: i32, flags: u32) u32 { pub const FStatError = error{ SystemResources, - Unexpected, -}; + AccessDenied, +} || UnexpectedError; pub fn fstat(fd: fd_t) FStatError!Stat { var stat: Stat = undefined; - if (darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { switch (darwin.getErrno(darwin.@"fstat$INODE64"(fd, &stat))) { 0 => return stat, EINVAL => unreachable, EBADF => unreachable, // Always a race condition. ENOMEM => return error.SystemResources, + EACCES => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -1846,6 +2050,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { EINVAL => unreachable, EBADF => unreachable, // Always a race condition. ENOMEM => return error.SystemResources, + EACCES => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -1856,9 +2061,7 @@ pub const KQueueError = error{ /// The system-wide limit on the total number of open files has been reached. SystemFdQuotaExceeded, - - Unexpected, -}; +} || UnexpectedError; pub fn kqueue() KQueueError!i32 { const rc = system.kqueue(); @@ -1922,8 +2125,7 @@ pub const INotifyInitError = error{ ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SystemResources, - Unexpected, -}; +} || UnexpectedError; /// initialize an inotify instance pub fn inotify_init1(flags: u32) INotifyInitError!i32 { @@ -1944,8 +2146,7 @@ pub const INotifyAddWatchError = error{ FileNotFound, SystemResources, UserResourceLimitReached, - Unexpected, -}; +} || UnexpectedError; /// add a watch to an initialized inotify instance pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 { @@ -1954,7 +2155,7 @@ pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INoti } /// Same as `inotify_add_watch` except pathname is null-terminated. -pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 { +pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 { const rc = system.inotify_add_watch(inotify_fd, pathname, mask); switch (errno(rc)) { 0 => return @intCast(i32, rc), @@ -1992,8 +2193,7 @@ pub const MProtectError = error{ /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three mapโ€ /// pings: two read/write mappings at each end and a read-only mapping in the middle.) OutOfMemory, - Unexpected, -}; +} || UnexpectedError; /// `memory.len` must be page-aligned. pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void { @@ -2007,10 +2207,7 @@ pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectErro } } -pub const ForkError = error{ - SystemResources, - Unexpected, -}; +pub const ForkError = error{SystemResources} || UnexpectedError; pub fn fork() ForkError!pid_t { const rc = system.fork(); @@ -2037,8 +2234,7 @@ pub const MMapError = error{ PermissionDenied, LockedMemoryLimitExceeded, OutOfMemory, - Unexpected, -}; +} || UnexpectedError; /// Map files or devices into memory. /// Use of a mapped region can result in these signals: @@ -2101,14 +2297,12 @@ pub const AccessError = error{ /// On Windows, file paths must be valid Unicode. InvalidUtf8, - - Unexpected, -}; +} || UnexpectedError; /// check user's permissions for a file /// TODO currently this assumes `mode` is `F_OK` on Windows. pub fn access(path: []const u8, mode: u32) AccessError!void { - if (windows.is_the_target) { + if (builtin.os == .windows) { const path_w = try windows.sliceToPrefixedFileW(path); _ = try windows.GetFileAttributesW(&path_w); return; @@ -2118,8 +2312,8 @@ pub fn access(path: []const u8, mode: u32) AccessError!void { } /// Same as `access` except `path` is null-terminated. -pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { - if (windows.is_the_target) { +pub fn accessC(path: [*:0]const u8, mode: u32) AccessError!void { + if (builtin.os == .windows) { const path_w = try windows.cStrToPrefixedFileW(path); _ = try windows.GetFileAttributesW(&path_w); return; @@ -2145,7 +2339,7 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. /// Otherwise use `access` or `accessC`. /// TODO currently this ignores `mode`. -pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void { +pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void { const ret = try windows.GetFileAttributesW(path); if (ret != windows.INVALID_FILE_ATTRIBUTES) { return; @@ -2161,8 +2355,7 @@ pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!voi pub const PipeError = error{ SystemFdQuotaExceeded, ProcessFdQuotaExceeded, - Unexpected, -}; +} || UnexpectedError; /// Creates a unidirectional data channel that can be used for interprocess communication. pub fn pipe() PipeError![2]fd_t { @@ -2193,8 +2386,7 @@ pub const SysCtlError = error{ PermissionDenied, SystemResources, NameTooLong, - Unexpected, -}; +} || UnexpectedError; pub fn sysctl( name: []const c_int, @@ -2214,7 +2406,7 @@ pub fn sysctl( } pub fn sysctlbynameC( - name: [*]const u8, + name: [*:0]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, @@ -2237,14 +2429,11 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { } } -pub const SeekError = error{ - Unseekable, - Unexpected, -}; +pub const SeekError = error{Unseekable} || UnexpectedError; /// Repositions read/write file offset relative to the beginning. pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { - if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { 0 => return, @@ -2256,7 +2445,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.SetFilePointerEx_BEGIN(fd, offset); } const ipos = @bitCast(i64, offset); // the OS treats this as unsigned @@ -2273,7 +2462,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { /// Repositions read/write file offset relative to the current offset. pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { - if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { 0 => return, @@ -2285,7 +2474,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.SetFilePointerEx_CURRENT(fd, offset); } switch (errno(system.lseek(fd, offset, SEEK_CUR))) { @@ -2301,7 +2490,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { /// Repositions read/write file offset relative to the end. pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { - if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { 0 => return, @@ -2313,7 +2502,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { else => |err| return unexpectedErrno(err), } } - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.SetFilePointerEx_END(fd, offset); } switch (errno(system.lseek(fd, offset, SEEK_END))) { @@ -2329,7 +2518,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { /// Returns the read/write file offset relative to the beginning. pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { - if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) { + if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { var result: u64 = undefined; switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { 0 => return result, @@ -2341,7 +2530,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { else => |err| return unexpectedErrno(err), } } - if (windows.is_the_target) { + if (builtin.os == .windows) { return windows.SetFilePointerEx_CURRENT_get(fd); } const rc = system.lseek(fd, 0, SEEK_CUR); @@ -2382,9 +2571,7 @@ pub const RealPathError = error{ InvalidUtf8, PathAlreadyExists, - - Unexpected, -}; +} || UnexpectedError; /// Return the canonicalized absolute pathname. /// Expands all symbolic links and resolves references to `.`, `..`, and @@ -2392,7 +2579,7 @@ pub const RealPathError = error{ /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. /// See also `realpathC` and `realpathW`. pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { - if (windows.is_the_target) { + if (builtin.os == .windows) { const pathname_w = try windows.sliceToPrefixedFileW(pathname); return realpathW(&pathname_w, out_buffer); } @@ -2401,19 +2588,19 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE } /// Same as `realpath` except `pathname` is null-terminated. -pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { - if (windows.is_the_target) { +pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { + if (builtin.os == .windows) { const pathname_w = try windows.cStrToPrefixedFileW(pathname); return realpathW(&pathname_w, out_buffer); } - if (linux.is_the_target and !builtin.link_libc) { + if (builtin.os == .linux and !builtin.link_libc) { const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0); defer close(fd); - var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable; + var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; + const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; - return readlinkC(proc_path.ptr, out_buffer); + return readlinkC(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer); } const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) { EINVAL => unreachable, @@ -2432,7 +2619,7 @@ pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPat } /// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded. -pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { +pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { const h_file = try windows.CreateFileW( pathname, windows.GENERIC_READ, @@ -2445,14 +2632,12 @@ pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPa defer windows.CloseHandle(h_file); var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; - const wide_len = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS); - assert(wide_len <= wide_buf.len); - const wide_slice = wide_buf[0..wide_len]; + const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS); // Windows returns \\?\ prepended to the path. // We strip it to make this function consistent across platforms. const prefix = [_]u16{ '\\', '\\', '?', '\\' }; - const start_index = if (mem.startsWith(u16, wide_slice, prefix)) prefix.len else 0; + const start_index = if (mem.startsWith(u16, wide_slice, &prefix)) prefix.len else 0; // Trust that Windows gives us valid UTF-16LE. const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable; @@ -2484,9 +2669,12 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void { } } -pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize { - // This is implemented only for systems using ELF executables - if (windows.is_the_target or builtin.os == .uefi or wasi.is_the_target or darwin.is_the_target) +pub fn dl_iterate_phdr( + comptime T: type, + callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, + data: ?*T, +) isize { + if (builtin.object_format != .elf) @compileError("dl_iterate_phdr is not available for this target"); if (builtin.link_libc) { @@ -2510,7 +2698,7 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf if (it.end()) { var info = dl_phdr_info{ .dlpi_addr = elf_base, - .dlpi_name = c"/proc/self/exe", + .dlpi_name = "/proc/self/exe", .dlpi_phdr = phdrs.ptr, .dlpi_phnum = ehdr.e_phnum, }; @@ -2548,12 +2736,23 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf return last_r; } -pub const ClockGetTimeError = error{ - UnsupportedClock, - Unexpected, -}; +pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { + if (comptime std.Target.current.getOs() == .wasi) { + var ts: timestamp_t = undefined; + switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { + 0 => { + tp.* = .{ + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), + }; + }, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), + } + return; + } switch (errno(system.clock_gettime(clk_id, tp))) { 0 => return, EFAULT => unreachable, @@ -2563,6 +2762,19 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { } pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { + if (comptime std.Target.current.getOs() == .wasi) { + var ts: timestamp_t = undefined; + switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { + 0 => res.* = .{ + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), + }, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), + } + return; + } + switch (errno(system.clock_getres(clk_id, res))) { 0 => return, EFAULT => unreachable, @@ -2571,10 +2783,7 @@ pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { } } -pub const SchedGetAffinityError = error{ - PermissionDenied, - Unexpected, -}; +pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { var set: cpu_set_t = undefined; @@ -2590,8 +2799,8 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { /// Used to convert a slice to a null terminated slice on the stack. /// TODO https://github.com/ziglang/zig/issues/287 -pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { - var path_with_null: [PATH_MAX]u8 = undefined; +pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 { + var path_with_null: [PATH_MAX - 1:0]u8 = undefined; // >= rather than > to make room for the null byte if (file_path.len >= PATH_MAX) return error.NameTooLong; mem.copy(u8, &path_with_null, file_path); @@ -2616,7 +2825,7 @@ pub const UnexpectedError = error{ /// and you get an unexpected error. pub fn unexpectedErrno(err: usize) UnexpectedError { if (unexpected_error_tracing) { - std.debug.warn("unexpected errno: {}\n", err); + std.debug.warn("unexpected errno: {}\n", .{err}); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; @@ -2628,11 +2837,10 @@ pub const SigaltstackError = error{ /// Attempted to change the signal stack while it was active. PermissionDenied, - Unexpected, -}; +} || UnexpectedError; pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { - if (windows.is_the_target or uefi.is_the_target or wasi.is_the_target) + if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi) @compileError("std.os.sigaltstack not available for this target"); switch (errno(system.sigaltstack(ss, old_ss))) { @@ -2677,9 +2885,7 @@ pub const FutimensError = error{ PermissionDenied, ReadOnlyFileSystem, - - Unexpected, -}; +} || UnexpectedError; pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { switch (errno(system.futimens(fd, times))) { @@ -2694,26 +2900,23 @@ pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { } } -pub const GetHostNameError = error{ - PermissionDenied, - Unexpected, -}; +pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { if (builtin.link_libc) { switch (errno(system.gethostname(name_buffer, name_buffer.len))) { - 0 => return mem.toSlice(u8, name_buffer), + 0 => return mem.toSlice(u8, @ptrCast([*:0]u8, name_buffer)), EFAULT => unreachable, ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this EPERM => return error.PermissionDenied, else => |err| return unexpectedErrno(err), } } - if (linux.is_the_target) { + if (builtin.os == .linux) { var uts: utsname = undefined; switch (errno(system.uname(&uts))) { 0 => { - const hostname = mem.toSlice(u8, &uts.nodename); + const hostname = mem.toSlice(u8, @ptrCast([*:0]u8, &uts.nodename)); mem.copy(u8, name_buffer, hostname); return name_buffer[0..hostname.len]; }, @@ -2726,15 +2929,401 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { @compileError("TODO implement gethostname for this OS"); } -test "" { - _ = @import("os/darwin.zig"); - _ = @import("os/freebsd.zig"); - _ = @import("os/linux.zig"); - _ = @import("os/netbsd.zig"); - _ = @import("os/uefi.zig"); - _ = @import("os/wasi.zig"); - _ = @import("os/windows.zig"); - _ = @import("os/zen.zig"); +pub fn res_mkquery( + op: u4, + dname: []const u8, + class: u8, + ty: u8, + data: []const u8, + newrr: ?[*]const u8, + buf: []u8, +) usize { + // This implementation is ported from musl libc. + // A more idiomatic "ziggy" implementation would be welcome. + var name = dname; + if (mem.endsWith(u8, name, ".")) name.len -= 1; + assert(name.len <= 253); + const n = 17 + name.len + @boolToInt(name.len != 0); - _ = @import("os/test.zig"); + // Construct query template - ID will be filled later + var q: [280]u8 = undefined; + @memset(&q, 0, n); + q[2] = @as(u8, op) * 8 + 1; + q[5] = 1; + mem.copy(u8, q[13..], name); + var i: usize = 13; + var j: usize = undefined; + while (q[i] != 0) : (i = j + 1) { + j = i; + while (q[j] != 0 and q[j] != '.') : (j += 1) {} + // TODO determine the circumstances for this and whether or + // not this should be an error. + if (j - i - 1 > 62) unreachable; + q[i - 1] = @intCast(u8, j - i); + } + q[i + 1] = ty; + q[i + 3] = class; + + // Make a reasonably unpredictable id + var ts: timespec = undefined; + clock_gettime(CLOCK_REALTIME, &ts) catch {}; + const UInt = @IntType(false, @TypeOf(ts.tv_nsec).bit_count); + const unsec = @bitCast(UInt, ts.tv_nsec); + const id = @truncate(u32, unsec + unsec / 65536); + q[0] = @truncate(u8, id / 256); + q[1] = @truncate(u8, id); + + mem.copy(u8, buf, q[0..n]); + return n; +} + +pub const SendError = error{ + /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied + /// on the destination socket file, or search permission is denied for one of the + /// directories the path prefix. (See path_resolution(7).) + /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though + /// it was a unicast address. + AccessDenied, + + /// The socket is marked nonblocking and the requested operation would block, and + /// there is no global event loop configured. + /// It's also possible to get this error under the following condition: + /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously + /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was + /// determined that all port numbers in the ephemeral port range are currently in use. See + /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). + WouldBlock, + + /// Another Fast Open is already in progress. + FastOpenAlreadyInProgress, + + /// Connection reset by peer. + ConnectionResetByPeer, + + /// The socket type requires that message be sent atomically, and the size of the message + /// to be sent made this impossible. The message is not transmitted. + /// + MessageTooBig, + + /// The output queue for a network interface was full. This generally indicates that the + /// interface has stopped sending, but may be caused by transient congestion. (Normally, + /// this does not occur in Linux. Packets are just silently dropped when a device queue + /// overflows.) + /// This is also caused when there is not enough kernel memory available. + SystemResources, + + /// The local end has been shut down on a connection oriented socket. In this case, the + /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. + BrokenPipe, +} || UnexpectedError; + +/// Transmit a message to another socket. +/// +/// The `sendto` call may be used only when the socket is in a connected state (so that the intended +/// recipient is known). The following call +/// +/// send(sockfd, buf, len, flags); +/// +/// is equivalent to +/// +/// sendto(sockfd, buf, len, flags, NULL, 0); +/// +/// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments +/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted +/// that the socket was actually connected. +/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size. +/// +/// If the message is too long to pass atomically through the underlying protocol, +/// `SendError.MessageTooBig` is returned, and the message is not transmitted. +/// +/// There is no indication of failure to deliver. +/// +/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks, +/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail +/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is +/// possible to send more data. +pub fn sendto( + /// The file descriptor of the sending socket. + sockfd: fd_t, + /// Message to send. + buf: []const u8, + flags: u32, + dest_addr: ?*const sockaddr, + addrlen: socklen_t, +) SendError!usize { + while (true) { + const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + + EACCES => return error.AccessDenied, + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdWritable(sockfd); + continue; + } else { + return error.WouldBlock; + }, + EALREADY => return error.FastOpenAlreadyInProgress, + EBADF => unreachable, // always a race condition + ECONNRESET => return error.ConnectionResetByPeer, + EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. + EFAULT => unreachable, // An invalid user space address was specified for an argument. + EINTR => continue, + EINVAL => unreachable, // Invalid argument passed. + EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified + EMSGSIZE => return error.MessageTooBig, + ENOBUFS => return error.SystemResources, + ENOMEM => return error.SystemResources, + ENOTCONN => unreachable, // The socket is not connected, and no target has been given. + ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. + EPIPE => return error.BrokenPipe, + else => |err| return unexpectedErrno(err), + } + } +} + +/// Transmit a message to another socket. +/// +/// The `send` call may be used only when the socket is in a connected state (so that the intended +/// recipient is known). The only difference between `send` and `write` is the presence of +/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following +/// call +/// +/// send(sockfd, buf, len, flags); +/// +/// is equivalent to +/// +/// sendto(sockfd, buf, len, flags, NULL, 0); +/// +/// There is no indication of failure to deliver. +/// +/// When the message does not fit into the send buffer of the socket, `send` normally blocks, +/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail +/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is +/// possible to send more data. +pub fn send( + /// The file descriptor of the sending socket. + sockfd: fd_t, + buf: []const u8, + flags: u32, +) SendError!usize { + return sendto(sockfd, buf, flags, null, 0); +} + +pub const PollError = error{ + /// The kernel had no space to allocate file descriptor tables. + SystemResources, +} || UnexpectedError; + +pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { + while (true) { + const rc = system.poll(fds.ptr, fds.len, timeout); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EFAULT => unreachable, + EINTR => continue, + EINVAL => unreachable, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } + } +} + +pub const RecvFromError = error{ + /// The socket is marked nonblocking and the requested operation would block, and + /// there is no global event loop configured. + WouldBlock, + + /// A remote host refused to allow the network connection, typically because it is not + /// running the requested service. + ConnectionRefused, + + /// Could not allocate kernel memory. + SystemResources, +} || UnexpectedError; + +pub fn recvfrom( + sockfd: fd_t, + buf: []u8, + flags: u32, + src_addr: ?*sockaddr, + addrlen: ?*socklen_t, +) RecvFromError!usize { + while (true) { + const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EBADF => unreachable, // always a race condition + EFAULT => unreachable, + EINVAL => unreachable, + ENOTCONN => unreachable, + ENOTSOCK => unreachable, + EINTR => continue, + EAGAIN => if (std.event.Loop.instance) |loop| { + loop.waitUntilFdReadable(sockfd); + continue; + } else { + return error.WouldBlock; + }, + ENOMEM => return error.SystemResources, + ECONNREFUSED => return error.ConnectionRefused, + else => |err| return unexpectedErrno(err), + } + } +} + +pub const DnExpandError = error{InvalidDnsPacket}; + +pub fn dn_expand( + msg: []const u8, + comp_dn: []const u8, + exp_dn: []u8, +) DnExpandError!usize { + // This implementation is ported from musl libc. + // A more idiomatic "ziggy" implementation would be welcome. + var p = comp_dn.ptr; + var len: usize = std.math.maxInt(usize); + const end = msg.ptr + msg.len; + if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket; + var dest = exp_dn.ptr; + const dend = dest + std.math.min(exp_dn.len, 254); + // detect reference loop using an iteration counter + var i: usize = 0; + while (i < msg.len) : (i += 2) { + // loop invariants: p= msg.len) return error.InvalidDnsPacket; + p = msg.ptr + j; + } else if (p[0] != 0) { + if (dest != exp_dn.ptr) { + dest.* = '.'; + dest += 1; + } + var j = p[0]; + p += 1; + if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) { + return error.InvalidDnsPacket; + } + while (j != 0) { + j -= 1; + dest.* = p[0]; + dest += 1; + p += 1; + } + } else { + dest.* = 0; + if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr); + return len; + } + } + return error.InvalidDnsPacket; +} + +pub const SchedYieldError = error{ + /// The system is not configured to allow yielding + SystemCannotYield, +}; + +pub fn sched_yield() SchedYieldError!void { + if (builtin.os == .windows) { + // The return value has to do with how many other threads there are; it is not + // an error condition on Windows. + _ = windows.kernel32.SwitchToThread(); + return; + } + switch (errno(system.sched_yield())) { + 0 => return, + ENOSYS => return error.SystemCannotYield, + else => return error.SystemCannotYield, + } +} + +pub const SetSockOptError = error{ + /// The socket is already connected, and a specified option cannot be set while the socket is connected. + AlreadyConnected, + + /// The option is not supported by the protocol. + InvalidProtocolOption, + + /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure. + TimeoutTooBig, + + /// Insufficient resources are available in the system to complete the call. + SystemResources, +} || UnexpectedError; + +/// Set a socket's options. +pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { + switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { + 0 => {}, + EBADF => unreachable, // always a race condition + ENOTSOCK => unreachable, // always a race condition + EINVAL => unreachable, + EFAULT => unreachable, + EDOM => return error.TimeoutTooBig, + EISCONN => return error.AlreadyConnected, + ENOPROTOOPT => return error.InvalidProtocolOption, + ENOMEM => return error.SystemResources, + ENOBUFS => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +} + +pub const MemFdCreateError = error{ + SystemFdQuotaExceeded, + ProcessFdQuotaExceeded, + OutOfMemory, + + /// memfd_create is available in Linux 3.17 and later. This error is returned + /// for older kernel versions. + SystemOutdated, +} || UnexpectedError; + +pub fn memfd_createC(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { + // memfd_create is available only in glibc versions starting with 2.27. + const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; + const sys = if (use_c) std.c else linux; + const getErrno = if (use_c) std.c.getErrno else linux.getErrno; + const rc = sys.memfd_create(name, flags); + switch (getErrno(rc)) { + 0 => return @intCast(fd_t, rc), + EFAULT => unreachable, // name has invalid memory + EINVAL => unreachable, // name/flags are faulty + ENFILE => return error.SystemFdQuotaExceeded, + EMFILE => return error.ProcessFdQuotaExceeded, + ENOMEM => return error.OutOfMemory, + ENOSYS => return error.SystemOutdated, + else => |err| return unexpectedErrno(err), + } +} + +pub const MFD_NAME_PREFIX = "memfd:"; +pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len; +fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 { + var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined; + // >= rather than > to make room for the null byte + if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong; + mem.copy(u8, &path_with_null, name); + path_with_null[name.len] = 0; + return path_with_null; +} + +pub fn memfd_create(name: []const u8, flags: u32) !fd_t { + const name_t = try toMemFdPath(name); + return memfd_createC(&name_t, flags); +} + +pub fn getrusage(who: i32) rusage { + var result: rusage = undefined; + const rc = system.getrusage(who, &result); + switch (errno(rc)) { + 0 => return result, + EINVAL => unreachable, + EFAULT => unreachable, + else => unreachable, + } } diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig index f16da2487..bab9ad0ae 100644 --- a/lib/std/os/bits.zig +++ b/lib/std/os/bits.zig @@ -1,10 +1,14 @@ -// Platform-dependent types and values that are used along with OS-specific APIs. -// These are imported into `std.c`, `std.os`, and `std.os.linux`. +//! Platform-dependent types and values that are used along with OS-specific APIs. +//! These are imported into `std.c`, `std.os`, and `std.os.linux`. +//! Root source files can define `os.bits` and these will additionally be added +//! to the namespace. const builtin = @import("builtin"); +const root = @import("root"); pub usingnamespace switch (builtin.os) { .macosx, .ios, .tvos, .watchos => @import("bits/darwin.zig"), + .dragonfly => @import("bits/dragonfly.zig"), .freebsd => @import("bits/freebsd.zig"), .linux => @import("bits/linux.zig"), .netbsd => @import("bits/netbsd.zig"), @@ -13,8 +17,7 @@ pub usingnamespace switch (builtin.os) { else => struct {}, }; -pub const pthread_t = *@OpaqueType(); -pub const FILE = @OpaqueType(); +pub usingnamespace if (@hasDecl(root, "os") and @hasDecl(root.os, "bits")) root.os.bits else struct {}; pub const iovec = extern struct { iov_base: [*]u8, diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index b076f9588..d4340443e 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -8,26 +8,34 @@ pub const pid_t = c_int; pub const in_port_t = u16; pub const sa_family_t = u8; pub const socklen_t = u32; -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, +pub const sockaddr = extern struct { + len: u8, + family: sa_family_t, + data: [14]u8, }; pub const sockaddr_in = extern struct { - len: u8, - family: sa_family_t, + len: u8 = @sizeOf(sockaddr_in), + family: sa_family_t = AF_INET, port: in_port_t, addr: u32, - zero: [8]u8, + zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, }; pub const sockaddr_in6 = extern struct { - len: u8, - family: sa_family_t, + len: u8 = @sizeOf(sockaddr_in6), + family: sa_family_t = AF_INET6, port: in_port_t, flowinfo: u32, addr: [16]u8, scope_id: u32, }; +/// UNIX domain socket +pub const sockaddr_un = extern struct { + len: u8 = @sizeOf(sockaddr_un), + family: sa_family_t = AF_UNIX, + path: [104]u8, +}; + pub const timeval = extern struct { tv_sec: c_long, tv_usec: i32, @@ -119,11 +127,10 @@ pub const dirent = extern struct { d_namlen: u16, d_type: u8, d_name: u8, // field address is address of first byte of name -}; -pub const pthread_attr_t = extern struct { - __sig: c_long, - __opaque: [56]u8, + pub fn reclen(self: dirent) u16 { + return self.d_reclen; + } }; /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. @@ -260,7 +267,6 @@ pub const SA_USERTRAMP = 0x0100; /// signal handler with SA_SIGINFO args with 64bit regs information pub const SA_64REGSET = 0x0200; -pub const O_LARGEFILE = 0x0000; pub const O_PATH = 0x0000; pub const F_OK = 0; @@ -1177,4 +1183,43 @@ pub fn S_ISSOCK(m: u32) bool { pub fn S_IWHT(m: u32) bool { return m & S_IFMT == S_IFWHT; } + pub const HOST_NAME_MAX = 72; + +pub const AT_FDCWD = -2; + +/// Use effective ids in access check +pub const AT_EACCESS = 0x0010; + +/// Act on the symlink itself not the target +pub const AT_SYMLINK_NOFOLLOW = 0x0020; + +/// Act on target of symlink +pub const AT_SYMLINK_FOLLOW = 0x0040; + +/// Path refers to directory +pub const AT_REMOVEDIR = 0x0080; + +pub const addrinfo = extern struct { + flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + canonname: ?[*:0]u8, + addr: ?*sockaddr, + next: ?*addrinfo, +}; + +pub const RTLD_LAZY = 0x1; +pub const RTLD_NOW = 0x2; +pub const RTLD_LOCAL = 0x4; +pub const RTLD_GLOBAL = 0x8; +pub const RTLD_NOLOAD = 0x10; +pub const RTLD_NODELETE = 0x80; +pub const RTLD_FIRST = 0x100; + +pub const RTLD_NEXT = @intToPtr(*c_void, ~maxInt(usize)); +pub const RTLD_DEFAULT = @intToPtr(*c_void, ~maxInt(usize) - 1); +pub const RTLD_SELF = @intToPtr(*c_void, ~maxInt(usize) - 2); +pub const RTLD_MAIN_ONLY = @intToPtr(*c_void, ~maxInt(usize) - 4); diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig new file mode 100644 index 000000000..750ca9dff --- /dev/null +++ b/lib/std/os/bits/dragonfly.zig @@ -0,0 +1,696 @@ +const std = @import("../../std.zig"); +const maxInt = std.math.maxInt; + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} +pub const fd_t = c_int; +pub const pid_t = c_int; +pub const off_t = c_long; + +pub const ENOTSUP = EOPNOTSUPP; +pub const EWOULDBLOCK = EAGAIN; +pub const EPERM = 1; +pub const ENOENT = 2; +pub const ESRCH = 3; +pub const EINTR = 4; +pub const EIO = 5; +pub const ENXIO = 6; +pub const E2BIG = 7; +pub const ENOEXEC = 8; +pub const EBADF = 9; +pub const ECHILD = 10; +pub const EDEADLK = 11; +pub const ENOMEM = 12; +pub const EACCES = 13; +pub const EFAULT = 14; +pub const ENOTBLK = 15; +pub const EBUSY = 16; +pub const EEXIST = 17; +pub const EXDEV = 18; +pub const ENODEV = 19; +pub const ENOTDIR = 20; +pub const EISDIR = 21; +pub const EINVAL = 22; +pub const ENFILE = 23; +pub const EMFILE = 24; +pub const ENOTTY = 25; +pub const ETXTBSY = 26; +pub const EFBIG = 27; +pub const ENOSPC = 28; +pub const ESPIPE = 29; +pub const EROFS = 30; +pub const EMLINK = 31; +pub const EPIPE = 32; +pub const EDOM = 33; +pub const ERANGE = 34; +pub const EAGAIN = 35; +pub const EINPROGRESS = 36; +pub const EALREADY = 37; +pub const ENOTSOCK = 38; +pub const EDESTADDRREQ = 39; +pub const EMSGSIZE = 40; +pub const EPROTOTYPE = 41; +pub const ENOPROTOOPT = 42; +pub const EPROTONOSUPPORT = 43; +pub const ESOCKTNOSUPPORT = 44; +pub const EOPNOTSUPP = 45; +pub const EPFNOSUPPORT = 46; +pub const EAFNOSUPPORT = 47; +pub const EADDRINUSE = 48; +pub const EADDRNOTAVAIL = 49; +pub const ENETDOWN = 50; +pub const ENETUNREACH = 51; +pub const ENETRESET = 52; +pub const ECONNABORTED = 53; +pub const ECONNRESET = 54; +pub const ENOBUFS = 55; +pub const EISCONN = 56; +pub const ENOTCONN = 57; +pub const ESHUTDOWN = 58; +pub const ETOOMANYREFS = 59; +pub const ETIMEDOUT = 60; +pub const ECONNREFUSED = 61; +pub const ELOOP = 62; +pub const ENAMETOOLONG = 63; +pub const EHOSTDOWN = 64; +pub const EHOSTUNREACH = 65; +pub const ENOTEMPTY = 66; +pub const EPROCLIM = 67; +pub const EUSERS = 68; +pub const EDQUOT = 69; +pub const ESTALE = 70; +pub const EREMOTE = 71; +pub const EBADRPC = 72; +pub const ERPCMISMATCH = 73; +pub const EPROGUNAVAIL = 74; +pub const EPROGMISMATCH = 75; +pub const EPROCUNAVAIL = 76; +pub const ENOLCK = 77; +pub const ENOSYS = 78; +pub const EFTYPE = 79; +pub const EAUTH = 80; +pub const ENEEDAUTH = 81; +pub const EIDRM = 82; +pub const ENOMSG = 83; +pub const EOVERFLOW = 84; +pub const ECANCELED = 85; +pub const EILSEQ = 86; +pub const ENOATTR = 87; +pub const EDOOFUS = 88; +pub const EBADMSG = 89; +pub const EMULTIHOP = 90; +pub const ENOLINK = 91; +pub const EPROTO = 92; +pub const ENOMEDIUM = 93; +pub const ELAST = 99; +pub const EASYNC = 99; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const MAP_FILE = 0; +pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_COPY = MAP_PRIVATE; +pub const MAP_SHARED = 1; +pub const MAP_PRIVATE = 2; +pub const MAP_FIXED = 16; +pub const MAP_RENAME = 32; +pub const MAP_NORESERVE = 64; +pub const MAP_INHERIT = 128; +pub const MAP_NOEXTEND = 256; +pub const MAP_HASSEMAPHORE = 512; +pub const MAP_STACK = 1024; +pub const MAP_NOSYNC = 2048; +pub const MAP_ANON = 4096; +pub const MAP_VPAGETABLE = 8192; +pub const MAP_TRYFIXED = 65536; +pub const MAP_NOCORE = 131072; +pub const MAP_SIZEALIGN = 262144; + +pub const PATH_MAX = 1024; + +pub const Stat = extern struct { + ino: c_ulong, + nlink: c_uint, + dev: c_uint, + mode: c_ushort, + padding1: u16, + uid: c_uint, + gid: c_uint, + rdev: c_uint, + atim: timespec, + mtim: timespec, + ctim: timespec, + size: c_ulong, + blocks: i64, + blksize: u32, + flags: u32, + gen: u32, + lspare: i32, + qspare1: i64, + qspare2: i64, + pub fn atime(self: Stat) timespec { + return self.atim; + } + + pub fn mtime(self: Stat) timespec { + return self.mtim; + } + + pub fn ctime(self: Stat) timespec { + return self.ctim; + } +}; + +pub const timespec = extern struct { + tv_sec: c_long, + tv_nsec: c_long, +}; + +pub const CTL_UNSPEC = 0; +pub const CTL_KERN = 1; +pub const CTL_VM = 2; +pub const CTL_VFS = 3; +pub const CTL_NET = 4; +pub const CTL_DEBUG = 5; +pub const CTL_HW = 6; +pub const CTL_MACHDEP = 7; +pub const CTL_USER = 8; +pub const CTL_LWKT = 10; +pub const CTL_MAXID = 11; +pub const CTL_MAXNAME = 12; + +pub const KERN_PROC_ALL = 0; +pub const KERN_OSTYPE = 1; +pub const KERN_PROC_PID = 1; +pub const KERN_OSRELEASE = 2; +pub const KERN_PROC_PGRP = 2; +pub const KERN_OSREV = 3; +pub const KERN_PROC_SESSION = 3; +pub const KERN_VERSION = 4; +pub const KERN_PROC_TTY = 4; +pub const KERN_MAXVNODES = 5; +pub const KERN_PROC_UID = 5; +pub const KERN_MAXPROC = 6; +pub const KERN_PROC_RUID = 6; +pub const KERN_MAXFILES = 7; +pub const KERN_PROC_ARGS = 7; +pub const KERN_ARGMAX = 8; +pub const KERN_PROC_CWD = 8; +pub const KERN_PROC_PATHNAME = 9; +pub const KERN_SECURELVL = 9; +pub const KERN_PROC_SIGTRAMP = 10; +pub const KERN_HOSTNAME = 10; +pub const KERN_HOSTID = 11; +pub const KERN_CLOCKRATE = 12; +pub const KERN_VNODE = 13; +pub const KERN_PROC = 14; +pub const KERN_FILE = 15; +pub const KERN_PROC_FLAGMASK = 16; +pub const KERN_PROF = 16; +pub const KERN_PROC_FLAG_LWP = 16; +pub const KERN_POSIX1 = 17; +pub const KERN_NGROUPS = 18; +pub const KERN_JOB_CONTROL = 19; +pub const KERN_SAVED_IDS = 20; +pub const KERN_BOOTTIME = 21; +pub const KERN_NISDOMAINNAME = 22; +pub const KERN_UPDATEINTERVAL = 23; +pub const KERN_OSRELDATE = 24; +pub const KERN_NTP_PLL = 25; +pub const KERN_BOOTFILE = 26; +pub const KERN_MAXFILESPERPROC = 27; +pub const KERN_MAXPROCPERUID = 28; +pub const KERN_DUMPDEV = 29; +pub const KERN_IPC = 30; +pub const KERN_DUMMY = 31; +pub const KERN_PS_STRINGS = 32; +pub const KERN_USRSTACK = 33; +pub const KERN_LOGSIGEXIT = 34; +pub const KERN_IOV_MAX = 35; +pub const KERN_MAXPOSIXLOCKSPERUID = 36; +pub const KERN_MAXID = 37; + +pub const HOST_NAME_MAX = 255; + +pub const O_RDONLY = 0; +pub const O_NDELAY = O_NONBLOCK; +pub const O_WRONLY = 1; +pub const O_RDWR = 2; +pub const O_ACCMODE = 3; +pub const O_NONBLOCK = 4; +pub const O_APPEND = 8; +pub const O_SHLOCK = 16; +pub const O_EXLOCK = 32; +pub const O_ASYNC = 64; +pub const O_FSYNC = 128; +pub const O_SYNC = 128; +pub const O_NOFOLLOW = 256; +pub const O_CREAT = 512; +pub const O_TRUNC = 1024; +pub const O_EXCL = 2048; +pub const O_NOCTTY = 32768; +pub const O_DIRECT = 65536; +pub const O_CLOEXEC = 131072; +pub const O_FBLOCKING = 262144; +pub const O_FNONBLOCKING = 524288; +pub const O_FAPPEND = 1048576; +pub const O_FOFFSET = 2097152; +pub const O_FSYNCWRITE = 4194304; +pub const O_FASYNCWRITE = 8388608; +pub const O_DIRECTORY = 134217728; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; +pub const SEEK_DATA = 3; +pub const SEEK_HOLE = 4; + +pub const F_OK = 0; +pub const F_ULOCK = 0; +pub const F_LOCK = 1; +pub const F_TLOCK = 2; +pub const F_TEST = 3; + +pub const AT_FDCWD = -328243; +pub const AT_SYMLINK_NOFOLLOW = 1; +pub const AT_REMOVEDIR = 2; +pub const AT_EACCESS = 4; +pub const AT_SYMLINK_FOLLOW = 8; + +pub fn WEXITSTATUS(s: u32) u32 { + return (s & 0xff00) >> 8; +} +pub fn WTERMSIG(s: u32) u32 { + return s & 0x7f; +} +pub fn WSTOPSIG(s: u32) u32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: u32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: u32) bool { + return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; +} +pub fn WIFSIGNALED(s: u32) bool { + return (s & 0xffff) -% 1 < 0xff; +} + +pub const dirent = extern struct { + d_fileno: c_ulong, + d_namlen: u16, + d_type: u8, + d_unused1: u8, + d_unused2: u32, + d_name: [256]u8, + + pub fn reclen(self: dirent) u16 { + return (@byteOffsetOf(dirent, "d_name") + self.d_namlen + 1 + 7) & ~@as(u16, 7); + } +}; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; +pub const DT_DBF = 15; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 4; +pub const CLOCK_UPTIME = 5; +pub const CLOCK_UPTIME_PRECISE = 7; +pub const CLOCK_UPTIME_FAST = 8; +pub const CLOCK_REALTIME_PRECISE = 9; +pub const CLOCK_REALTIME_FAST = 10; +pub const CLOCK_MONOTONIC_PRECISE = 11; +pub const CLOCK_MONOTONIC_FAST = 12; +pub const CLOCK_SECOND = 13; +pub const CLOCK_THREAD_CPUTIME_ID = 14; +pub const CLOCK_PROCESS_CPUTIME_ID = 15; + +pub const sockaddr = extern struct { + sa_len: u8, + sa_family: u8, + sa_data: [14]u8, +}; + +pub const Kevent = extern struct { + ident: usize, + filter: c_short, + flags: c_ushort, + fflags: c_uint, + data: isize, + udata: usize, +}; + +pub const EVFILT_FS = -10; +pub const EVFILT_USER = -9; +pub const EVFILT_EXCEPT = -8; +pub const EVFILT_TIMER = -7; +pub const EVFILT_SIGNAL = -6; +pub const EVFILT_PROC = -5; +pub const EVFILT_VNODE = -4; +pub const EVFILT_AIO = -3; +pub const EVFILT_WRITE = -2; +pub const EVFILT_READ = -1; +pub const EVFILT_SYSCOUNT = 10; +pub const EVFILT_MARKER = 15; + +pub const EV_ADD = 1; +pub const EV_DELETE = 2; +pub const EV_ENABLE = 4; +pub const EV_DISABLE = 8; +pub const EV_ONESHOT = 16; +pub const EV_CLEAR = 32; +pub const EV_RECEIPT = 64; +pub const EV_DISPATCH = 128; +pub const EV_NODATA = 4096; +pub const EV_FLAG1 = 8192; +pub const EV_ERROR = 16384; +pub const EV_EOF = 32768; +pub const EV_SYSFLAGS = 61440; + +pub const NOTE_FFNOP = 0; +pub const NOTE_TRACK = 1; +pub const NOTE_DELETE = 1; +pub const NOTE_LOWAT = 1; +pub const NOTE_TRACKERR = 2; +pub const NOTE_OOB = 2; +pub const NOTE_WRITE = 2; +pub const NOTE_EXTEND = 4; +pub const NOTE_CHILD = 4; +pub const NOTE_ATTRIB = 8; +pub const NOTE_LINK = 16; +pub const NOTE_RENAME = 32; +pub const NOTE_REVOKE = 64; +pub const NOTE_PDATAMASK = 1048575; +pub const NOTE_FFLAGSMASK = 16777215; +pub const NOTE_TRIGGER = 16777216; +pub const NOTE_EXEC = 536870912; +pub const NOTE_FFAND = 1073741824; +pub const NOTE_FORK = 1073741824; +pub const NOTE_EXIT = 2147483648; +pub const NOTE_FFOR = 2147483648; +pub const NOTE_FFCTRLMASK = 3221225472; +pub const NOTE_FFCOPY = 3221225472; +pub const NOTE_PCTRLMASK = 4026531840; + +pub const stack_t = extern struct { + ss_sp: [*]u8, + ss_size: isize, + ss_flags: i32, +}; + +pub const S_IREAD = S_IRUSR; +pub const S_IEXEC = S_IXUSR; +pub const S_IWRITE = S_IWUSR; +pub const S_IXOTH = 1; +pub const S_IWOTH = 2; +pub const S_IROTH = 4; +pub const S_IRWXO = 7; +pub const S_IXGRP = 8; +pub const S_IWGRP = 16; +pub const S_IRGRP = 32; +pub const S_IRWXG = 56; +pub const S_IXUSR = 64; +pub const S_IWUSR = 128; +pub const S_IRUSR = 256; +pub const S_IRWXU = 448; +pub const S_ISTXT = 512; +pub const S_BLKSIZE = 512; +pub const S_ISVTX = 512; +pub const S_ISGID = 1024; +pub const S_ISUID = 2048; +pub const S_IFIFO = 4096; +pub const S_IFCHR = 8192; +pub const S_IFDIR = 16384; +pub const S_IFBLK = 24576; +pub const S_IFREG = 32768; +pub const S_IFDB = 36864; +pub const S_IFLNK = 40960; +pub const S_IFSOCK = 49152; +pub const S_IFWHT = 57344; +pub const S_IFMT = 61440; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); +pub const BADSIG = SIG_ERR; +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SIGIOT = SIGABRT; +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGTHR = 32; +pub const SIGCKPT = 33; +pub const SIGCKPTEXIT = 34; +pub const siginfo_t = extern struct { + si_signo: c_int, + si_errno: c_int, + si_code: c_int, + si_pid: c_int, + si_uid: c_uint, + si_status: c_int, + si_addr: ?*c_void, + si_value: union_sigval, + si_band: c_long, + __spare__: [7]c_int, +}; +pub const sigset_t = extern struct { + __bits: [4]c_uint, +}; +pub const sig_atomic_t = c_int; +pub const Sigaction = extern struct { + __sigaction_u: extern union { + __sa_handler: ?extern fn (c_int) void, + __sa_sigaction: ?extern fn (c_int, [*c]siginfo_t, ?*c_void) void, + }, + sa_flags: c_int, + sa_mask: sigset_t, +}; +pub const sig_t = [*c]extern fn (c_int) void; + +pub const sigvec = extern struct { + sv_handler: [*c]__sighandler_t, + sv_mask: c_int, + sv_flags: c_int, +}; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; +pub const SOCK_MAXADDRLEN = 255; +pub const SOCK_CLOEXEC = 268435456; +pub const SOCK_NONBLOCK = 536870912; + +pub const PF_INET6 = AF_INET6; +pub const PF_IMPLINK = AF_IMPLINK; +pub const PF_ROUTE = AF_ROUTE; +pub const PF_ISO = AF_ISO; +pub const PF_PIP = pseudo_AF_PIP; +pub const PF_CHAOS = AF_CHAOS; +pub const PF_DATAKIT = AF_DATAKIT; +pub const PF_INET = AF_INET; +pub const PF_APPLETALK = AF_APPLETALK; +pub const PF_SIP = AF_SIP; +pub const PF_OSI = AF_ISO; +pub const PF_CNT = AF_CNT; +pub const PF_LINK = AF_LINK; +pub const PF_HYLINK = AF_HYLINK; +pub const PF_MAX = AF_MAX; +pub const PF_KEY = pseudo_AF_KEY; +pub const PF_PUP = AF_PUP; +pub const PF_COIP = AF_COIP; +pub const PF_SNA = AF_SNA; +pub const PF_LOCAL = AF_LOCAL; +pub const PF_NETBIOS = AF_NETBIOS; +pub const PF_NATM = AF_NATM; +pub const PF_BLUETOOTH = AF_BLUETOOTH; +pub const PF_UNSPEC = AF_UNSPEC; +pub const PF_NETGRAPH = AF_NETGRAPH; +pub const PF_ECMA = AF_ECMA; +pub const PF_IPX = AF_IPX; +pub const PF_DLI = AF_DLI; +pub const PF_ATM = AF_ATM; +pub const PF_CCITT = AF_CCITT; +pub const PF_ISDN = AF_ISDN; +pub const PF_RTIP = pseudo_AF_RTIP; +pub const PF_LAT = AF_LAT; +pub const PF_UNIX = PF_LOCAL; +pub const PF_XTP = pseudo_AF_XTP; +pub const PF_DECnet = AF_DECnet; + +pub const AF_UNSPEC = 0; +pub const AF_OSI = AF_ISO; +pub const AF_UNIX = AF_LOCAL; +pub const AF_LOCAL = 1; +pub const AF_INET = 2; +pub const AF_IMPLINK = 3; +pub const AF_PUP = 4; +pub const AF_CHAOS = 5; +pub const AF_NETBIOS = 6; +pub const AF_ISO = 7; +pub const AF_ECMA = 8; +pub const AF_DATAKIT = 9; +pub const AF_CCITT = 10; +pub const AF_SNA = 11; +pub const AF_DLI = 13; +pub const AF_LAT = 14; +pub const AF_HYLINK = 15; +pub const AF_APPLETALK = 16; +pub const AF_ROUTE = 17; +pub const AF_LINK = 18; +pub const AF_COIP = 20; +pub const AF_CNT = 21; +pub const AF_IPX = 23; +pub const AF_SIP = 24; +pub const AF_ISDN = 26; +pub const AF_NATM = 29; +pub const AF_ATM = 30; +pub const AF_NETGRAPH = 32; +pub const AF_BLUETOOTH = 33; +pub const AF_MPLS = 34; +pub const AF_MAX = 36; + +pub const sa_family_t = u8; +pub const socklen_t = c_uint; +pub const sockaddr_storage = extern struct { + ss_len: u8, + ss_family: sa_family_t, + __ss_pad1: [6]u8, + __ss_align: i64, + __ss_pad2: [112]u8, +}; +pub const dl_phdr_info = extern struct { + dlpi_addr: usize, + dlpi_name: ?[*:0]const u8, + dlpi_phdr: [*]std.elf.Phdr, + dlpi_phnum: u16, +}; +pub const msghdr = extern struct { + msg_name: ?*c_void, + msg_namelen: socklen_t, + msg_iov: [*c]iovec, + msg_iovlen: c_int, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: c_int, +}; +pub const cmsghdr = extern struct { + cmsg_len: socklen_t, + cmsg_level: c_int, + cmsg_type: c_int, +}; +pub const cmsgcred = extern struct { + cmcred_pid: pid_t, + cmcred_uid: uid_t, + cmcred_euid: uid_t, + cmcred_gid: gid_t, + cmcred_ngroups: c_short, + cmcred_groups: [16]gid_t, +}; +pub const sf_hdtr = extern struct { + headers: [*c]iovec, + hdr_cnt: c_int, + trailers: [*c]iovec, + trl_cnt: c_int, +}; + +pub const MS_SYNC = 0; +pub const MS_ASYNC = 1; +pub const MS_INVALIDATE = 2; + +pub const POSIX_MADV_SEQUENTIAL = 2; +pub const POSIX_MADV_RANDOM = 1; +pub const POSIX_MADV_DONTNEED = 4; +pub const POSIX_MADV_NORMAL = 0; +pub const POSIX_MADV_WILLNEED = 3; + +pub const MADV_SEQUENTIAL = 2; +pub const MADV_CONTROL_END = MADV_SETMAP; +pub const MADV_DONTNEED = 4; +pub const MADV_RANDOM = 1; +pub const MADV_WILLNEED = 3; +pub const MADV_NORMAL = 0; +pub const MADV_CONTROL_START = MADV_INVAL; +pub const MADV_FREE = 5; +pub const MADV_NOSYNC = 6; +pub const MADV_AUTOSYNC = 7; +pub const MADV_NOCORE = 8; +pub const MADV_CORE = 9; +pub const MADV_INVAL = 10; +pub const MADV_SETMAP = 11; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_RDLCK = 1; +pub const F_SETFD = 2; +pub const F_UNLCK = 2; +pub const F_WRLCK = 3; +pub const F_GETFL = 3; +pub const F_SETFL = 4; +pub const F_GETOWN = 5; +pub const F_SETOWN = 6; +pub const F_GETLK = 7; +pub const F_SETLK = 8; +pub const F_SETLKW = 9; +pub const F_DUP2FD = 10; +pub const F_DUPFD_CLOEXEC = 17; +pub const F_DUP2FD_CLOEXEC = 18; + +pub const Flock = extern struct { + l_start: off_t, + l_len: off_t, + l_pid: pid_t, + l_type: c_short, + l_whence: c_short, +}; diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index 3d07e92e0..66540433e 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -4,6 +4,8 @@ const maxInt = std.math.maxInt; pub const fd_t = c_int; pub const pid_t = c_int; +pub const socklen_t = u32; + /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. pub const Kevent = extern struct { ident: usize, @@ -15,14 +17,35 @@ pub const Kevent = extern struct { // TODO ext }; -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; +// Modes and flags for dlopen() +// include/dlfcn.h + +/// Bind function calls lazily. +pub const RTLD_LAZY = 1; + +/// Bind function calls immediately. +pub const RTLD_NOW = 2; + +pub const RTLD_MODEMASK = 0x3; + +/// Make symbols globally available. +pub const RTLD_GLOBAL = 0x100; + +/// Opposite of RTLD_GLOBAL, and the default. +pub const RTLD_LOCAL = 0; + +/// Trace loaded objects and exit. +pub const RTLD_TRACE = 0x200; + +/// Do not remove members. +pub const RTLD_NODELETE = 0x01000; + +/// Do not load if not already loaded. +pub const RTLD_NOLOAD = 0x02000; pub const dl_phdr_info = extern struct { dlpi_addr: usize, - dlpi_name: ?[*]const u8, + dlpi_name: ?[*:0]const u8, dlpi_phdr: [*]std.elf.Phdr, dlpi_phnum: u16, }; @@ -132,33 +155,49 @@ pub const dirent = extern struct { d_namlen: u16, d_pad1: u16, d_name: [256]u8, + + pub fn reclen(self: dirent) u16 { + return self.d_reclen; + } }; pub const in_port_t = u16; -pub const sa_family_t = u16; +pub const sa_family_t = u8; -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, +pub const sockaddr = extern struct { + /// total length + len: u8, + + /// address family + family: sa_family_t, + + /// actually longer; address value + data: [14]u8, }; pub const sockaddr_in = extern struct { - len: u8, - family: sa_family_t, + len: u8 = @sizeOf(sockaddr_in), + family: sa_family_t = AF_INET, port: in_port_t, - addr: [16]u8, - zero: [8]u8, + addr: u32, + zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, }; pub const sockaddr_in6 = extern struct { - len: u8, - family: sa_family_t, + len: u8 = @sizeOf(sockaddr_in6), + family: sa_family_t = AF_INET6, port: in_port_t, flowinfo: u32, addr: [16]u8, scope_id: u32, }; +pub const sockaddr_un = extern struct { + len: u8 = @sizeOf(sockaddr_un), + family: sa_family_t = AF_UNIX, + path: [104]u8, +}; + pub const CTL_KERN = 1; pub const CTL_DEBUG = 5; @@ -289,7 +328,6 @@ pub const O_CLOEXEC = 0x00100000; pub const O_ASYNC = 0x0040; pub const O_DIRECT = 0x00010000; -pub const O_LARGEFILE = 0; pub const O_NOATIME = 0o1000000; pub const O_PATH = 0o10000000; pub const O_TMPFILE = 0o20200000; @@ -332,134 +370,93 @@ pub const SOCK_SEQPACKET = 5; pub const SOCK_CLOEXEC = 0x10000000; pub const SOCK_NONBLOCK = 0x20000000; -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; +pub const PF_UNSPEC = AF_UNSPEC; +pub const PF_LOCAL = AF_LOCAL; pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_AX25 = 3; -pub const PF_IPX = 4; -pub const PF_APPLETALK = 5; -pub const PF_NETROM = 6; -pub const PF_BRIDGE = 7; -pub const PF_ATMPVC = 8; -pub const PF_X25 = 9; -pub const PF_INET6 = 10; -pub const PF_ROSE = 11; -pub const PF_DECnet = 12; -pub const PF_NETBEUI = 13; -pub const PF_SECURITY = 14; -pub const PF_KEY = 15; -pub const PF_NETLINK = 16; -pub const PF_ROUTE = PF_NETLINK; -pub const PF_PACKET = 17; -pub const PF_ASH = 18; -pub const PF_ECONET = 19; -pub const PF_ATMSVC = 20; -pub const PF_RDS = 21; -pub const PF_SNA = 22; -pub const PF_IRDA = 23; -pub const PF_PPPOX = 24; -pub const PF_WANPIPE = 25; -pub const PF_LLC = 26; -pub const PF_IB = 27; -pub const PF_MPLS = 28; -pub const PF_CAN = 29; -pub const PF_TIPC = 30; -pub const PF_BLUETOOTH = 31; -pub const PF_IUCV = 32; -pub const PF_RXRPC = 33; -pub const PF_ISDN = 34; -pub const PF_PHONET = 35; -pub const PF_IEEE802154 = 36; -pub const PF_CAIF = 37; -pub const PF_ALG = 38; -pub const PF_NFC = 39; -pub const PF_VSOCK = 40; -pub const PF_MAX = 41; +pub const PF_INET = AF_INET; +pub const PF_IMPLINK = AF_IMPLINK; +pub const PF_PUP = AF_PUP; +pub const PF_CHAOS = AF_CHAOS; +pub const PF_NETBIOS = AF_NETBIOS; +pub const PF_ISO = AF_ISO; +pub const PF_OSI = AF_ISO; +pub const PF_ECMA = AF_ECMA; +pub const PF_DATAKIT = AF_DATAKIT; +pub const PF_CCITT = AF_CCITT; +pub const PF_DECnet = AF_DECnet; +pub const PF_DLI = AF_DLI; +pub const PF_LAT = AF_LAT; +pub const PF_HYLINK = AF_HYLINK; +pub const PF_APPLETALK = AF_APPLETALK; +pub const PF_ROUTE = AF_ROUTE; +pub const PF_LINK = AF_LINK; +pub const PF_XTP = pseudo_AF_XTP; +pub const PF_COIP = AF_COIP; +pub const PF_CNT = AF_CNT; +pub const PF_SIP = AF_SIP; +pub const PF_IPX = AF_IPX; +pub const PF_RTIP = pseudo_AF_RTIP; +pub const PF_PIP = psuedo_AF_PIP; +pub const PF_ISDN = AF_ISDN; +pub const PF_KEY = pseudo_AF_KEY; +pub const PF_INET6 = pseudo_AF_INET6; +pub const PF_NATM = AF_NATM; +pub const PF_ATM = AF_ATM; +pub const PF_NETGRAPH = AF_NETGRAPH; +pub const PF_SLOW = AF_SLOW; +pub const PF_SCLUSTER = AF_SCLUSTER; +pub const PF_ARP = AF_ARP; +pub const PF_BLUETOOTH = AF_BLUETOOTH; +pub const PF_IEEE80211 = AF_IEE80211; +pub const PF_INET_SDP = AF_INET_SDP; +pub const PF_INET6_SDP = AF_INET6_SDP; +pub const PF_MAX = AF_MAX; -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; +pub const AF_UNSPEC = 0; +pub const AF_UNIX = 1; +pub const AF_LOCAL = AF_UNIX; pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_AX25 = PF_AX25; -pub const AF_IPX = PF_IPX; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_NETROM = PF_NETROM; -pub const AF_BRIDGE = PF_BRIDGE; -pub const AF_ATMPVC = PF_ATMPVC; -pub const AF_X25 = PF_X25; -pub const AF_INET6 = PF_INET6; -pub const AF_ROSE = PF_ROSE; -pub const AF_DECnet = PF_DECnet; -pub const AF_NETBEUI = PF_NETBEUI; -pub const AF_SECURITY = PF_SECURITY; -pub const AF_KEY = PF_KEY; -pub const AF_NETLINK = PF_NETLINK; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_PACKET = PF_PACKET; -pub const AF_ASH = PF_ASH; -pub const AF_ECONET = PF_ECONET; -pub const AF_ATMSVC = PF_ATMSVC; -pub const AF_RDS = PF_RDS; -pub const AF_SNA = PF_SNA; -pub const AF_IRDA = PF_IRDA; -pub const AF_PPPOX = PF_PPPOX; -pub const AF_WANPIPE = PF_WANPIPE; -pub const AF_LLC = PF_LLC; -pub const AF_IB = PF_IB; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_TIPC = PF_TIPC; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_IUCV = PF_IUCV; -pub const AF_RXRPC = PF_RXRPC; -pub const AF_ISDN = PF_ISDN; -pub const AF_PHONET = PF_PHONET; -pub const AF_IEEE802154 = PF_IEEE802154; -pub const AF_CAIF = PF_CAIF; -pub const AF_ALG = PF_ALG; -pub const AF_NFC = PF_NFC; -pub const AF_VSOCK = PF_VSOCK; -pub const AF_MAX = PF_MAX; +pub const AF_INET = 2; +pub const AF_IMPLINK = 3; +pub const AF_PUP = 4; +pub const AF_CHAOS = 5; +pub const AF_NETBIOS = 6; +pub const AF_ISO = 7; +pub const AF_OSI = AF_ISO; +pub const AF_ECMA = 8; +pub const AF_DATAKIT = 9; +pub const AF_CCITT = 10; +pub const AF_SNA = 11; +pub const AF_DECnet = 12; +pub const AF_DLI = 13; +pub const AF_LAT = 14; +pub const AF_HYLINK = 15; +pub const AF_APPLETALK = 16; +pub const AF_ROUTE = 17; +pub const AF_LINK = 18; +pub const pseudo_AF_XTP = 19; +pub const AF_COIP = 20; +pub const AF_CNT = 21; +pub const pseudo_AF_RTIP = 22; +pub const AF_IPX = 23; +pub const AF_SIP = 24; +pub const pseudo_AF_PIP = 25; +pub const AF_ISDN = 26; +pub const AF_E164 = AF_ISDN; +pub const pseudo_AF_KEY = 27; +pub const AF_INET6 = 28; +pub const AF_NATM = 29; +pub const AF_ATM = 30; +pub const pseudo_AF_HDRCMPLT = 31; +pub const AF_NETGRAPH = 32; +pub const AF_SLOW = 33; +pub const AF_SCLUSTER = 34; +pub const AF_ARP = 35; +pub const AF_BLUETOOTH = 36; +pub const AF_IEEE80211 = 37; +pub const AF_INET_SDP = 38; +pub const AF_INET6_SDP = 39; +pub const AF_MAX = 42; pub const DT_UNKNOWN = 0; pub const DT_FIFO = 1; @@ -939,3 +936,382 @@ pub fn S_IWHT(m: u32) bool { } pub const HOST_NAME_MAX = 255; + +/// Magic value that specify the use of the current working directory +/// to determine the target of relative file paths in the openat() and +/// similar syscalls. +pub const AT_FDCWD = -100; + +/// Check access using effective user and group ID +pub const AT_EACCESS = 0x0100; + +/// Do not follow symbolic links +pub const AT_SYMLINK_NOFOLLOW = 0x0200; + +/// Follow symbolic link +pub const AT_SYMLINK_FOLLOW = 0x0400; + +/// Remove directory instead of file +pub const AT_REMOVEDIR = 0x0800; + +pub const addrinfo = extern struct { + flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + canonname: ?[*:0]u8, + addr: ?*sockaddr, + next: ?*addrinfo, +}; + +/// Fail if not under dirfd +pub const AT_BENEATH = 0x1000; + +/// dummy for IP +pub const IPPROTO_IP = 0; + +/// control message protocol +pub const IPPROTO_ICMP = 1; + +/// tcp +pub const IPPROTO_TCP = 6; + +/// user datagram protocol +pub const IPPROTO_UDP = 17; + +/// IP6 header +pub const IPPROTO_IPV6 = 41; + +/// raw IP packet +pub const IPPROTO_RAW = 255; + +/// IP6 hop-by-hop options +pub const IPPROTO_HOPOPTS = 0; + +/// group mgmt protocol +pub const IPPROTO_IGMP = 2; + +/// gateway^2 (deprecated) +pub const IPPROTO_GGP = 3; + +/// IPv4 encapsulation +pub const IPPROTO_IPV4 = 4; + +/// for compatibility +pub const IPPROTO_IPIP = IPPROTO_IPV4; + +/// Stream protocol II +pub const IPPROTO_ST = 7; + +/// exterior gateway protocol +pub const IPPROTO_EGP = 8; + +/// private interior gateway +pub const IPPROTO_PIGP = 9; + +/// BBN RCC Monitoring +pub const IPPROTO_RCCMON = 10; + +/// network voice protocol +pub const IPPROTO_NVPII = 11; + +/// pup +pub const IPPROTO_PUP = 12; + +/// Argus +pub const IPPROTO_ARGUS = 13; + +/// EMCON +pub const IPPROTO_EMCON = 14; + +/// Cross Net Debugger +pub const IPPROTO_XNET = 15; + +/// Chaos +pub const IPPROTO_CHAOS = 16; + +/// Multiplexing +pub const IPPROTO_MUX = 18; + +/// DCN Measurement Subsystems +pub const IPPROTO_MEAS = 19; + +/// Host Monitoring +pub const IPPROTO_HMP = 20; + +/// Packet Radio Measurement +pub const IPPROTO_PRM = 21; + +/// xns idp +pub const IPPROTO_IDP = 22; + +/// Trunk-1 +pub const IPPROTO_TRUNK1 = 23; + +/// Trunk-2 +pub const IPPROTO_TRUNK2 = 24; + +/// Leaf-1 +pub const IPPROTO_LEAF1 = 25; + +/// Leaf-2 +pub const IPPROTO_LEAF2 = 26; + +/// Reliable Data +pub const IPPROTO_RDP = 27; + +/// Reliable Transaction +pub const IPPROTO_IRTP = 28; + +/// tp-4 w/ class negotiation +pub const IPPROTO_TP = 29; + +/// Bulk Data Transfer +pub const IPPROTO_BLT = 30; + +/// Network Services +pub const IPPROTO_NSP = 31; + +/// Merit Internodal +pub const IPPROTO_INP = 32; + +/// Datagram Congestion Control Protocol +pub const IPPROTO_DCCP = 33; + +/// Third Party Connect +pub const IPPROTO_3PC = 34; + +/// InterDomain Policy Routing +pub const IPPROTO_IDPR = 35; + +/// XTP +pub const IPPROTO_XTP = 36; + +/// Datagram Delivery +pub const IPPROTO_DDP = 37; + +/// Control Message Transport +pub const IPPROTO_CMTP = 38; + +/// TP++ Transport +pub const IPPROTO_TPXX = 39; + +/// IL transport protocol +pub const IPPROTO_IL = 40; + +/// Source Demand Routing +pub const IPPROTO_SDRP = 42; + +/// IP6 routing header +pub const IPPROTO_ROUTING = 43; + +/// IP6 fragmentation header +pub const IPPROTO_FRAGMENT = 44; + +/// InterDomain Routing +pub const IPPROTO_IDRP = 45; + +/// resource reservation +pub const IPPROTO_RSVP = 46; + +/// General Routing Encap. +pub const IPPROTO_GRE = 47; + +/// Mobile Host Routing +pub const IPPROTO_MHRP = 48; + +/// BHA +pub const IPPROTO_BHA = 49; + +/// IP6 Encap Sec. Payload +pub const IPPROTO_ESP = 50; + +/// IP6 Auth Header +pub const IPPROTO_AH = 51; + +/// Integ. Net Layer Security +pub const IPPROTO_INLSP = 52; + +/// IP with encryption +pub const IPPROTO_SWIPE = 53; + +/// Next Hop Resolution +pub const IPPROTO_NHRP = 54; + +/// IP Mobility +pub const IPPROTO_MOBILE = 55; + +/// Transport Layer Security +pub const IPPROTO_TLSP = 56; + +/// SKIP +pub const IPPROTO_SKIP = 57; + +/// ICMP6 +pub const IPPROTO_ICMPV6 = 58; + +/// IP6 no next header +pub const IPPROTO_NONE = 59; + +/// IP6 destination option +pub const IPPROTO_DSTOPTS = 60; + +/// any host internal protocol +pub const IPPROTO_AHIP = 61; + +/// CFTP +pub const IPPROTO_CFTP = 62; + +/// "hello" routing protocol +pub const IPPROTO_HELLO = 63; + +/// SATNET/Backroom EXPAK +pub const IPPROTO_SATEXPAK = 64; + +/// Kryptolan +pub const IPPROTO_KRYPTOLAN = 65; + +/// Remote Virtual Disk +pub const IPPROTO_RVD = 66; + +/// Pluribus Packet Core +pub const IPPROTO_IPPC = 67; + +/// Any distributed FS +pub const IPPROTO_ADFS = 68; + +/// Satnet Monitoring +pub const IPPROTO_SATMON = 69; + +/// VISA Protocol +pub const IPPROTO_VISA = 70; + +/// Packet Core Utility +pub const IPPROTO_IPCV = 71; + +/// Comp. Prot. Net. Executive +pub const IPPROTO_CPNX = 72; + +/// Comp. Prot. HeartBeat +pub const IPPROTO_CPHB = 73; + +/// Wang Span Network +pub const IPPROTO_WSN = 74; + +/// Packet Video Protocol +pub const IPPROTO_PVP = 75; + +/// BackRoom SATNET Monitoring +pub const IPPROTO_BRSATMON = 76; + +/// Sun net disk proto (temp.) +pub const IPPROTO_ND = 77; + +/// WIDEBAND Monitoring +pub const IPPROTO_WBMON = 78; + +/// WIDEBAND EXPAK +pub const IPPROTO_WBEXPAK = 79; + +/// ISO cnlp +pub const IPPROTO_EON = 80; + +/// VMTP +pub const IPPROTO_VMTP = 81; + +/// Secure VMTP +pub const IPPROTO_SVMTP = 82; + +/// Banyon VINES +pub const IPPROTO_VINES = 83; + +/// TTP +pub const IPPROTO_TTP = 84; + +/// NSFNET-IGP +pub const IPPROTO_IGP = 85; + +/// dissimilar gateway prot. +pub const IPPROTO_DGP = 86; + +/// TCF +pub const IPPROTO_TCF = 87; + +/// Cisco/GXS IGRP +pub const IPPROTO_IGRP = 88; + +/// OSPFIGP +pub const IPPROTO_OSPFIGP = 89; + +/// Strite RPC protocol +pub const IPPROTO_SRPC = 90; + +/// Locus Address Resoloution +pub const IPPROTO_LARP = 91; + +/// Multicast Transport +pub const IPPROTO_MTP = 92; + +/// AX.25 Frames +pub const IPPROTO_AX25 = 93; + +/// IP encapsulated in IP +pub const IPPROTO_IPEIP = 94; + +/// Mobile Int.ing control +pub const IPPROTO_MICP = 95; + +/// Semaphore Comm. security +pub const IPPROTO_SCCSP = 96; + +/// Ethernet IP encapsulation +pub const IPPROTO_ETHERIP = 97; + +/// encapsulation header +pub const IPPROTO_ENCAP = 98; + +/// any private encr. scheme +pub const IPPROTO_APES = 99; + +/// GMTP +pub const IPPROTO_GMTP = 100; + +/// payload compression (IPComp) +pub const IPPROTO_IPCOMP = 108; + +/// SCTP +pub const IPPROTO_SCTP = 132; + +/// IPv6 Mobility Header +pub const IPPROTO_MH = 135; + +/// UDP-Lite +pub const IPPROTO_UDPLITE = 136; + +/// IP6 Host Identity Protocol +pub const IPPROTO_HIP = 139; + +/// IP6 Shim6 Protocol +pub const IPPROTO_SHIM6 = 140; + +/// Protocol Independent Mcast +pub const IPPROTO_PIM = 103; + +/// CARP +pub const IPPROTO_CARP = 112; + +/// PGM +pub const IPPROTO_PGM = 113; + +/// MPLS-in-IP +pub const IPPROTO_MPLS = 137; + +/// PFSYNC +pub const IPPROTO_PFSYNC = 240; + +/// Reserved +pub const IPPROTO_RESERVED_253 = 253; + +/// Reserved +pub const IPPROTO_RESERVED_254 = 254; diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 5ed5c0b62..51ea49005 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -9,6 +9,7 @@ pub usingnamespace switch (builtin.arch) { }; pub usingnamespace switch (builtin.arch) { + .i386 => @import("linux/i386.zig"), .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), .arm => @import("linux/arm-eabi.zig"), @@ -25,6 +26,7 @@ pub const uid_t = i32; pub const gid_t = u32; pub const clock_t = isize; +pub const NAME_MAX = 255; pub const PATH_MAX = 4096; pub const IOV_MAX = 1024; @@ -227,43 +229,6 @@ pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - pub const SHUT_RD = 0; pub const SHUT_WR = 1; pub const SHUT_RDWR = 2; @@ -596,10 +561,10 @@ pub const EPOLLMSG = 0x400; pub const EPOLLERR = 0x008; pub const EPOLLHUP = 0x010; pub const EPOLLRDHUP = 0x2000; -pub const EPOLLEXCLUSIVE = (u32(1) << 28); -pub const EPOLLWAKEUP = (u32(1) << 29); -pub const EPOLLONESHOT = (u32(1) << 30); -pub const EPOLLET = (u32(1) << 31); +pub const EPOLLEXCLUSIVE = (@as(u32, 1) << 28); +pub const EPOLLWAKEUP = (@as(u32, 1) << 29); +pub const EPOLLONESHOT = (@as(u32, 1) << 30); +pub const EPOLLET = (@as(u32, 1) << 31); pub const CLOCK_REALTIME = 0; pub const CLOCK_MONOTONIC = 1; @@ -800,19 +765,14 @@ pub const winsize = extern struct { ws_ypixel: u16, }; +/// NSIG is the total number of signals defined. +/// As signal numbers are sequential, NSIG is one greater than the largest defined signal number. pub const NSIG = if (is_mips) 128 else 65; -pub const sigset_t = [128 / @sizeOf(usize)]usize; -pub usingnamespace if (NSIG == 65) - struct { - pub const all_mask = [2]u32{ 0xffffffff, 0xffffffff }; - pub const app_mask = [2]u32{ 0xfffffffc, 0x7fffffff }; - } -else - struct { - pub const all_mask = [4]u32{ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; - pub const app_mask = [4]u32{ 0xfffffffc, 0x7fffffff, 0xffffffff, 0xffffffff }; - }; +pub const sigset_t = [1024 / 32]u32; + +pub const all_mask: sigset_t = [_]u32{0xffffffff} ** sigset_t.len; +pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30; pub const k_sigaction = if (is_mips) extern struct { @@ -840,36 +800,37 @@ pub const Sigaction = extern struct { pub const SIG_ERR = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, maxInt(usize)); pub const SIG_DFL = @intToPtr(?extern fn (i32, *siginfo_t, *c_void) void, 0); pub const SIG_IGN = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, 1); -pub const empty_sigset = [_]usize{0} ** sigset_t.len; +pub const empty_sigset = [_]u32{0} ** sigset_t.len; pub const in_port_t = u16; pub const sa_family_t = u16; pub const socklen_t = u32; -/// This intentionally only has ip4 and ip6 -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, - un: sockaddr_un, +pub const sockaddr = extern struct { + family: sa_family_t, + data: [14]u8, }; +/// IPv4 socket address pub const sockaddr_in = extern struct { - family: sa_family_t, + family: sa_family_t = AF_INET, port: in_port_t, addr: u32, - zero: [8]u8, + zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, }; +/// IPv6 socket address pub const sockaddr_in6 = extern struct { - family: sa_family_t, + family: sa_family_t = AF_INET6, port: in_port_t, flowinfo: u32, addr: [16]u8, scope_id: u32, }; +/// UNIX domain socket address pub const sockaddr_un = extern struct { - family: sa_family_t, + family: sa_family_t = AF_UNIX, path: [108]u8, }; @@ -986,7 +947,7 @@ pub fn cap_valid(u8: x) bool { } pub fn CAP_TO_MASK(cap: u8) u32 { - return u32(1) << u5(cap & 31); + return @as(u32, 1) << @intCast(u5, cap & 31); } pub fn CAP_TO_INDEX(cap: u8) u8 { @@ -1023,20 +984,19 @@ pub const dirent64 = extern struct { d_reclen: u16, d_type: u8, d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 + + pub fn reclen(self: dirent64) u16 { + return self.d_reclen; + } }; pub const dl_phdr_info = extern struct { dlpi_addr: usize, - dlpi_name: ?[*]const u8, + dlpi_name: ?[*:0]const u8, dlpi_phdr: [*]std.elf.Phdr, dlpi_phnum: u16, }; -pub const pthread_attr_t = extern struct { - __size: [56]u8, - __align: c_long, -}; - pub const CPU_SETSIZE = 128; pub const cpu_set_t = [CPU_SETSIZE / @sizeOf(usize)]usize; pub const cpu_count_t = @IntType(false, std.math.log2(CPU_SETSIZE * 8)); @@ -1151,11 +1111,16 @@ pub const io_uring_params = extern struct { flags: u32, sq_thread_cpu: u32, sq_thread_idle: u32, - resv: [5]u32, + features: u32, + resv: [4]u32, sq_off: io_sqring_offsets, cq_off: io_cqring_offsets, }; +// io_uring_params.features flags + +pub const IORING_FEAT_SINGLE_MMAP = 1 << 0; + // io_uring_params.flags /// io_context is polled @@ -1222,6 +1187,7 @@ pub const io_uring_sqe = extern struct { poll_events: u16, sync_range_flags: u32, msg_flags: u32, + timeout_flags: u32, }; union1: union1, user_data: u64, @@ -1254,6 +1220,7 @@ pub const IORING_OP_POLL_REMOVE = 7; pub const IORING_OP_SYNC_FILE_RANGE = 8; pub const IORING_OP_SENDMSG = 9; pub const IORING_OP_RECVMSG = 10; +pub const IORING_OP_TIMEOUT = 11; // io_uring_sqe.fsync_flags pub const IORING_FSYNC_DATASYNC = (1 << 0); @@ -1293,3 +1260,220 @@ pub const utsname = extern struct { domainname: [65]u8, }; pub const HOST_NAME_MAX = 64; + +pub const STATX_TYPE = 0x0001; +pub const STATX_MODE = 0x0002; +pub const STATX_NLINK = 0x0004; +pub const STATX_UID = 0x0008; +pub const STATX_GID = 0x0010; +pub const STATX_ATIME = 0x0020; +pub const STATX_MTIME = 0x0040; +pub const STATX_CTIME = 0x0080; +pub const STATX_INO = 0x0100; +pub const STATX_SIZE = 0x0200; +pub const STATX_BLOCKS = 0x0400; +pub const STATX_BASIC_STATS = 0x07ff; + +pub const STATX_BTIME = 0x0800; + +pub const STATX_ATTR_COMPRESSED = 0x0004; +pub const STATX_ATTR_IMMUTABLE = 0x0010; +pub const STATX_ATTR_APPEND = 0x0020; +pub const STATX_ATTR_NODUMP = 0x0040; +pub const STATX_ATTR_ENCRYPTED = 0x0800; +pub const STATX_ATTR_AUTOMOUNT = 0x1000; + +pub const statx_timestamp = extern struct { + tv_sec: i64, + tv_nsec: u32, + __pad1: u32, +}; + +/// Renamed to `Statx` to not conflict with the `statx` function. +pub const Statx = extern struct { + /// Mask of bits indicating filled fields + mask: u32, + + /// Block size for filesystem I/O + blksize: u32, + + /// Extra file attribute indicators + attributes: u64, + + /// Number of hard links + nlink: u32, + + /// User ID of owner + uid: u32, + + /// Group ID of owner + gid: u32, + + /// File type and mode + mode: u16, + __pad1: u16, + + /// Inode number + ino: u64, + + /// Total size in bytes + size: u64, + + /// Number of 512B blocks allocated + blocks: u64, + + /// Mask to show what's supported in `attributes`. + attributes_mask: u64, + + /// Last access file timestamp + atime: statx_timestamp, + + /// Creation file timestamp + btime: statx_timestamp, + + /// Last status change file timestamp + ctime: statx_timestamp, + + /// Last modification file timestamp + mtime: statx_timestamp, + + /// Major ID, if this file represents a device. + rdev_major: u32, + + /// Minor ID, if this file represents a device. + rdev_minor: u32, + + /// Major ID of the device containing the filesystem where this file resides. + dev_major: u32, + + /// Minor ID of the device containing the filesystem where this file resides. + dev_minor: u32, + + __pad2: [14]u64, +}; + +pub const addrinfo = extern struct { + flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + addr: ?*sockaddr, + canonname: ?[*:0]u8, + next: ?*addrinfo, +}; + +pub const IPPORT_RESERVED = 1024; + +pub const IPPROTO_IP = 0; +pub const IPPROTO_HOPOPTS = 0; +pub const IPPROTO_ICMP = 1; +pub const IPPROTO_IGMP = 2; +pub const IPPROTO_IPIP = 4; +pub const IPPROTO_TCP = 6; +pub const IPPROTO_EGP = 8; +pub const IPPROTO_PUP = 12; +pub const IPPROTO_UDP = 17; +pub const IPPROTO_IDP = 22; +pub const IPPROTO_TP = 29; +pub const IPPROTO_DCCP = 33; +pub const IPPROTO_IPV6 = 41; +pub const IPPROTO_ROUTING = 43; +pub const IPPROTO_FRAGMENT = 44; +pub const IPPROTO_RSVP = 46; +pub const IPPROTO_GRE = 47; +pub const IPPROTO_ESP = 50; +pub const IPPROTO_AH = 51; +pub const IPPROTO_ICMPV6 = 58; +pub const IPPROTO_NONE = 59; +pub const IPPROTO_DSTOPTS = 60; +pub const IPPROTO_MTP = 92; +pub const IPPROTO_BEETPH = 94; +pub const IPPROTO_ENCAP = 98; +pub const IPPROTO_PIM = 103; +pub const IPPROTO_COMP = 108; +pub const IPPROTO_SCTP = 132; +pub const IPPROTO_MH = 135; +pub const IPPROTO_UDPLITE = 136; +pub const IPPROTO_MPLS = 137; +pub const IPPROTO_RAW = 255; +pub const IPPROTO_MAX = 256; + +pub const RR_A = 1; +pub const RR_CNAME = 5; +pub const RR_AAAA = 28; + +pub const nfds_t = usize; +pub const pollfd = extern struct { + fd: fd_t, + events: i16, + revents: i16, +}; + +pub const POLLIN = 0x001; +pub const POLLPRI = 0x002; +pub const POLLOUT = 0x004; +pub const POLLERR = 0x008; +pub const POLLHUP = 0x010; +pub const POLLNVAL = 0x020; +pub const POLLRDNORM = 0x040; +pub const POLLRDBAND = 0x080; + +pub const MFD_CLOEXEC = 0x0001; +pub const MFD_ALLOW_SEALING = 0x0002; +pub const MFD_HUGETLB = 0x0004; +pub const MFD_ALL_FLAGS = MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB; + +pub const HUGETLB_FLAG_ENCODE_SHIFT = 26; +pub const HUGETLB_FLAG_ENCODE_MASK = 0x3f; +pub const HUGETLB_FLAG_ENCODE_64KB = 16 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_512KB = 19 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_1MB = 20 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_2MB = 21 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_8MB = 23 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_16MB = 24 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_32MB = 25 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_256MB = 28 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_512MB = 29 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_1GB = 30 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_2GB = 31 << HUGETLB_FLAG_ENCODE_SHIFT; +pub const HUGETLB_FLAG_ENCODE_16GB = 34 << HUGETLB_FLAG_ENCODE_SHIFT; + +pub const MFD_HUGE_SHIFT = HUGETLB_FLAG_ENCODE_SHIFT; +pub const MFD_HUGE_MASK = HUGETLB_FLAG_ENCODE_MASK; +pub const MFD_HUGE_64KB = HUGETLB_FLAG_ENCODE_64KB; +pub const MFD_HUGE_512KB = HUGETLB_FLAG_ENCODE_512KB; +pub const MFD_HUGE_1MB = HUGETLB_FLAG_ENCODE_1MB; +pub const MFD_HUGE_2MB = HUGETLB_FLAG_ENCODE_2MB; +pub const MFD_HUGE_8MB = HUGETLB_FLAG_ENCODE_8MB; +pub const MFD_HUGE_16MB = HUGETLB_FLAG_ENCODE_16MB; +pub const MFD_HUGE_32MB = HUGETLB_FLAG_ENCODE_32MB; +pub const MFD_HUGE_256MB = HUGETLB_FLAG_ENCODE_256MB; +pub const MFD_HUGE_512MB = HUGETLB_FLAG_ENCODE_512MB; +pub const MFD_HUGE_1GB = HUGETLB_FLAG_ENCODE_1GB; +pub const MFD_HUGE_2GB = HUGETLB_FLAG_ENCODE_2GB; +pub const MFD_HUGE_16GB = HUGETLB_FLAG_ENCODE_16GB; + +pub const RUSAGE_SELF = 0; +pub const RUSAGE_CHILDREN = -1; +pub const RUSAGE_THREAD = 1; + +pub const rusage = extern struct { + utime: timeval, + stime: timeval, + maxrss: isize, + ix_rss: isize, + idrss: isize, + isrss: isize, + minflt: isize, + majflt: isize, + nswap: isize, + inblock: isize, + oublock: isize, + msgsnd: isize, + msgrcv: isize, + nsignals: isize, + nvcsw: isize, + nivcsw: isize, + __reserved: [16]isize = [1]isize{0} ** 16, +}; diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig index ae169684f..de71607c3 100644 --- a/lib/std/os/bits/linux/arm-eabi.zig +++ b/lib/std/os/bits/linux/arm-eabi.zig @@ -6,6 +6,8 @@ const iovec = linux.iovec; const iovec_const = linux.iovec_const; const stack_t = linux.stack_t; const sigset_t = linux.sigset_t; +const uid_t = linux.uid_t; +const gid_t = linux.gid_t; pub const SYS_restart_syscall = 0; pub const SYS_exit = 1; @@ -464,7 +466,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6"; @@ -512,7 +513,14 @@ pub const msghdr_const = extern struct { msg_flags: i32, }; +pub const blksize_t = i32; +pub const nlink_t = u32; +pub const time_t = isize; +pub const mode_t = u32; pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; /// Renamed to Stat to not conflict with the stat function. /// atime, mtime, and ctime have functions to return `timespec`, @@ -521,22 +529,22 @@ pub const off_t = i64; /// in C, macros are used to hide the differences. Here we use /// methods to accomplish this. pub const Stat = extern struct { - dev: u64, + dev: dev_t, __dev_padding: u32, __ino_truncated: u32, - mode: u32, - nlink: u32, - uid: u32, - gid: u32, - rdev: u64, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, __rdev_padding: u32, size: off_t, - blksize: i32, - blocks: u64, + blksize: blksize_t, + blocks: blkcnt_t, atim: timespec, mtim: timespec, ctim: timespec, - ino: u64, + ino: ino_t, pub fn atime(self: Stat) timespec { return self.atim; diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig index 84f621b07..8dcebc5dd 100644 --- a/lib/std/os/bits/linux/arm64.zig +++ b/lib/std/os/bits/linux/arm64.zig @@ -358,7 +358,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6.39"; diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig new file mode 100644 index 000000000..2585785b1 --- /dev/null +++ b/lib/std/os/bits/linux/i386.zig @@ -0,0 +1,642 @@ +// i386-specific declarations that are intended to be imported into the POSIX namespace. +// This does include Linux-only APIs. + +const std = @import("../../../std.zig"); +const linux = std.os.linux; +const socklen_t = linux.socklen_t; +const iovec = linux.iovec; +const iovec_const = linux.iovec_const; +const uid_t = linux.uid_t; +const gid_t = linux.gid_t; +const stack_t = linux.stack_t; +const sigset_t = linux.sigset_t; + +pub const SYS_restart_syscall = 0; +pub const SYS_exit = 1; +pub const SYS_fork = 2; +pub const SYS_read = 3; +pub const SYS_write = 4; +pub const SYS_open = 5; +pub const SYS_close = 6; +pub const SYS_waitpid = 7; +pub const SYS_creat = 8; +pub const SYS_link = 9; +pub const SYS_unlink = 10; +pub const SYS_execve = 11; +pub const SYS_chdir = 12; +pub const SYS_time = 13; +pub const SYS_mknod = 14; +pub const SYS_chmod = 15; +pub const SYS_lchown = 16; +pub const SYS_break = 17; +pub const SYS_oldstat = 18; +pub const SYS_lseek = 19; +pub const SYS_getpid = 20; +pub const SYS_mount = 21; +pub const SYS_umount = 22; +pub const SYS_setuid = 23; +pub const SYS_getuid = 24; +pub const SYS_stime = 25; +pub const SYS_ptrace = 26; +pub const SYS_alarm = 27; +pub const SYS_oldfstat = 28; +pub const SYS_pause = 29; +pub const SYS_utime = 30; +pub const SYS_stty = 31; +pub const SYS_gtty = 32; +pub const SYS_access = 33; +pub const SYS_nice = 34; +pub const SYS_ftime = 35; +pub const SYS_sync = 36; +pub const SYS_kill = 37; +pub const SYS_rename = 38; +pub const SYS_mkdir = 39; +pub const SYS_rmdir = 40; +pub const SYS_dup = 41; +pub const SYS_pipe = 42; +pub const SYS_times = 43; +pub const SYS_prof = 44; +pub const SYS_brk = 45; +pub const SYS_setgid = 46; +pub const SYS_getgid = 47; +pub const SYS_signal = 48; +pub const SYS_geteuid = 49; +pub const SYS_getegid = 50; +pub const SYS_acct = 51; +pub const SYS_umount2 = 52; +pub const SYS_lock = 53; +pub const SYS_ioctl = 54; +pub const SYS_fcntl = 55; +pub const SYS_mpx = 56; +pub const SYS_setpgid = 57; +pub const SYS_ulimit = 58; +pub const SYS_oldolduname = 59; +pub const SYS_umask = 60; +pub const SYS_chroot = 61; +pub const SYS_ustat = 62; +pub const SYS_dup2 = 63; +pub const SYS_getppid = 64; +pub const SYS_getpgrp = 65; +pub const SYS_setsid = 66; +pub const SYS_sigaction = 67; +pub const SYS_sgetmask = 68; +pub const SYS_ssetmask = 69; +pub const SYS_setreuid = 70; +pub const SYS_setregid = 71; +pub const SYS_sigsuspend = 72; +pub const SYS_sigpending = 73; +pub const SYS_sethostname = 74; +pub const SYS_setrlimit = 75; +pub const SYS_getrlimit = 76; +pub const SYS_getrusage = 77; +pub const SYS_gettimeofday = 78; +pub const SYS_settimeofday = 79; +pub const SYS_getgroups = 80; +pub const SYS_setgroups = 81; +pub const SYS_select = 82; +pub const SYS_symlink = 83; +pub const SYS_oldlstat = 84; +pub const SYS_readlink = 85; +pub const SYS_uselib = 86; +pub const SYS_swapon = 87; +pub const SYS_reboot = 88; +pub const SYS_readdir = 89; +pub const SYS_mmap = 90; +pub const SYS_munmap = 91; +pub const SYS_truncate = 92; +pub const SYS_ftruncate = 93; +pub const SYS_fchmod = 94; +pub const SYS_fchown = 95; +pub const SYS_getpriority = 96; +pub const SYS_setpriority = 97; +pub const SYS_profil = 98; +pub const SYS_statfs = 99; +pub const SYS_fstatfs = 100; +pub const SYS_ioperm = 101; +pub const SYS_socketcall = 102; +pub const SYS_syslog = 103; +pub const SYS_setitimer = 104; +pub const SYS_getitimer = 105; +pub const SYS_stat = 106; +pub const SYS_lstat = 107; +pub const SYS_fstat = 108; +pub const SYS_olduname = 109; +pub const SYS_iopl = 110; +pub const SYS_vhangup = 111; +pub const SYS_idle = 112; +pub const SYS_vm86old = 113; +pub const SYS_wait4 = 114; +pub const SYS_swapoff = 115; +pub const SYS_sysinfo = 116; +pub const SYS_ipc = 117; +pub const SYS_fsync = 118; +pub const SYS_sigreturn = 119; +pub const SYS_clone = 120; +pub const SYS_setdomainname = 121; +pub const SYS_uname = 122; +pub const SYS_modify_ldt = 123; +pub const SYS_adjtimex = 124; +pub const SYS_mprotect = 125; +pub const SYS_sigprocmask = 126; +pub const SYS_create_module = 127; +pub const SYS_init_module = 128; +pub const SYS_delete_module = 129; +pub const SYS_get_kernel_syms = 130; +pub const SYS_quotactl = 131; +pub const SYS_getpgid = 132; +pub const SYS_fchdir = 133; +pub const SYS_bdflush = 134; +pub const SYS_sysfs = 135; +pub const SYS_personality = 136; +pub const SYS_afs_syscall = 137; +pub const SYS_setfsuid = 138; +pub const SYS_setfsgid = 139; +pub const SYS__llseek = 140; +pub const SYS_getdents = 141; +pub const SYS__newselect = 142; +pub const SYS_flock = 143; +pub const SYS_msync = 144; +pub const SYS_readv = 145; +pub const SYS_writev = 146; +pub const SYS_getsid = 147; +pub const SYS_fdatasync = 148; +pub const SYS__sysctl = 149; +pub const SYS_mlock = 150; +pub const SYS_munlock = 151; +pub const SYS_mlockall = 152; +pub const SYS_munlockall = 153; +pub const SYS_sched_setparam = 154; +pub const SYS_sched_getparam = 155; +pub const SYS_sched_setscheduler = 156; +pub const SYS_sched_getscheduler = 157; +pub const SYS_sched_yield = 158; +pub const SYS_sched_get_priority_max = 159; +pub const SYS_sched_get_priority_min = 160; +pub const SYS_sched_rr_get_interval = 161; +pub const SYS_nanosleep = 162; +pub const SYS_mremap = 163; +pub const SYS_setresuid = 164; +pub const SYS_getresuid = 165; +pub const SYS_vm86 = 166; +pub const SYS_query_module = 167; +pub const SYS_poll = 168; +pub const SYS_nfsservctl = 169; +pub const SYS_setresgid = 170; +pub const SYS_getresgid = 171; +pub const SYS_prctl = 172; +pub const SYS_rt_sigreturn = 173; +pub const SYS_rt_sigaction = 174; +pub const SYS_rt_sigprocmask = 175; +pub const SYS_rt_sigpending = 176; +pub const SYS_rt_sigtimedwait = 177; +pub const SYS_rt_sigqueueinfo = 178; +pub const SYS_rt_sigsuspend = 179; +pub const SYS_pread64 = 180; +pub const SYS_pwrite64 = 181; +pub const SYS_chown = 182; +pub const SYS_getcwd = 183; +pub const SYS_capget = 184; +pub const SYS_capset = 185; +pub const SYS_sigaltstack = 186; +pub const SYS_sendfile = 187; +pub const SYS_getpmsg = 188; +pub const SYS_putpmsg = 189; +pub const SYS_vfork = 190; +pub const SYS_ugetrlimit = 191; +pub const SYS_mmap2 = 192; +pub const SYS_truncate64 = 193; +pub const SYS_ftruncate64 = 194; +pub const SYS_stat64 = 195; +pub const SYS_lstat64 = 196; +pub const SYS_fstat64 = 197; +pub const SYS_lchown32 = 198; +pub const SYS_getuid32 = 199; +pub const SYS_getgid32 = 200; +pub const SYS_geteuid32 = 201; +pub const SYS_getegid32 = 202; +pub const SYS_setreuid32 = 203; +pub const SYS_setregid32 = 204; +pub const SYS_getgroups32 = 205; +pub const SYS_setgroups32 = 206; +pub const SYS_fchown32 = 207; +pub const SYS_setresuid32 = 208; +pub const SYS_getresuid32 = 209; +pub const SYS_setresgid32 = 210; +pub const SYS_getresgid32 = 211; +pub const SYS_chown32 = 212; +pub const SYS_setuid32 = 213; +pub const SYS_setgid32 = 214; +pub const SYS_setfsuid32 = 215; +pub const SYS_setfsgid32 = 216; +pub const SYS_pivot_root = 217; +pub const SYS_mincore = 218; +pub const SYS_madvise = 219; +pub const SYS_getdents64 = 220; +pub const SYS_fcntl64 = 221; +pub const SYS_gettid = 224; +pub const SYS_readahead = 225; +pub const SYS_setxattr = 226; +pub const SYS_lsetxattr = 227; +pub const SYS_fsetxattr = 228; +pub const SYS_getxattr = 229; +pub const SYS_lgetxattr = 230; +pub const SYS_fgetxattr = 231; +pub const SYS_listxattr = 232; +pub const SYS_llistxattr = 233; +pub const SYS_flistxattr = 234; +pub const SYS_removexattr = 235; +pub const SYS_lremovexattr = 236; +pub const SYS_fremovexattr = 237; +pub const SYS_tkill = 238; +pub const SYS_sendfile64 = 239; +pub const SYS_futex = 240; +pub const SYS_sched_setaffinity = 241; +pub const SYS_sched_getaffinity = 242; +pub const SYS_set_thread_area = 243; +pub const SYS_get_thread_area = 244; +pub const SYS_io_setup = 245; +pub const SYS_io_destroy = 246; +pub const SYS_io_getevents = 247; +pub const SYS_io_submit = 248; +pub const SYS_io_cancel = 249; +pub const SYS_fadvise64 = 250; +pub const SYS_exit_group = 252; +pub const SYS_lookup_dcookie = 253; +pub const SYS_epoll_create = 254; +pub const SYS_epoll_ctl = 255; +pub const SYS_epoll_wait = 256; +pub const SYS_remap_file_pages = 257; +pub const SYS_set_tid_address = 258; +pub const SYS_timer_create = 259; +pub const SYS_timer_settime = SYS_timer_create + 1; +pub const SYS_timer_gettime = SYS_timer_create + 2; +pub const SYS_timer_getoverrun = SYS_timer_create + 3; +pub const SYS_timer_delete = SYS_timer_create + 4; +pub const SYS_clock_settime = SYS_timer_create + 5; +pub const SYS_clock_gettime = SYS_timer_create + 6; +pub const SYS_clock_getres = SYS_timer_create + 7; +pub const SYS_clock_nanosleep = SYS_timer_create + 8; +pub const SYS_statfs64 = 268; +pub const SYS_fstatfs64 = 269; +pub const SYS_tgkill = 270; +pub const SYS_utimes = 271; +pub const SYS_fadvise64_64 = 272; +pub const SYS_vserver = 273; +pub const SYS_mbind = 274; +pub const SYS_get_mempolicy = 275; +pub const SYS_set_mempolicy = 276; +pub const SYS_mq_open = 277; +pub const SYS_mq_unlink = SYS_mq_open + 1; +pub const SYS_mq_timedsend = SYS_mq_open + 2; +pub const SYS_mq_timedreceive = SYS_mq_open + 3; +pub const SYS_mq_notify = SYS_mq_open + 4; +pub const SYS_mq_getsetattr = SYS_mq_open + 5; +pub const SYS_kexec_load = 283; +pub const SYS_waitid = 284; +pub const SYS_add_key = 286; +pub const SYS_request_key = 287; +pub const SYS_keyctl = 288; +pub const SYS_ioprio_set = 289; +pub const SYS_ioprio_get = 290; +pub const SYS_inotify_init = 291; +pub const SYS_inotify_add_watch = 292; +pub const SYS_inotify_rm_watch = 293; +pub const SYS_migrate_pages = 294; +pub const SYS_openat = 295; +pub const SYS_mkdirat = 296; +pub const SYS_mknodat = 297; +pub const SYS_fchownat = 298; +pub const SYS_futimesat = 299; +pub const SYS_fstatat64 = 300; +pub const SYS_unlinkat = 301; +pub const SYS_renameat = 302; +pub const SYS_linkat = 303; +pub const SYS_symlinkat = 304; +pub const SYS_readlinkat = 305; +pub const SYS_fchmodat = 306; +pub const SYS_faccessat = 307; +pub const SYS_pselect6 = 308; +pub const SYS_ppoll = 309; +pub const SYS_unshare = 310; +pub const SYS_set_robust_list = 311; +pub const SYS_get_robust_list = 312; +pub const SYS_splice = 313; +pub const SYS_sync_file_range = 314; +pub const SYS_tee = 315; +pub const SYS_vmsplice = 316; +pub const SYS_move_pages = 317; +pub const SYS_getcpu = 318; +pub const SYS_epoll_pwait = 319; +pub const SYS_utimensat = 320; +pub const SYS_signalfd = 321; +pub const SYS_timerfd_create = 322; +pub const SYS_eventfd = 323; +pub const SYS_fallocate = 324; +pub const SYS_timerfd_settime = 325; +pub const SYS_timerfd_gettime = 326; +pub const SYS_signalfd4 = 327; +pub const SYS_eventfd2 = 328; +pub const SYS_epoll_create1 = 329; +pub const SYS_dup3 = 330; +pub const SYS_pipe2 = 331; +pub const SYS_inotify_init1 = 332; +pub const SYS_preadv = 333; +pub const SYS_pwritev = 334; +pub const SYS_rt_tgsigqueueinfo = 335; +pub const SYS_perf_event_open = 336; +pub const SYS_recvmmsg = 337; +pub const SYS_fanotify_init = 338; +pub const SYS_fanotify_mark = 339; +pub const SYS_prlimit64 = 340; +pub const SYS_name_to_handle_at = 341; +pub const SYS_open_by_handle_at = 342; +pub const SYS_clock_adjtime = 343; +pub const SYS_syncfs = 344; +pub const SYS_sendmmsg = 345; +pub const SYS_setns = 346; +pub const SYS_process_vm_readv = 347; +pub const SYS_process_vm_writev = 348; +pub const SYS_kcmp = 349; +pub const SYS_finit_module = 350; +pub const SYS_sched_setattr = 351; +pub const SYS_sched_getattr = 352; +pub const SYS_renameat2 = 353; +pub const SYS_seccomp = 354; +pub const SYS_getrandom = 355; +pub const SYS_memfd_create = 356; +pub const SYS_bpf = 357; +pub const SYS_execveat = 358; +pub const SYS_socket = 359; +pub const SYS_socketpair = 360; +pub const SYS_bind = 361; +pub const SYS_connect = 362; +pub const SYS_listen = 363; +pub const SYS_accept4 = 364; +pub const SYS_getsockopt = 365; +pub const SYS_setsockopt = 366; +pub const SYS_getsockname = 367; +pub const SYS_getpeername = 368; +pub const SYS_sendto = 369; +pub const SYS_sendmsg = 370; +pub const SYS_recvfrom = 371; +pub const SYS_recvmsg = 372; +pub const SYS_shutdown = 373; +pub const SYS_userfaultfd = 374; +pub const SYS_membarrier = 375; +pub const SYS_mlock2 = 376; +pub const SYS_copy_file_range = 377; +pub const SYS_preadv2 = 378; +pub const SYS_pwritev2 = 379; +pub const SYS_pkey_mprotect = 380; +pub const SYS_pkey_alloc = 381; +pub const SYS_pkey_free = 382; +pub const SYS_statx = 383; +pub const SYS_arch_prctl = 384; +pub const SYS_io_pgetevents = 385; +pub const SYS_rseq = 386; +pub const SYS_semget = 393; +pub const SYS_semctl = 394; +pub const SYS_shmget = 395; +pub const SYS_shmctl = 396; +pub const SYS_shmat = 397; +pub const SYS_shmdt = 398; +pub const SYS_msgget = 399; +pub const SYS_msgsnd = 400; +pub const SYS_msgrcv = 401; +pub const SYS_msgctl = 402; +pub const SYS_clock_gettime64 = 403; +pub const SYS_clock_settime64 = 404; +pub const SYS_clock_adjtime64 = 405; +pub const SYS_clock_getres_time64 = 406; +pub const SYS_clock_nanosleep_time64 = 407; +pub const SYS_timer_gettime64 = 408; +pub const SYS_timer_settime64 = 409; +pub const SYS_timerfd_gettime64 = 410; +pub const SYS_timerfd_settime64 = 411; +pub const SYS_utimensat_time64 = 412; +pub const SYS_pselect6_time64 = 413; +pub const SYS_ppoll_time64 = 414; +pub const SYS_io_pgetevents_time64 = 416; +pub const SYS_recvmmsg_time64 = 417; +pub const SYS_mq_timedsend_time64 = 418; +pub const SYS_mq_timedreceive_time64 = 419; +pub const SYS_semtimedop_time64 = 420; +pub const SYS_rt_sigtimedwait_time64 = 421; +pub const SYS_futex_time64 = 422; +pub const SYS_sched_rr_get_interval_time64 = 423; +pub const SYS_pidfd_send_signal = 424; +pub const SYS_io_uring_setup = 425; +pub const SYS_io_uring_enter = 426; +pub const SYS_io_uring_register = 427; +pub const SYS_open_tree = 428; +pub const SYS_move_mount = 429; +pub const SYS_fsopen = 430; +pub const SYS_fsconfig = 431; +pub const SYS_fsmount = 432; +pub const SYS_fspick = 433; + +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0o400000; +pub const O_CLOEXEC = 0o2000000; + +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o40000; +pub const O_LARGEFILE = 0o100000; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 12; +pub const F_SETLK = 13; +pub const F_SETLKW = 14; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const MAP_NORESERVE = 0x4000; +pub const MAP_GROWSDOWN = 0x0100; +pub const MAP_DENYWRITE = 0x0800; +pub const MAP_EXECUTABLE = 0x1000; +pub const MAP_LOCKED = 0x2000; +pub const MAP_32BIT = 0x40; + +pub const MMAP2_UNIT = 4096; + +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; + +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const blksize_t = i32; +pub const nlink_t = u32; +pub const time_t = isize; +pub const mode_t = u32; +pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; + +/// Renamed to Stat to not conflict with the stat function. +/// atime, mtime, and ctime have functions to return `timespec`, +/// because although this is a POSIX API, the layout and names of +/// the structs are inconsistent across operating systems, and +/// in C, macros are used to hide the differences. Here we use +/// methods to accomplish this. +pub const Stat = extern struct { + dev: dev_t, + __dev_padding: u32, + __ino_truncated: u32, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + __rdev_padding: u32, + size: off_t, + blksize: blksize_t, + blocks: blkcnt_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + ino: ino_t, + + pub fn atime(self: Stat) timespec { + return self.atim; + } + + pub fn mtime(self: Stat) timespec { + return self.mtim; + } + + pub fn ctime(self: Stat) timespec { + return self.ctim; + } +}; + +pub const timespec = extern struct { + tv_sec: i32, + tv_nsec: i32, +}; + +pub const timeval = extern struct { + tv_sec: i32, + tv_usec: i32, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const mcontext_t = extern struct { + gregs: [19]usize, + fpregs: [*]u8, + oldmask: usize, + cr2: usize, +}; + +pub const REG_GS = 0; +pub const REG_FS = 1; +pub const REG_ES = 2; +pub const REG_DS = 3; +pub const REG_EDI = 4; +pub const REG_ESI = 5; +pub const REG_EBP = 6; +pub const REG_ESP = 7; +pub const REG_EBX = 8; +pub const REG_EDX = 9; +pub const REG_ECX = 10; +pub const REG_EAX = 11; +pub const REG_TRAPNO = 12; +pub const REG_ERR = 13; +pub const REG_EIP = 14; +pub const REG_CS = 15; +pub const REG_EFL = 16; +pub const REG_UESP = 17; +pub const REG_SS = 18; + +pub const ucontext_t = extern struct { + flags: usize, + link: *ucontext_t, + stack: stack_t, + mcontext: mcontext_t, + sigmask: sigset_t, + regspace: [64]u64, +}; + +pub const Elf_Symndx = u32; + +pub const user_desc = packed struct { + entry_number: u32, + base_addr: u32, + limit: u32, + seg_32bit: u1, + contents: u2, + read_exec_only: u1, + limit_in_pages: u1, + seg_not_present: u1, + useable: u1, +}; + +// socketcall() call numbers +pub const SC_socket = 1; +pub const SC_bind = 2; +pub const SC_connect = 3; +pub const SC_listen = 4; +pub const SC_accept = 5; +pub const SC_getsockname = 6; +pub const SC_getpeername = 7; +pub const SC_socketpair = 8; +pub const SC_send = 9; +pub const SC_recv = 10; +pub const SC_sendto = 11; +pub const SC_recvfrom = 12; +pub const SC_shutdown = 13; +pub const SC_setsockopt = 14; +pub const SC_getsockopt = 15; +pub const SC_sendmsg = 16; +pub const SC_recvmsg = 17; +pub const SC_accept4 = 18; +pub const SC_recvmmsg = 19; +pub const SC_sendmmsg = 20; diff --git a/lib/std/os/bits/linux/mipsel.zig b/lib/std/os/bits/linux/mipsel.zig index 928b974c0..638a4b1de 100644 --- a/lib/std/os/bits/linux/mipsel.zig +++ b/lib/std/os/bits/linux/mipsel.zig @@ -454,7 +454,6 @@ pub const SO_PEERSEC = 30; pub const SO_SNDBUFFORCE = 31; pub const SO_RCVBUFFORCE = 33; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6.39"; @@ -484,6 +483,7 @@ pub const Stat = extern struct { blksize: blksize_t, __pad3: [1]u32, blocks: blkcnt_t, + __pad4: [14]usize, pub fn atime(self: Stat) timespec { return self.atim; diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig index 7b1ad4dd5..da3caf2c8 100644 --- a/lib/std/os/bits/linux/x86_64.zig +++ b/lib/std/os/bits/linux/x86_64.zig @@ -420,7 +420,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6"; pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 14c35faf6..2f2494d4c 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -14,15 +14,9 @@ pub const Kevent = extern struct { udata: usize, }; -pub const pthread_attr_t = extern struct { - pta_magic: u32, - pta_flags: c_int, - pta_private: *c_void, -}; - pub const dl_phdr_info = extern struct { dlpi_addr: usize, - dlpi_name: ?[*]const u8, + dlpi_name: ?[*:0]const u8, dlpi_phdr: [*]std.elf.Phdr, dlpi_phnum: u16, }; @@ -128,26 +122,36 @@ pub const dirent = extern struct { d_type: u8, d_off: i64, d_name: [512]u8, + + pub fn reclen(self: dirent) u16 { + return self.d_reclen; + } }; pub const in_port_t = u16; pub const sa_family_t = u8; -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, +pub const sockaddr = extern struct { + /// total length + len: u8, + + /// address family + family: sa_family_t, + + /// actually longer; address value + data: [14]u8, }; pub const sockaddr_in = extern struct { - len: u8, + len: u8 = @sizeOf(sockaddr_in), family: sa_family_t, port: in_port_t, addr: u32, - zero: [8]u8, + zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, }; pub const sockaddr_in6 = extern struct { - len: u8, + len: u8 = @sizeOf(sockaddr_in6), family: sa_family_t, port: in_port_t, flowinfo: u32, @@ -155,6 +159,18 @@ pub const sockaddr_in6 = extern struct { scope_id: u32, }; +/// Definitions for UNIX IPC domain. +pub const sockaddr_un = extern struct { + /// total sockaddr length + len: u8 = @sizeOf(sockaddr_un), + + /// AF_LOCAL + family: sa_family_t, + + /// path name + path: [104]u8, +}; + pub const CTL_KERN = 1; pub const CTL_DEBUG = 5; @@ -276,7 +292,6 @@ pub const O_CLOEXEC = 0x00400000; pub const O_ASYNC = 0x0040; pub const O_DIRECT = 0x00080000; -pub const O_LARGEFILE = 0; pub const O_NOATIME = 0; pub const O_PATH = 0; pub const O_TMPFILE = 0; @@ -312,31 +327,6 @@ pub const SOCK_SEQPACKET = 5; pub const SOCK_CLOEXEC = 0x10000000; pub const SOCK_NONBLOCK = 0x20000000; -pub const PROTO_ip = 0; -pub const PROTO_icmp = 1; -pub const PROTO_igmp = 2; -pub const PROTO_ggp = 3; -pub const PROTO_ipencap = 4; -pub const PROTO_tcp = 6; -pub const PROTO_egp = 8; -pub const PROTO_pup = 12; -pub const PROTO_udp = 17; -pub const PROTO_xns_idp = 22; -pub const PROTO_iso_tp4 = 29; -pub const PROTO_ipv6 = 41; -pub const PROTO_ipv6_route = 43; -pub const PROTO_ipv6_frag = 44; -pub const PROTO_rsvp = 46; -pub const PROTO_gre = 47; -pub const PROTO_esp = 50; -pub const PROTO_ah = 51; -pub const PROTO_ipv6_icmp = 58; -pub const PROTO_ipv6_nonxt = 59; -pub const PROTO_ipv6_opts = 60; -pub const PROTO_encap = 98; -pub const PROTO_pim = 103; -pub const PROTO_raw = 255; - pub const PF_UNSPEC = 0; pub const PF_LOCAL = 1; pub const PF_UNIX = PF_LOCAL; @@ -823,3 +813,114 @@ pub fn S_IWHT(m: u32) bool { } pub const HOST_NAME_MAX = 255; + +/// dummy for IP +pub const IPPROTO_IP = 0; + +/// IP6 hop-by-hop options +pub const IPPROTO_HOPOPTS = 0; + +/// control message protocol +pub const IPPROTO_ICMP = 1; + +/// group mgmt protocol +pub const IPPROTO_IGMP = 2; + +/// gateway^2 (deprecated) +pub const IPPROTO_GGP = 3; + +/// IP header +pub const IPPROTO_IPV4 = 4; + +/// IP inside IP +pub const IPPROTO_IPIP = 4; + +/// tcp +pub const IPPROTO_TCP = 6; + +/// exterior gateway protocol +pub const IPPROTO_EGP = 8; + +/// pup +pub const IPPROTO_PUP = 12; + +/// user datagram protocol +pub const IPPROTO_UDP = 17; + +/// xns idp +pub const IPPROTO_IDP = 22; + +/// tp-4 w/ class negotiation +pub const IPPROTO_TP = 29; + +/// DCCP +pub const IPPROTO_DCCP = 33; + +/// IP6 header +pub const IPPROTO_IPV6 = 41; + +/// IP6 routing header +pub const IPPROTO_ROUTING = 43; + +/// IP6 fragmentation header +pub const IPPROTO_FRAGMENT = 44; + +/// resource reservation +pub const IPPROTO_RSVP = 46; + +/// GRE encaps RFC 1701 +pub const IPPROTO_GRE = 47; + +/// encap. security payload +pub const IPPROTO_ESP = 50; + +/// authentication header +pub const IPPROTO_AH = 51; + +/// IP Mobility RFC 2004 +pub const IPPROTO_MOBILE = 55; + +/// IPv6 ICMP +pub const IPPROTO_IPV6_ICMP = 58; + +/// ICMP6 +pub const IPPROTO_ICMPV6 = 58; + +/// IP6 no next header +pub const IPPROTO_NONE = 59; + +/// IP6 destination option +pub const IPPROTO_DSTOPTS = 60; + +/// ISO cnlp +pub const IPPROTO_EON = 80; + +/// Ethernet-in-IP +pub const IPPROTO_ETHERIP = 97; + +/// encapsulation header +pub const IPPROTO_ENCAP = 98; + +/// Protocol indep. multicast +pub const IPPROTO_PIM = 103; + +/// IP Payload Comp. Protocol +pub const IPPROTO_IPCOMP = 108; + +/// VRRP RFC 2338 +pub const IPPROTO_VRRP = 112; + +/// Common Address Resolution Protocol +pub const IPPROTO_CARP = 112; + +/// L2TPv3 +pub const IPPROTO_L2TP = 115; + +/// SCTP +pub const IPPROTO_SCTP = 132; + +/// PFSYNC +pub const IPPROTO_PFSYNC = 240; + +/// raw IP packet +pub const IPPROTO_RAW = 255; diff --git a/lib/std/os/bits/wasi.zig b/lib/std/os/bits/wasi.zig index 93d2a82fd..139418ded 100644 --- a/lib/std/os/bits/wasi.zig +++ b/lib/std/os/bits/wasi.zig @@ -138,7 +138,7 @@ pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; pub const FDFLAG_RSYNC: fdflags_t = 0x0008; pub const FDFLAG_SYNC: fdflags_t = 0x0010; -const fdstat_t = extern struct { +pub const fdstat_t = extern struct { fs_filetype: filetype_t, fs_flags: fdflags_t, fs_rights_base: rights_t, @@ -298,6 +298,7 @@ pub const subscription_t = extern struct { }; pub const timestamp_t = u64; +pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc pub const userdata_t = u64; @@ -305,3 +306,8 @@ pub const whence_t = u8; pub const WHENCE_CUR: whence_t = 0; pub const WHENCE_END: whence_t = 1; pub const WHENCE_SET: whence_t = 2; + +pub const timespec = extern struct { + tv_sec: time_t, + tv_nsec: isize, +}; diff --git a/lib/std/os/bits/windows.zig b/lib/std/os/bits/windows.zig index fc148d812..299804957 100644 --- a/lib/std/os/bits/windows.zig +++ b/lib/std/os/bits/windows.zig @@ -1,6 +1,7 @@ // The reference for these types and values is Microsoft Windows's ucrt (Universal C RunTime). usingnamespace @import("../windows/bits.zig"); +const ws2_32 = @import("../windows/ws2_32.zig"); pub const fd_t = HANDLE; pub const pid_t = HANDLE; @@ -158,3 +159,68 @@ pub const EWOULDBLOCK = 140; pub const EDQUOT = 10069; pub const F_OK = 0; + +/// Remove directory instead of unlinking file +pub const AT_REMOVEDIR = 0x200; + +pub const in_port_t = u16; +pub const sa_family_t = ws2_32.ADDRESS_FAMILY; +pub const socklen_t = u32; + +pub const sockaddr = ws2_32.sockaddr; +pub const sockaddr_in = ws2_32.sockaddr_in; +pub const sockaddr_in6 = ws2_32.sockaddr_in6; +pub const sockaddr_un = ws2_32.sockaddr_un; + +pub const in6_addr = [16]u8; +pub const in_addr = u32; + +pub const AF_UNSPEC = ws2_32.AF_UNSPEC; +pub const AF_UNIX = ws2_32.AF_UNIX; +pub const AF_INET = ws2_32.AF_INET; +pub const AF_IMPLINK = ws2_32.AF_IMPLINK; +pub const AF_PUP = ws2_32.AF_PUP; +pub const AF_CHAOS = ws2_32.AF_CHAOS; +pub const AF_NS = ws2_32.AF_NS; +pub const AF_IPX = ws2_32.AF_IPX; +pub const AF_ISO = ws2_32.AF_ISO; +pub const AF_OSI = ws2_32.AF_OSI; +pub const AF_ECMA = ws2_32.AF_ECMA; +pub const AF_DATAKIT = ws2_32.AF_DATAKIT; +pub const AF_CCITT = ws2_32.AF_CCITT; +pub const AF_SNA = ws2_32.AF_SNA; +pub const AF_DECnet = ws2_32.AF_DECnet; +pub const AF_DLI = ws2_32.AF_DLI; +pub const AF_LAT = ws2_32.AF_LAT; +pub const AF_HYLINK = ws2_32.AF_HYLINK; +pub const AF_APPLETALK = ws2_32.AF_APPLETALK; +pub const AF_NETBIOS = ws2_32.AF_NETBIOS; +pub const AF_VOICEVIEW = ws2_32.AF_VOICEVIEW; +pub const AF_FIREFOX = ws2_32.AF_FIREFOX; +pub const AF_UNKNOWN1 = ws2_32.AF_UNKNOWN1; +pub const AF_BAN = ws2_32.AF_BAN; +pub const AF_ATM = ws2_32.AF_ATM; +pub const AF_INET6 = ws2_32.AF_INET6; +pub const AF_CLUSTER = ws2_32.AF_CLUSTER; +pub const AF_12844 = ws2_32.AF_12844; +pub const AF_IRDA = ws2_32.AF_IRDA; +pub const AF_NETDES = ws2_32.AF_NETDES; +pub const AF_TCNPROCESS = ws2_32.AF_TCNPROCESS; +pub const AF_TCNMESSAGE = ws2_32.AF_TCNMESSAGE; +pub const AF_ICLFXBM = ws2_32.AF_ICLFXBM; +pub const AF_BTH = ws2_32.AF_BTH; +pub const AF_MAX = ws2_32.AF_MAX; + +pub const SOCK_STREAM = ws2_32.SOCK_STREAM; +pub const SOCK_DGRAM = ws2_32.SOCK_DGRAM; +pub const SOCK_RAW = ws2_32.SOCK_RAW; +pub const SOCK_RDM = ws2_32.SOCK_RDM; +pub const SOCK_SEQPACKET = ws2_32.SOCK_SEQPACKET; + +pub const IPPROTO_ICMP = ws2_32.IPPROTO_ICMP; +pub const IPPROTO_IGMP = ws2_32.IPPROTO_IGMP; +pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM; +pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP; +pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP; +pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6; +pub const IPPROTO_RM = ws2_32.IPPROTO_RM; diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig index 0adf71aff..d3e9cefb8 100644 --- a/lib/std/os/darwin.zig +++ b/lib/std/os/darwin.zig @@ -1,8 +1,4 @@ const builtin = @import("builtin"); const std = @import("../std.zig"); -pub const is_the_target = switch (builtin.os) { - .macosx, .tvos, .watchos, .ios => true, - else => false, -}; pub usingnamespace std.c; pub usingnamespace @import("bits.zig"); diff --git a/lib/std/os/dragonfly.zig b/lib/std/os/dragonfly.zig new file mode 100644 index 000000000..5f9684b96 --- /dev/null +++ b/lib/std/os/dragonfly.zig @@ -0,0 +1,3 @@ +const std = @import("../std.zig"); +pub usingnamespace std.c; +pub usingnamespace @import("bits.zig"); diff --git a/lib/std/os/freebsd.zig b/lib/std/os/freebsd.zig index ddbf98f2b..5f9684b96 100644 --- a/lib/std/os/freebsd.zig +++ b/lib/std/os/freebsd.zig @@ -1,5 +1,3 @@ const std = @import("../std.zig"); -const builtin = @import("builtin"); -pub const is_the_target = builtin.os == .freebsd; pub usingnamespace std.c; pub usingnamespace @import("bits.zig"); diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index e813519c6..fcc1e6248 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -13,8 +13,8 @@ const elf = std.elf; const vdso = @import("linux/vdso.zig"); const dl = @import("../dynamic_library.zig"); -pub const is_the_target = builtin.os == .linux; pub usingnamespace switch (builtin.arch) { + .i386 => @import("linux/i386.zig"), .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), .arm => @import("linux/arm-eabi.zig"), @@ -47,36 +47,33 @@ pub fn getErrno(r: usize) u12 { pub fn dup2(old: i32, new: i32) usize { if (@hasDecl(@This(), "SYS_dup2")) { - return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new))); + return syscall2(SYS_dup2, @bitCast(usize, @as(isize, old)), @bitCast(usize, @as(isize, new))); } else { if (old == new) { if (std.debug.runtime_safety) { - const rc = syscall2(SYS_fcntl, @bitCast(usize, isize(old)), F_GETFD); + const rc = syscall2(SYS_fcntl, @bitCast(usize, @as(isize, old)), F_GETFD); if (@bitCast(isize, rc) < 0) return rc; } return @intCast(usize, old); } else { - return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), 0); + return syscall3(SYS_dup3, @bitCast(usize, @as(isize, old)), @bitCast(usize, @as(isize, new)), 0); } } } pub fn dup3(old: i32, new: i32, flags: u32) usize { - return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); + return syscall3(SYS_dup3, @bitCast(usize, @as(isize, old)), @bitCast(usize, @as(isize, new)), flags); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chdir(path: [*]const u8) usize { +pub fn chdir(path: [*:0]const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chroot(path: [*]const u8) usize { +pub fn chroot(path: [*:0]const u8) usize { return syscall1(SYS_chroot, @ptrToInt(path)); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { +pub fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -94,16 +91,15 @@ pub fn fork() usize { /// the compiler is not aware of how vfork affects control flow and you may /// see different results in optimized builds. pub inline fn vfork() usize { - return @inlineCall(syscall0, SYS_vfork); + return @call(.{ .modifier = .always_inline }, syscall0, .{SYS_vfork}); } pub fn futimens(fd: i32, times: *const [2]timespec) usize { return utimensat(fd, null, times, 0); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn utimensat(dirfd: i32, path: ?[*]const u8, times: *const [2]timespec, flags: u32) usize { - return syscall4(SYS_utimensat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(times), flags); +pub fn utimensat(dirfd: i32, path: ?[*:0]const u8, times: *const [2]timespec, flags: u32) usize { + return syscall4(SYS_utimensat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), @ptrToInt(times), flags); } pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { @@ -121,7 +117,7 @@ pub fn getcwd(buf: [*]u8, size: usize) usize { pub fn getdents(fd: i32, dirp: [*]u8, len: usize) usize { return syscall3( SYS_getdents, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(dirp), std.math.min(len, maxInt(c_int)), ); @@ -130,7 +126,7 @@ pub fn getdents(fd: i32, dirp: [*]u8, len: usize) usize { pub fn getdents64(fd: i32, dirp: [*]u8, len: usize) usize { return syscall3( SYS_getdents64, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(dirp), std.math.min(len, maxInt(c_int)), ); @@ -140,54 +136,47 @@ pub fn inotify_init1(flags: u32) usize { return syscall1(SYS_inotify_init1, flags); } -pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { - return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); +pub fn inotify_add_watch(fd: i32, pathname: [*:0]const u8, mask: u32) usize { + return syscall3(SYS_inotify_add_watch, @bitCast(usize, @as(isize, fd)), @ptrToInt(pathname), mask); } pub fn inotify_rm_watch(fd: i32, wd: i32) usize { - return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); + return syscall2(SYS_inotify_rm_watch, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, wd))); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { +pub fn readlink(noalias path: [*:0]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { if (@hasDecl(@This(), "SYS_readlink")) { return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } else { - return syscall4(SYS_readlinkat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + return syscall4(SYS_readlinkat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); +pub fn readlinkat(dirfd: i32, noalias path: [*:0]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return syscall4(SYS_readlinkat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdir(path: [*]const u8, mode: u32) usize { +pub fn mkdir(path: [*:0]const u8, mode: u32) usize { if (@hasDecl(@This(), "SYS_mkdir")) { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } else { - return syscall3(SYS_mkdirat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(path), mode); + return syscall3(SYS_mkdirat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), mode); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { - return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); +pub fn mkdirat(dirfd: i32, path: [*:0]const u8, mode: u32) usize { + return syscall3(SYS_mkdirat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), mode); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { +pub fn mount(special: [*:0]const u8, dir: [*:0]const u8, fstype: [*:0]const u8, flags: u32, data: usize) usize { return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount(special: [*]const u8) usize { +pub fn umount(special: [*:0]const u8) usize { return syscall2(SYS_umount2, @ptrToInt(special), 0); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount2(special: [*]const u8, flags: u32) usize { +pub fn umount2(special: [*:0]const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } @@ -195,7 +184,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of if (@hasDecl(@This(), "SYS_mmap2")) { // Make sure the offset is also specified in multiples of page size if ((offset & (MMAP2_UNIT - 1)) != 0) - return @bitCast(usize, isize(-EINVAL)); + return @bitCast(usize, @as(isize, -EINVAL)); return syscall6( SYS_mmap2, @@ -203,7 +192,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of length, prot, flags, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @truncate(usize, offset / MMAP2_UNIT), ); } else { @@ -213,7 +202,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of length, prot, flags, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), offset, ); } @@ -227,14 +216,36 @@ pub fn munmap(address: [*]const u8, length: usize) usize { return syscall2(SYS_munmap, @ptrToInt(address), length); } +pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize { + if (@hasDecl(@This(), "SYS_poll")) { + return syscall3(SYS_poll, @ptrToInt(fds), n, @bitCast(u32, timeout)); + } else { + return syscall6( + SYS_ppoll, + @ptrToInt(fds), + n, + @ptrToInt(if (timeout >= 0) + ×pec{ + .tv_sec = @divTrunc(timeout, 1000), + .tv_nsec = @rem(timeout, 1000) * 1000000, + } + else + null), + 0, + 0, + NSIG / 8, + ); + } +} + pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); + return syscall3(SYS_read, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count); } pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { return syscall5( SYS_preadv, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, @truncate(usize, offset), @@ -245,7 +256,7 @@ pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { pub fn preadv2(fd: i32, iov: [*]const iovec, count: usize, offset: u64, flags: kernel_rwf) usize { return syscall6( SYS_preadv2, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, @truncate(usize, offset), @@ -255,17 +266,17 @@ pub fn preadv2(fd: i32, iov: [*]const iovec, count: usize, offset: u64, flags: k } pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { - return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); + return syscall3(SYS_readv, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count); } pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { - return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); + return syscall3(SYS_writev, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count); } pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { return syscall5( SYS_pwritev, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, @truncate(usize, offset), @@ -276,7 +287,7 @@ pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) us pub fn pwritev2(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64, flags: kernel_rwf) usize { return syscall6( SYS_pwritev2, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, @truncate(usize, offset), @@ -285,50 +296,46 @@ pub fn pwritev2(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64, f ); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rmdir(path: [*]const u8) usize { +pub fn rmdir(path: [*:0]const u8) usize { if (@hasDecl(@This(), "SYS_rmdir")) { return syscall1(SYS_rmdir, @ptrToInt(path)); } else { - return syscall3(SYS_unlinkat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(path), AT_REMOVEDIR); + return syscall3(SYS_unlinkat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), AT_REMOVEDIR); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { +pub fn symlink(existing: [*:0]const u8, new: [*:0]const u8) usize { if (@hasDecl(@This(), "SYS_symlink")) { return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); } else { - return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(new)); + return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(new)); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { - return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); +pub fn symlinkat(existing: [*:0]const u8, newfd: i32, newpath: [*:0]const u8) usize { + return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, @as(isize, newfd)), @ptrToInt(newpath)); } -// TODO https://github.com/ziglang/zig/issues/265 pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); + return syscall4(SYS_pread, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count, offset); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn access(path: [*]const u8, mode: u32) usize { +pub fn access(path: [*:0]const u8, mode: u32) usize { if (@hasDecl(@This(), "SYS_access")) { return syscall2(SYS_access, @ptrToInt(path), mode); } else { - return syscall4(SYS_faccessat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(path), mode, 0); + return syscall4(SYS_faccessat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), mode, 0); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32, flags: u32) usize { - return syscall4(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode, flags); +pub fn faccessat(dirfd: i32, path: [*:0]const u8, mode: u32, flags: u32) usize { + return syscall4(SYS_faccessat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), mode, flags); } pub fn pipe(fd: *[2]i32) usize { - if (@hasDecl(@This(), "SYS_pipe")) { + if (builtin.arch == .mipsel) { + return syscall_pipe(fd); + } else if (@hasDecl(@This(), "SYS_pipe")) { return syscall1(SYS_pipe, @ptrToInt(fd)); } else { return syscall2(SYS_pipe2, @ptrToInt(fd), 0); @@ -340,21 +347,20 @@ pub fn pipe2(fd: *[2]i32, flags: u32) usize { } pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); + return syscall3(SYS_write, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count); } pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); + return syscall4(SYS_pwrite, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count, offset); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rename(old: [*]const u8, new: [*]const u8) usize { +pub fn rename(old: [*:0]const u8, new: [*:0]const u8) usize { if (@hasDecl(@This(), "SYS_rename")) { return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); } else if (@hasDecl(@This(), "SYS_renameat")) { - return syscall4(SYS_renameat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(old), @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(new)); + return syscall4(SYS_renameat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(old), @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(new)); } else { - return syscall5(SYS_renameat2, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(old), @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(new), 0); + return syscall5(SYS_renameat2, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(old), @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(new), 0); } } @@ -362,43 +368,41 @@ pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const if (@hasDecl(@This(), "SYS_renameat")) { return syscall4( SYS_renameat, - @bitCast(usize, isize(oldfd)), + @bitCast(usize, @as(isize, oldfd)), @ptrToInt(old), - @bitCast(usize, isize(newfd)), + @bitCast(usize, @as(isize, newfd)), @ptrToInt(new), ); } else { return syscall5( SYS_renameat2, - @bitCast(usize, isize(oldfd)), + @bitCast(usize, @as(isize, oldfd)), @ptrToInt(old), - @bitCast(usize, isize(newfd)), + @bitCast(usize, @as(isize, newfd)), @ptrToInt(new), 0, ); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { +pub fn renameat2(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]const u8, flags: u32) usize { return syscall5( SYS_renameat2, - @bitCast(usize, isize(oldfd)), + @bitCast(usize, @as(isize, oldfd)), @ptrToInt(oldpath), - @bitCast(usize, isize(newfd)), + @bitCast(usize, @as(isize, newfd)), @ptrToInt(newpath), flags, ); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { +pub fn open(path: [*:0]const u8, flags: u32, perm: usize) usize { if (@hasDecl(@This(), "SYS_open")) { return syscall3(SYS_open, @ptrToInt(path), flags, perm); } else { return syscall4( SYS_openat, - @bitCast(usize, isize(AT_FDCWD)), + @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), flags, perm, @@ -406,15 +410,13 @@ pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn create(path: [*]const u8, perm: usize) usize { +pub fn create(path: [*:0]const u8, perm: usize) usize { return syscall2(SYS_creat, @ptrToInt(path), perm); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { +pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, mode: usize) usize { // dirfd could be negative, for example AT_FDCWD is -100 - return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); + return syscall4(SYS_openat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), flags, mode); } /// See also `clone` (from the arch-specific include) @@ -428,14 +430,14 @@ pub fn clone2(flags: u32, child_stack_ptr: usize) usize { } pub fn close(fd: i32) usize { - return syscall1(SYS_close, @bitCast(usize, isize(fd))); + return syscall1(SYS_close, @bitCast(usize, @as(isize, fd))); } /// Can only be called on 32 bit systems. For 64 bit see `lseek`. pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize { return syscall5( SYS__llseek, - @bitCast(usize, isize(fd)), + @bitCast(usize, @as(isize, fd)), @truncate(usize, offset >> 32), @truncate(usize, offset), @ptrToInt(result), @@ -445,16 +447,16 @@ pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize { /// Can only be called on 64 bit systems. For 32 bit see `llseek`. pub fn lseek(fd: i32, offset: i64, whence: usize) usize { - return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), whence); + return syscall3(SYS_lseek, @bitCast(usize, @as(isize, fd)), @bitCast(usize, offset), whence); } pub fn exit(status: i32) noreturn { - _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); + _ = syscall1(SYS_exit, @bitCast(usize, @as(isize, status))); unreachable; } pub fn exit_group(status: i32) noreturn { - _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); + _ = syscall1(SYS_exit_group, @bitCast(usize, @as(isize, status))); unreachable; } @@ -462,26 +464,32 @@ pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); } -pub fn kill(pid: i32, sig: i32) usize { - return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); +pub fn kill(pid: pid_t, sig: i32) usize { + return syscall2(SYS_kill, @bitCast(usize, @as(isize, pid)), @bitCast(usize, @as(isize, sig))); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlink(path: [*]const u8) usize { +pub fn tkill(tid: pid_t, sig: i32) usize { + return syscall2(SYS_tkill, @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); +} + +pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { + return syscall2(SYS_tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); +} + +pub fn unlink(path: [*:0]const u8) usize { if (@hasDecl(@This(), "SYS_unlink")) { return syscall1(SYS_unlink, @ptrToInt(path)); } else { - return syscall3(SYS_unlinkat, @bitCast(usize, isize(AT_FDCWD)), @ptrToInt(path), 0); + return syscall3(SYS_unlinkat, @bitCast(usize, @as(isize, AT_FDCWD)), @ptrToInt(path), 0); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { - return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); +pub fn unlinkat(dirfd: i32, path: [*:0]const u8, flags: u32) usize { + return syscall3(SYS_unlinkat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), flags); } -pub fn waitpid(pid: i32, status: *u32, flags: u32) usize { - return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), flags, 0); +pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { + return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); } var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); @@ -496,33 +504,33 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); const rc = f(clk_id, tp); switch (rc) { - 0, @bitCast(usize, isize(-EINVAL)) => return rc, + 0, @bitCast(usize, @as(isize, -EINVAL)) => return rc, else => {}, } } } - return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); + return syscall2(SYS_clock_gettime, @bitCast(usize, @as(isize, clk_id)), @ptrToInt(tp)); } -extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { +fn init_vdso_clock_gettime(clk: i32, ts: *timespec) callconv(.C) usize { const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); // Note that we may not have a VDSO at all, update the stub address anyway // so that clock_gettime will fall back on the good old (and slow) syscall - _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic); + @atomicStore(?*const c_void, &vdso_clock_gettime, ptr, .Monotonic); // Call into the VDSO if available if (ptr) |fn_ptr| { const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); return f(clk, ts); } - return @bitCast(usize, isize(-ENOSYS)); + return @bitCast(usize, @as(isize, -ENOSYS)); } pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); + return syscall2(SYS_clock_getres, @bitCast(usize, @as(isize, clk_id)), @ptrToInt(tp)); } pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { - return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); + return syscall2(SYS_clock_settime, @bitCast(usize, @as(isize, clk_id)), @ptrToInt(tp)); } pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { @@ -571,33 +579,33 @@ pub fn setregid(rgid: u32, egid: u32) usize { pub fn getuid() u32 { if (@hasDecl(@This(), "SYS_getuid32")) { - return u32(syscall0(SYS_getuid32)); + return @as(u32, syscall0(SYS_getuid32)); } else { - return u32(syscall0(SYS_getuid)); + return @as(u32, syscall0(SYS_getuid)); } } pub fn getgid() u32 { if (@hasDecl(@This(), "SYS_getgid32")) { - return u32(syscall0(SYS_getgid32)); + return @as(u32, syscall0(SYS_getgid32)); } else { - return u32(syscall0(SYS_getgid)); + return @as(u32, syscall0(SYS_getgid)); } } pub fn geteuid() u32 { if (@hasDecl(@This(), "SYS_geteuid32")) { - return u32(syscall0(SYS_geteuid32)); + return @as(u32, syscall0(SYS_geteuid32)); } else { - return u32(syscall0(SYS_geteuid)); + return @as(u32, syscall0(SYS_geteuid)); } } pub fn getegid() u32 { if (@hasDecl(@This(), "SYS_getegid32")) { - return u32(syscall0(SYS_getegid32)); + return @as(u32, syscall0(SYS_getegid32)); } else { - return u32(syscall0(SYS_getegid)); + return @as(u32, syscall0(SYS_getegid)); } } @@ -657,15 +665,15 @@ pub fn setgroups(size: usize, list: *const u32) usize { } } -pub fn getpid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); +pub fn getpid() pid_t { + return @bitCast(pid_t, @truncate(u32, syscall0(SYS_getpid))); } -pub fn gettid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); +pub fn gettid() pid_t { + return @bitCast(pid_t, @truncate(u32, syscall0(SYS_gettid))); } -pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { +pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) usize { return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } @@ -682,7 +690,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti .restorer = @ptrCast(extern fn () void, restorer_fn), }; var ksa_old: k_sigaction = undefined; - const ksa_mask_size = @sizeOf(@typeOf(ksa_old.mask)); + const ksa_mask_size = @sizeOf(@TypeOf(ksa_old.mask)); @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), ksa_mask_size); const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), ksa_mask_size); const err = getErrno(result); @@ -697,18 +705,6 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti return 0; } -pub fn blockAllSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); -} - -pub fn blockAppSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); -} - -pub fn restoreSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); -} - pub fn sigaddset(set: *sigset_t, sig: u6) void { const s = sig - 1; (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); @@ -720,31 +716,49 @@ pub fn sigismember(set: *const sigset_t, sig: u6) bool { } pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); + if (builtin.arch == .i386) { + return socketcall(SC_getsockname, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) }); + } + return syscall3(SYS_getsockname, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len)); } pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); + if (builtin.arch == .i386) { + return socketcall(SC_getpeername, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) }); + } + return syscall3(SYS_getpeername, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len)); } pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_socket, &[3]usize{ domain, socket_type, protocol }); + } return syscall3(SYS_socket, domain, socket_type, protocol); } pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); + if (builtin.arch == .i386) { + return socketcall(SC_setsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen) }); + } + return syscall5(SYS_setsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); } pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); + if (builtin.arch == .i386) { + return socketcall(SC_getsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen) }); + } + return syscall5(SYS_getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize { - return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); + if (builtin.arch == .i386) { + return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + } + return syscall3(SYS_sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); } pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { - if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) { + if (@typeInfo(usize).Int.bits > @typeInfo(@TypeOf(mmsghdr(undefined).msg_len)).Int.bits) { // workaround kernel brokenness: // if adding up all iov_len overflows a i32 then split into multiple calls // see https://www.openwall.com/lists/musl/2014/06/07/5 @@ -758,7 +772,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize // batch-send all messages up to the current message if (next_unsent < i) { const batch_size = i - next_unsent; - const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + const r = syscall4(SYS_sendmmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); if (getErrno(r) != 0) return next_unsent; if (r < batch_size) return next_unsent + r; } @@ -774,65 +788,94 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize } if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR) const batch_size = kvlen - next_unsent; - const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + const r = syscall4(SYS_sendmmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); if (getErrno(r) != 0) return r; return next_unsent + r; } return kvlen; } - return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags); + return syscall4(SYS_sendmmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msgvec), vlen, flags); } pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { - return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); + if (builtin.arch == .i386) { + return socketcall(SC_connect, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len }); + } + return syscall3(SYS_connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { - return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); + if (builtin.arch == .i386) { + return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + } + return syscall3(SYS_recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); } pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { - return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); + if (builtin.arch == .i386) { + return socketcall(SC_recvfrom, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen) }); + } + return syscall6(SYS_recvfrom, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } pub fn shutdown(fd: i32, how: i32) usize { - return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); + if (builtin.arch == .i386) { + return socketcall(SC_shutdown, &[2]usize{ @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)) }); + } + return syscall2(SYS_shutdown, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how))); } pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); + if (builtin.arch == .i386) { + return socketcall(SC_bind, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len) }); + } + return syscall3(SYS_bind, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len)); } pub fn listen(fd: i32, backlog: u32) usize { - return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); + if (builtin.arch == .i386) { + return socketcall(SC_listen, &[2]usize{ @bitCast(usize, @as(isize, fd)), backlog }); + } + return syscall2(SYS_listen, @bitCast(usize, @as(isize, fd)), backlog); } pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); + if (builtin.arch == .i386) { + return socketcall(SC_sendto, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen) }); + } + return syscall6(SYS_sendto, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_socketpair, &[4]usize{ @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]) }); + } return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); } pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_accept, &[4]usize{ fd, addr, len, 0 }); + } return accept4(fd, addr, len, 0); } pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { - return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); + if (builtin.arch == .i386) { + return socketcall(SC_accept4, &[4]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags }); + } + return syscall4(SYS_accept4, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags); } pub fn fstat(fd: i32, stat_buf: *Stat) usize { if (@hasDecl(@This(), "SYS_fstat64")) { - return syscall2(SYS_fstat64, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); + return syscall2(SYS_fstat64, @bitCast(usize, @as(isize, fd)), @ptrToInt(stat_buf)); } else { - return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); + return syscall2(SYS_fstat, @bitCast(usize, @as(isize, fd)), @ptrToInt(stat_buf)); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { +pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize { if (@hasDecl(@This(), "SYS_stat64")) { return syscall2(SYS_stat64, @ptrToInt(pathname), @ptrToInt(statbuf)); } else { @@ -840,8 +883,7 @@ pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { +pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize { if (@hasDecl(@This(), "SYS_lstat64")) { return syscall2(SYS_lstat64, @ptrToInt(pathname), @ptrToInt(statbuf)); } else { @@ -849,22 +891,33 @@ pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { +pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize { if (@hasDecl(@This(), "SYS_fstatat64")) { - return syscall4(SYS_fstatat64, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); + return syscall4(SYS_fstatat64, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); } else { - return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); + return syscall4(SYS_fstatat, @bitCast(usize, @as(isize, dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); } } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { +pub fn statx(dirfd: i32, path: [*]const u8, flags: u32, mask: u32, statx_buf: *Statx) usize { + if (@hasDecl(@This(), "SYS_statx")) { + return syscall5( + SYS_statx, + @bitCast(usize, @as(isize, dirfd)), + @ptrToInt(path), + flags, + mask, + @ptrToInt(statx_buf), + ); + } + return @bitCast(usize, @as(isize, -ENOSYS)); +} + +pub fn listxattr(path: [*:0]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { +pub fn llistxattr(path: [*:0]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); } @@ -872,53 +925,48 @@ pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { +pub fn getxattr(path: [*:0]const u8, name: [*:0]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { +pub fn lgetxattr(path: [*:0]const u8, name: [*:0]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { +pub fn fgetxattr(fd: usize, name: [*:0]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { +pub fn setxattr(path: [*:0]const u8, name: [*:0]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { +pub fn lsetxattr(path: [*:0]const u8, name: [*:0]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { +pub fn fsetxattr(fd: usize, name: [*:0]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { +pub fn removexattr(path: [*:0]const u8, name: [*:0]const u8) usize { return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { +pub fn lremovexattr(path: [*:0]const u8, name: [*:0]const u8) usize { return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); } -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fremovexattr(fd: usize, name: [*]const u8) usize { +pub fn fremovexattr(fd: usize, name: [*:0]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } -pub fn sched_getaffinity(pid: i32, size: usize, set: *cpu_set_t) usize { - const rc = syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), size, @ptrToInt(set)); +pub fn sched_yield() usize { + return syscall0(SYS_sched_yield); +} + +pub fn sched_getaffinity(pid: pid_t, size: usize, set: *cpu_set_t) usize { + const rc = syscall3(SYS_sched_getaffinity, @bitCast(usize, @as(isize, pid)), size, @ptrToInt(set)); if (@bitCast(isize, rc) < 0) return rc; if (rc < size) @memset(@ptrCast([*]u8, set) + rc, 0, size - rc); return 0; @@ -933,7 +981,7 @@ pub fn epoll_create1(flags: usize) usize { } pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: ?*epoll_event) usize { - return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); + return syscall4(SYS_epoll_ctl, @bitCast(usize, @as(isize, epoll_fd)), @intCast(usize, op), @bitCast(usize, @as(isize, fd)), @ptrToInt(ev)); } pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { @@ -943,10 +991,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { return syscall6( SYS_epoll_pwait, - @bitCast(usize, isize(epoll_fd)), + @bitCast(usize, @as(isize, epoll_fd)), @ptrToInt(events), @intCast(usize, maxevents), - @bitCast(usize, isize(timeout)), + @bitCast(usize, @as(isize, timeout)), @ptrToInt(sigmask), @sizeOf(sigset_t), ); @@ -957,7 +1005,7 @@ pub fn eventfd(count: u32, flags: u32) usize { } pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); + return syscall2(SYS_timerfd_create, @bitCast(usize, @as(isize, clockid)), flags); } pub const itimerspec = extern struct { @@ -966,11 +1014,11 @@ pub const itimerspec = extern struct { }; pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); + return syscall2(SYS_timerfd_gettime, @bitCast(usize, @as(isize, fd)), @ptrToInt(curr_value)); } pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); + return syscall4(SYS_timerfd_settime, @bitCast(usize, @as(isize, fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); } pub fn unshare(flags: usize) usize { @@ -994,7 +1042,7 @@ pub fn uname(uts: *utsname) usize { } // XXX: This should be weak -extern const __ehdr_start: elf.Ehdr = undefined; +extern const __ehdr_start: elf.Ehdr; pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize { if (builtin.link_libc) { @@ -1012,7 +1060,7 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf if (it.end()) { var info = dl_phdr_info{ .dlpi_addr = elf_base, - .dlpi_name = c"/proc/self/exe", + .dlpi_name = "/proc/self/exe", .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff), .dlpi_phnum = __ehdr_start.e_phnum, }; @@ -1055,15 +1103,23 @@ pub fn io_uring_setup(entries: u32, p: *io_uring_params) usize { } pub fn io_uring_enter(fd: i32, to_submit: u32, min_complete: u32, flags: u32, sig: ?*sigset_t) usize { - return syscall6(SYS_io_uring_enter, @bitCast(usize, isize(fd)), to_submit, min_complete, flags, @ptrToInt(sig), NSIG / 8); + return syscall6(SYS_io_uring_enter, @bitCast(usize, @as(isize, fd)), to_submit, min_complete, flags, @ptrToInt(sig), NSIG / 8); } pub fn io_uring_register(fd: i32, opcode: u32, arg: ?*const c_void, nr_args: u32) usize { - return syscall4(SYS_io_uring_register, @bitCast(usize, isize(fd)), opcode, @ptrToInt(arg), nr_args); + return syscall4(SYS_io_uring_register, @bitCast(usize, @as(isize, fd)), opcode, @ptrToInt(arg), nr_args); +} + +pub fn memfd_create(name: [*:0]const u8, flags: u32) usize { + return syscall2(SYS_memfd_create, @ptrToInt(name), flags); +} + +pub fn getrusage(who: i32, usage: *rusage) usize { + return syscall2(SYS_getrusage, @bitCast(usize, @as(isize, who)), @ptrToInt(usage)); } test "" { - if (is_the_target) { + if (builtin.os == .linux) { _ = @import("linux/test.zig"); } } diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index c7371fc28..0fbbbc408 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -88,27 +88,18 @@ pub fn syscall6( /// This matches the libc clone function. pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; -// LLVM calls this when the read-tp-hard feature is set to false. Currently, there is no way to pass -// that to llvm via zig, see https://github.com/ziglang/zig/issues/2883. -// LLVM expects libc to provide this function as __aeabi_read_tp, so it is exported if needed from special/c.zig. -pub extern fn getThreadPointer() usize { - return asm volatile ("mrc p15, 0, %[ret], c13, c0, 3" - : [ret] "=r" (-> usize) - ); -} - -pub nakedcc fn restore() void { +pub fn restore() callconv(.Naked) void { return asm volatile ("svc #0" : - : [number] "{r7}" (usize(SYS_sigreturn)) + : [number] "{r7}" (@as(usize, SYS_sigreturn)) : "memory" ); } -pub nakedcc fn restore_rt() void { +pub fn restore_rt() callconv(.Naked) void { return asm volatile ("svc #0" : - : [number] "{r7}" (usize(SYS_rt_sigreturn)) + : [number] "{r7}" (@as(usize, SYS_rt_sigreturn)) : "memory" ); } diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index b8d8a1636..f565bea48 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -90,10 +90,10 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, a pub const restore = restore_rt; -pub nakedcc fn restore_rt() void { +pub fn restore_rt() callconv(.Naked) void { return asm volatile ("svc #0" : - : [number] "{x8}" (usize(SYS_rt_sigreturn)) + : [number] "{x8}" (@as(usize, SYS_rt_sigreturn)) : "memory", "cc" ); } diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig new file mode 100644 index 000000000..7652ece43 --- /dev/null +++ b/lib/std/os/linux/i386.zig @@ -0,0 +1,119 @@ +usingnamespace @import("../bits.zig"); + +pub fn syscall0(number: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number) + : "memory" + ); +} + +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1) + : "memory" + ); +} + +pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2) + : "memory" + ); +} + +pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3) + : "memory" + ); +} + +pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4) + : "memory" + ); +} + +pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5) + : "memory" + ); +} + +pub fn syscall6( + number: usize, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { + return asm volatile ( + \\ push %%ebp + \\ mov %[arg6], %%ebp + \\ int $0x80 + \\ pop %%ebp + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5), + [arg6] "rm" (arg6) + : "memory" + ); +} + +pub fn socketcall(call: usize, args: [*]usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (@as(usize, SYS_socketcall)), + [arg1] "{ebx}" (call), + [arg2] "{ecx}" (@ptrToInt(args)) + : "memory" + ); +} + +/// This matches the libc clone function. +pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +pub fn restore() callconv(.Naked) void { + return asm volatile ("int $0x80" + : + : [number] "{eax}" (@as(usize, SYS_sigreturn)) + : "memory" + ); +} + +pub fn restore_rt() callconv(.Naked) void { + return asm volatile ("int $0x80" + : + : [number] "{eax}" (@as(usize, SYS_rt_sigreturn)) + : "memory" + ); +} diff --git a/lib/std/os/linux/mipsel.zig b/lib/std/os/linux/mipsel.zig index b9a312009..5193133f6 100644 --- a/lib/std/os/linux/mipsel.zig +++ b/lib/std/os/linux/mipsel.zig @@ -12,6 +12,25 @@ pub fn syscall0(number: usize) usize { ); } +pub fn syscall_pipe(fd: *[2]i32) usize { + return asm volatile ( + \\ .set noat + \\ .set noreorder + \\ syscall + \\ blez $7, 1f + \\ nop + \\ b 2f + \\ subu $2, $0, $2 + \\ 1: + \\ sw $2, 0($4) + \\ sw $3, 4($4) + \\ 2: + : [ret] "={$2}" (-> usize) + : [number] "{$2}" (@as(usize, SYS_pipe)) + : "memory", "cc", "$7" + ); +} + pub fn syscall1(number: usize, arg1: usize) usize { return asm volatile ( \\ syscall @@ -125,18 +144,18 @@ pub fn syscall6( /// This matches the libc clone function. pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; -pub nakedcc fn restore() void { +pub fn restore() callconv(.Naked) void { return asm volatile ("syscall" : - : [number] "{$2}" (usize(SYS_sigreturn)) + : [number] "{$2}" (@as(usize, SYS_sigreturn)) : "memory", "cc", "$7" ); } -pub nakedcc fn restore_rt() void { +pub fn restore_rt() callconv(.Naked) void { return asm volatile ("syscall" : - : [number] "{$2}" (usize(SYS_rt_sigreturn)) + : [number] "{$2}" (@as(usize, SYS_rt_sigreturn)) : "memory", "cc", "$7" ); } diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 64facede8..2259dad78 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -89,10 +89,10 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, a pub const restore = restore_rt; -pub nakedcc fn restore_rt() void { +pub fn restore_rt() callconv(.Naked) void { return asm volatile ("ecall" : - : [number] "{x17}" (usize(SYS_rt_sigreturn)) + : [number] "{x17}" (@as(usize, SYS_rt_sigreturn)) : "memory" ); } diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 97bbcc402..aa6655b7d 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -4,6 +4,7 @@ const linux = std.os.linux; const mem = std.mem; const elf = std.elf; const expect = std.testing.expect; +const fs = std.fs; test "getpid" { expect(linux.getpid() != 0); @@ -44,3 +45,33 @@ test "timer" { // TODO implicit cast from *[N]T to [*]T err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } + +test "statx" { + const tmp_file_name = "just_a_temporary_file.txt"; + var file = try fs.cwd().createFile(tmp_file_name, .{}); + defer { + file.close(); + fs.cwd().deleteFile(tmp_file_name) catch {}; + } + + var statx_buf: linux.Statx = undefined; + switch (linux.getErrno(linux.statx(file.handle, "", linux.AT_EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { + 0 => {}, + // The statx syscall was only introduced in linux 4.11 + linux.ENOSYS => return error.SkipZigTest, + else => unreachable, + } + + var stat_buf: linux.Stat = undefined; + switch (linux.getErrno(linux.fstatat(file.handle, "", &stat_buf, linux.AT_EMPTY_PATH))) { + 0 => {}, + else => unreachable, + } + + expect(stat_buf.mode == statx_buf.mode); + expect(@bitCast(u32, stat_buf.uid) == statx_buf.uid); + expect(@bitCast(u32, stat_buf.gid) == statx_buf.gid); + expect(@bitCast(u64, @as(i64, stat_buf.size)) == statx_buf.size); + expect(@bitCast(u64, @as(i64, stat_buf.blksize)) == statx_buf.blksize); + expect(@bitCast(u64, @as(i64, stat_buf.blocks)) == statx_buf.blocks); +} diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 849a9ef40..efb1e5fe0 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -109,12 +109,38 @@ const TLSImage = struct { tcb_offset: usize, dtv_offset: usize, data_offset: usize, + // Only used on the i386 architecture + gdt_entry_number: usize, }; pub var tls_image: ?TLSImage = null; pub fn setThreadPointer(addr: usize) void { switch (builtin.arch) { + .i386 => { + var user_desc = std.os.linux.user_desc{ + .entry_number = tls_image.?.gdt_entry_number, + .base_addr = addr, + .limit = 0xfffff, + .seg_32bit = 1, + .contents = 0, // Data + .read_exec_only = 0, + .limit_in_pages = 1, + .seg_not_present = 0, + .useable = 1, + }; + const rc = std.os.linux.syscall1(std.os.linux.SYS_set_thread_area, @ptrToInt(&user_desc)); + assert(rc == 0); + + const gdt_entry_number = user_desc.entry_number; + // We have to keep track of our slot as it's also needed for clone() + tls_image.?.gdt_entry_number = gdt_entry_number; + // Update the %gs selector + asm volatile ("movl %[gs_val], %%gs" + : + : [gs_val] "r" (gdt_entry_number << 3 | 3) + ); + }, .x86_64 => { const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, std.os.linux.ARCH_SET_FS, addr); assert(rc == 0); @@ -238,6 +264,7 @@ pub fn initTLS() ?*elf.Phdr { .tcb_offset = tcb_offset, .dtv_offset = dtv_offset, .data_offset = data_offset, + .gdt_entry_number = @bitCast(usize, @as(isize, -1)), }; } @@ -254,7 +281,9 @@ pub fn copyTLS(addr: usize) usize { dtv.entries = 1; dtv.tls_block[0] = addr + tls_img.data_offset + tls_dtv_offset; // Set-up the TCB - const tcb_ptr = @intToPtr(*usize, addr + tls_img.tcb_offset); + // Force the alignment to 1 byte as the TCB may start from a non-aligned + // address under the variant II model + const tcb_ptr = @intToPtr(*align(1) usize, addr + tls_img.tcb_offset); if (tls_variant == TLSVariant.VariantI) { tcb_ptr.* = addr + tls_img.dtv_offset; } else { diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index 86d54bfbf..868eb26c6 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -62,10 +62,11 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var i: usize = 0; while (i < hashtab[1]) : (i += 1) { - if (0 == (u32(1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; - if (0 == (u32(1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; - if (!mem.eql(u8, name, mem.toSliceConst(u8, strings + syms[i].st_name))) continue; + const sym_name = @ptrCast([*:0]const u8, strings + syms[i].st_name); + if (!mem.eql(u8, name, mem.toSliceConst(u8, sym_name))) continue; if (maybe_versym) |versym| { if (!checkver(maybe_verdef.?, versym[i], vername, strings)) continue; @@ -87,5 +88,6 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - return mem.eql(u8, vername, mem.toSliceConst(u8, strings + aux.vda_name)); + const vda_name = @ptrCast([*:0]const u8, strings + aux.vda_name); + return mem.eql(u8, vername, mem.toSliceConst(u8, vda_name)); } diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index e0e4687c9..d6067f9b1 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -90,10 +90,10 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, pub const restore = restore_rt; -pub nakedcc fn restore_rt() void { +pub fn restore_rt() callconv(.Naked) void { return asm volatile ("syscall" : - : [number] "{rax}" (usize(SYS_rt_sigreturn)) + : [number] "{rax}" (@as(usize, SYS_rt_sigreturn)) : "rcx", "r11", "memory" ); } diff --git a/lib/std/os/netbsd.zig b/lib/std/os/netbsd.zig index d484e7374..5f9684b96 100644 --- a/lib/std/os/netbsd.zig +++ b/lib/std/os/netbsd.zig @@ -1,5 +1,3 @@ -const builtin = @import("builtin"); const std = @import("../std.zig"); -pub const is_the_target = builtin.os == .netbsd; pub usingnamespace std.c; pub usingnamespace @import("bits.zig"); diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index d886597a3..1e51dd6f8 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" { try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); - try fs.deleteTree(a, "os_test_tmp"); - if (fs.Dir.open(a, "os_test_tmp")) |dir| { + try fs.deleteTree("os_test_tmp"); + if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); @@ -37,7 +37,7 @@ test "access file" { try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", ""); try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK); - try fs.deleteTree(a, "os_test_tmp"); + try fs.deleteTree("os_test_tmp"); } fn testThreadIdFn(thread_id: *Thread.Id) void { @@ -53,7 +53,7 @@ test "std.Thread.getCurrentId" { thread.wait(); if (Thread.use_pthreads) { expect(thread_current_id == thread_id); - } else if (os.windows.is_the_target) { + } else if (builtin.os == .windows) { expect(Thread.getCurrentId() != thread_current_id); } else { // If the thread completes very quickly, then thread_id can be 0. See the @@ -111,7 +111,7 @@ test "AtomicFile" { const content = try io.readFileAlloc(allocator, test_out_file); expect(mem.eql(u8, content, test_content)); - try fs.deleteFile(test_out_file); + try fs.cwd().deleteFile(test_out_file); } test "thread local storage" { @@ -137,7 +137,7 @@ test "getrandom" { try os.getrandom(&buf_b); // If this test fails the chance is significantly higher that there is a bug than // that two sets of 50 bytes were equal. - expect(!mem.eql(u8, buf_a, buf_b)); + expect(!mem.eql(u8, &buf_a, &buf_b)); } test "getcwd" { @@ -166,13 +166,13 @@ test "sigaltstack" { // analyzed const dl_phdr_info = if (@hasDecl(os, "dl_phdr_info")) os.dl_phdr_info else c_void; -export fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) i32 { +fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) callconv(.C) i32 { if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx) return 0; var counter = data.?; // Count how many libraries are loaded - counter.* += usize(1); + counter.* += @as(usize, 1); // The image should contain at least a PT_LOAD segment if (info.dlpi_phnum < 1) return -1; @@ -212,10 +212,46 @@ test "dl_iterate_phdr" { } test "gethostname" { - if (os.windows.is_the_target) + if (builtin.os == .windows) return error.SkipZigTest; var buf: [os.HOST_NAME_MAX]u8 = undefined; const hostname = try os.gethostname(&buf); expect(hostname.len != 0); } + +test "pipe" { + if (builtin.os == .windows) + return error.SkipZigTest; + + var fds = try os.pipe(); + try os.write(fds[1], "hello"); + var buf: [16]u8 = undefined; + expect((try os.read(fds[0], buf[0..])) == 5); + testing.expectEqualSlices(u8, buf[0..5], "hello"); + os.close(fds[1]); + os.close(fds[0]); +} + +test "argsAlloc" { + var args = try std.process.argsAlloc(std.heap.page_allocator); + std.process.argsFree(std.heap.page_allocator, args); +} + +test "memfd_create" { + // memfd_create is linux specific. + if (builtin.os != .linux) return error.SkipZigTest; + const fd = std.os.memfd_create("test", 0) catch |err| switch (err) { + // Related: https://github.com/ziglang/zig/issues/4019 + error.SystemOutdated => return error.SkipZigTest, + else => |e| return e, + }; + defer std.os.close(fd); + try std.os.write(fd, "test"); + try std.os.lseek_SET(fd, 0); + + var buf: [10]u8 = undefined; + const bytes_read = try std.os.read(fd, &buf); + expect(bytes_read == 4); + expect(mem.eql(u8, buf[0..4], "test")); +} diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index db46d50c1..1095706c9 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -1,15 +1,22 @@ +/// A protocol is an interface identified by a GUID. pub const protocols = @import("uefi/protocols.zig"); + +/// Status codes returned by EFI interfaces pub const status = @import("uefi/status.zig"); pub const tables = @import("uefi/tables.zig"); -const builtin = @import("builtin"); -pub const is_the_target = builtin.os == .uefi; +const fmt = @import("std").fmt; +/// The EFI image's handle that is passed to its entry point. pub var handle: Handle = undefined; + +/// A pointer to the EFI System Table that is passed to the EFI image's entry point. pub var system_table: *tables.SystemTable = undefined; +/// A handle to an event structure. pub const Event = *@OpaqueType(); -// GUIDs must be align(8) + +/// GUIDs must be align(8) pub const Guid = extern struct { time_low: u32, time_mid: u16, @@ -17,29 +24,83 @@ pub const Guid = extern struct { clock_seq_high_and_reserved: u8, clock_seq_low: u8, node: [6]u8, + + /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format + pub fn format( + self: @This(), + comptime f: []const u8, + options: fmt.FormatOptions, + context: var, + comptime Errors: type, + output: fn (@TypeOf(context), []const u8) Errors!void, + ) Errors!void { + if (f.len == 0) { + return fmt.format(context, Errors, output, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ + self.time_low, + self.time_mid, + self.time_high_and_version, + self.clock_seq_high_and_reserved, + self.clock_seq_low, self.node, + }); + } else { + @compileError("Unknown format character: '" ++ f ++ "'"); + } + } }; + +/// An EFI Handle represents a collection of related interfaces. pub const Handle = *@OpaqueType(); + +/// This structure represents time information. pub const Time = extern struct { + /// 1900 - 9999 year: u16, + + /// 1 - 12 month: u8, + + /// 1 - 31 day: u8, + + /// 0 - 23 hour: u8, + + /// 0 - 59 minute: u8, + + /// 0 - 59 second: u8, _pad1: u8, + + /// 0 - 999999999 nanosecond: u32, + + /// The time's offset in minutes from UTC. + /// Allowed values are -1440 to 1440 or unspecified_timezone timezone: i16, daylight: packed struct { _pad1: u6, + + /// If true, the time has been adjusted for daylight savings time. in_daylight: bool, + + /// If true, the time is affected by daylight savings time. adjust_daylight: bool, }, _pad2: u8, + /// Time is to be interpreted as local time pub const unspecified_timezone: i16 = 0x7ff; }; + +/// Capabilities of the clock device pub const TimeCapabilities = extern struct { + /// Resolution in Hz resolution: u32, + + /// Accuracy in an error rate of 1e-6 parts per million. accuracy: u32, + + /// If true, a time set operation clears the device's time below the resolution level. sets_to_zero: bool, }; diff --git a/lib/std/os/uefi/protocols.zig b/lib/std/os/uefi/protocols.zig index ff0040860..58f342ac2 100644 --- a/lib/std/os/uefi/protocols.zig +++ b/lib/std/os/uefi/protocols.zig @@ -1,6 +1,11 @@ +pub const LoadedImageProtocol = @import("protocols/loaded_image_protocol.zig").LoadedImageProtocol; + +pub const DevicePathProtocol = @import("protocols/device_path_protocol.zig").DevicePathProtocol; + pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey; pub const KeyData = @import("protocols/simple_text_input_ex_protocol.zig").KeyData; pub const KeyState = @import("protocols/simple_text_input_ex_protocol.zig").KeyState; +pub const SimpleTextInputProtocol = @import("protocols/simple_text_input_protocol.zig").SimpleTextInputProtocol; pub const SimpleTextInputExProtocol = @import("protocols/simple_text_input_ex_protocol.zig").SimpleTextInputExProtocol; pub const SimpleTextOutputMode = @import("protocols/simple_text_output_protocol.zig").SimpleTextOutputMode; @@ -29,6 +34,46 @@ pub const EdidActiveProtocol = @import("protocols/edid_active_protocol.zig").Edi pub const EdidOverrideProtocol = @import("protocols/edid_override_protocol.zig").EdidOverrideProtocol; pub const EdidOverrideProtocolAttributes = @import("protocols/edid_override_protocol.zig").EdidOverrideProtocolAttributes; +pub const SimpleNetworkProtocol = @import("protocols/simple_network_protocol.zig").SimpleNetworkProtocol; +pub const MacAddress = @import("protocols/simple_network_protocol.zig").MacAddress; +pub const SimpleNetworkMode = @import("protocols/simple_network_protocol.zig").SimpleNetworkMode; +pub const SimpleNetworkReceiveFilter = @import("protocols/simple_network_protocol.zig").SimpleNetworkReceiveFilter; +pub const SimpleNetworkState = @import("protocols/simple_network_protocol.zig").SimpleNetworkState; +pub const NetworkStatistics = @import("protocols/simple_network_protocol.zig").NetworkStatistics; +pub const SimpleNetworkInterruptStatus = @import("protocols/simple_network_protocol.zig").SimpleNetworkInterruptStatus; + +pub const ManagedNetworkServiceBindingProtocol = @import("protocols/managed_network_service_binding_protocol.zig").ManagedNetworkServiceBindingProtocol; +pub const ManagedNetworkProtocol = @import("protocols/managed_network_protocol.zig").ManagedNetworkProtocol; +pub const ManagedNetworkConfigData = @import("protocols/managed_network_protocol.zig").ManagedNetworkConfigData; +pub const ManagedNetworkCompletionToken = @import("protocols/managed_network_protocol.zig").ManagedNetworkCompletionToken; +pub const ManagedNetworkReceiveData = @import("protocols/managed_network_protocol.zig").ManagedNetworkReceiveData; +pub const ManagedNetworkTransmitData = @import("protocols/managed_network_protocol.zig").ManagedNetworkTransmitData; +pub const ManagedNetworkFragmentData = @import("protocols/managed_network_protocol.zig").ManagedNetworkFragmentData; + +pub const Ip6ServiceBindingProtocol = @import("protocols/ip6_service_binding_protocol.zig").Ip6ServiceBindingProtocol; +pub const Ip6Protocol = @import("protocols/ip6_protocol.zig").Ip6Protocol; +pub const Ip6ModeData = @import("protocols/ip6_protocol.zig").Ip6ModeData; +pub const Ip6ConfigData = @import("protocols/ip6_protocol.zig").Ip6ConfigData; +pub const Ip6Address = @import("protocols/ip6_protocol.zig").Ip6Address; +pub const Ip6AddressInfo = @import("protocols/ip6_protocol.zig").Ip6AddressInfo; +pub const Ip6RouteTable = @import("protocols/ip6_protocol.zig").Ip6RouteTable; +pub const Ip6NeighborState = @import("protocols/ip6_protocol.zig").Ip6NeighborState; +pub const Ip6NeighborCache = @import("protocols/ip6_protocol.zig").Ip6NeighborCache; +pub const Ip6IcmpType = @import("protocols/ip6_protocol.zig").Ip6IcmpType; +pub const Ip6CompletionToken = @import("protocols/ip6_protocol.zig").Ip6CompletionToken; + +pub const Ip6ConfigProtocol = @import("protocols/ip6_config_protocol.zig").Ip6ConfigProtocol; +pub const Ip6ConfigDataType = @import("protocols/ip6_config_protocol.zig").Ip6ConfigDataType; + +pub const Udp6ServiceBindingProtocol = @import("protocols/udp6_service_binding_protocol.zig").Udp6ServiceBindingProtocol; +pub const Udp6Protocol = @import("protocols/udp6_protocol.zig").Udp6Protocol; +pub const Udp6ConfigData = @import("protocols/udp6_protocol.zig").Udp6ConfigData; +pub const Udp6CompletionToken = @import("protocols/udp6_protocol.zig").Udp6CompletionToken; +pub const Udp6ReceiveData = @import("protocols/udp6_protocol.zig").Udp6ReceiveData; +pub const Udp6TransmitData = @import("protocols/udp6_protocol.zig").Udp6TransmitData; +pub const Udp6SessionData = @import("protocols/udp6_protocol.zig").Udp6SessionData; +pub const Udp6FragmentData = @import("protocols/udp6_protocol.zig").Udp6FragmentData; + pub const hii = @import("protocols/hii.zig"); pub const HIIDatabaseProtocol = @import("protocols/hii_database_protocol.zig").HIIDatabaseProtocol; pub const HIIPopupProtocol = @import("protocols/hii_popup_protocol.zig").HIIPopupProtocol; diff --git a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig index df5a93031..722972a20 100644 --- a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig +++ b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig @@ -2,17 +2,19 @@ const uefi = @import("std").os.uefi; const Event = uefi.Event; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.7 +/// Protocol for touchscreens pub const AbsolutePointerProtocol = extern struct { _reset: extern fn (*const AbsolutePointerProtocol, bool) usize, _get_state: extern fn (*const AbsolutePointerProtocol, *AbsolutePointerState) usize, wait_for_input: Event, mode: *AbsolutePointerMode, + /// Resets the pointer device hardware. pub fn reset(self: *const AbsolutePointerProtocol, verify: bool) usize { return self._reset(self, verify); } + /// Retrieves the current state of a pointer device. pub fn getState(self: *const AbsolutePointerProtocol, state: *AbsolutePointerState) usize { return self._get_state(self, state); } @@ -42,12 +44,12 @@ pub const AbsolutePointerMode = extern struct { }; pub const AbsolutePointerState = extern struct { - current_x: u64 = undefined, - current_y: u64 = undefined, - current_z: u64 = undefined, + current_x: u64, + current_y: u64, + current_z: u64, active_buttons: packed struct { touch_active: bool, alt_active: bool, _pad1: u30, - } = undefined, + }, }; diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig new file mode 100644 index 000000000..a945608f8 --- /dev/null +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -0,0 +1,17 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; + +pub const DevicePathProtocol = extern struct { + type: u8, + subtype: u8, + length: u16, + + pub const guid align(8) = Guid{ + .time_low = 0x09576e91, + .time_mid = 0x6d3f, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x39, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; +}; diff --git a/lib/std/os/uefi/protocols/edid_active_protocol.zig b/lib/std/os/uefi/protocols/edid_active_protocol.zig index 1a96cb6cd..fcdef78d8 100644 --- a/lib/std/os/uefi/protocols/edid_active_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_active_protocol.zig @@ -1,7 +1,7 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.9 +/// EDID information for an active video output device pub const EdidActiveProtocol = extern struct { size_of_edid: u32, edid: ?[*]u8, diff --git a/lib/std/os/uefi/protocols/edid_discovered_protocol.zig b/lib/std/os/uefi/protocols/edid_discovered_protocol.zig index f68b0fa3d..00b59cb6c 100644 --- a/lib/std/os/uefi/protocols/edid_discovered_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_discovered_protocol.zig @@ -1,7 +1,7 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.9 +/// EDID information for a video output device pub const EdidDiscoveredProtocol = extern struct { size_of_edid: u32, edid: ?[*]u8, diff --git a/lib/std/os/uefi/protocols/edid_override_protocol.zig b/lib/std/os/uefi/protocols/edid_override_protocol.zig index ad2eec120..c11af8f0a 100644 --- a/lib/std/os/uefi/protocols/edid_override_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_override_protocol.zig @@ -2,10 +2,11 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; const Handle = uefi.Handle; -/// UEFI Specification, Version 2.8, 12.9 +/// Override EDID information pub const EdidOverrideProtocol = extern struct { _get_edid: extern fn (*const EdidOverrideProtocol, Handle, *u32, *usize, *?[*]u8) usize, + /// Returns policy information and potentially a replacement EDID for the specified video output device. /// attributes must be align(4) pub fn getEdid(self: *const EdidOverrideProtocol, handle: Handle, attributes: *EdidOverrideProtocolAttributes, edid_size: *usize, edid: *?[*]u8) usize { return self._get_edid(self, handle, attributes, edid_size, edid); diff --git a/lib/std/os/uefi/protocols/graphics_output_protocol.zig b/lib/std/os/uefi/protocols/graphics_output_protocol.zig index 4713df050..f63f9d12d 100644 --- a/lib/std/os/uefi/protocols/graphics_output_protocol.zig +++ b/lib/std/os/uefi/protocols/graphics_output_protocol.zig @@ -1,21 +1,24 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.9 +/// Graphics output pub const GraphicsOutputProtocol = extern struct { _query_mode: extern fn (*const GraphicsOutputProtocol, u32, *usize, **GraphicsOutputModeInformation) usize, _set_mode: extern fn (*const GraphicsOutputProtocol, u32) usize, _blt: extern fn (*const GraphicsOutputProtocol, ?[*]GraphicsOutputBltPixel, GraphicsOutputBltOperation, usize, usize, usize, usize, usize, usize, usize) usize, mode: *GraphicsOutputProtocolMode, + /// Returns information for an available graphics mode that the graphics device and the set of active video output devices supports. pub fn queryMode(self: *const GraphicsOutputProtocol, mode: u32, size_of_info: *usize, info: **GraphicsOutputModeInformation) usize { return self._query_mode(self, mode, size_of_info, info); } + /// Set the video device into the specified mode and clears the visible portions of the output display to black. pub fn setMode(self: *const GraphicsOutputProtocol, mode: u32) usize { return self._set_mode(self, mode); } + /// Blt a rectangle of pixels on the graphics screen. Blt stands for BLock Transfer. pub fn blt(self: *const GraphicsOutputProtocol, blt_buffer: ?[*]GraphicsOutputBltPixel, blt_operation: GraphicsOutputBltOperation, source_x: usize, source_y: usize, destination_x: usize, destination_y: usize, width: usize, height: usize, delta: usize) usize { return self._blt(self, blt_buffer, blt_operation, source_x, source_y, destination_x, destination_y, width, height, delta); } diff --git a/lib/std/os/uefi/protocols/hii.zig b/lib/std/os/uefi/protocols/hii.zig index 64d03b57c..55326b25b 100644 --- a/lib/std/os/uefi/protocols/hii.zig +++ b/lib/std/os/uefi/protocols/hii.zig @@ -3,6 +3,7 @@ const Guid = uefi.Guid; pub const HIIHandle = *@OpaqueType(); +/// The header found at the start of each package. pub const HIIPackageHeader = packed struct { length: u24, type: u8, @@ -22,8 +23,11 @@ pub const HIIPackageHeader = packed struct { pub const type_system_end: u8 = 0xff; }; +/// The header found at the start of each package list. pub const HIIPackageList = extern struct { package_list_guid: Guid, + + /// The size of the package list (in bytes), including the header. package_list_length: u32, // TODO implement iterator diff --git a/lib/std/os/uefi/protocols/hii_database_protocol.zig b/lib/std/os/uefi/protocols/hii_database_protocol.zig index 5e0f835cf..aaacce846 100644 --- a/lib/std/os/uefi/protocols/hii_database_protocol.zig +++ b/lib/std/os/uefi/protocols/hii_database_protocol.zig @@ -2,6 +2,7 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; const hii = uefi.protocols.hii; +/// Database manager for HII-related data structures. pub const HIIDatabaseProtocol = extern struct { _new_package_list: usize, // TODO _remove_package_list: extern fn (*const HIIDatabaseProtocol, hii.HIIHandle) usize, @@ -15,18 +16,22 @@ pub const HIIDatabaseProtocol = extern struct { _set_keyboard_layout: usize, // TODO _get_package_list_handle: usize, // TODO + /// Removes a package list from the HII database. pub fn removePackageList(self: *const HIIDatabaseProtocol, handle: hii.HIIHandle) usize { return self._remove_package_list(self, handle); } + /// Update a package list in the HII database. pub fn updatePackageList(self: *const HIIDatabaseProtocol, handle: hii.HIIHandle, buffer: *const hii.HIIPackageList) usize { return self._update_package_list(self, handle, buffer); } + /// Determines the handles that are currently active in the database. pub fn listPackageLists(self: *const HIIDatabaseProtocol, package_type: u8, package_guid: ?*const Guid, buffer_length: *usize, handles: [*]hii.HIIHandle) usize { return self._list_package_lists(self, package_type, package_guid, buffer_length, handles); } + /// Exports the contents of one or all package lists in the HII database into a buffer. pub fn exportPackageLists(self: *const HIIDatabaseProtocol, handle: ?hii.HIIHandle, buffer_size: *usize, buffer: *hii.HIIPackageList) usize { return self._export_package_lists(self, handle, buffer_size, buffer); } diff --git a/lib/std/os/uefi/protocols/hii_popup_protocol.zig b/lib/std/os/uefi/protocols/hii_popup_protocol.zig index ee12d359f..3152a8b7a 100644 --- a/lib/std/os/uefi/protocols/hii_popup_protocol.zig +++ b/lib/std/os/uefi/protocols/hii_popup_protocol.zig @@ -2,10 +2,12 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; const hii = uefi.protocols.hii; +/// Display a popup window pub const HIIPopupProtocol = extern struct { revision: u64, _create_popup: extern fn (*const HIIPopupProtocol, HIIPopupStyle, HIIPopupType, hii.HIIHandle, u16, ?*HIIPopupSelection) usize, + /// Displays a popup window. pub fn createPopup(self: *const HIIPopupProtocol, style: HIIPopupStyle, popup_type: HIIPopupType, handle: hii.HIIHandle, msg: u16, user_selection: ?*HIIPopupSelection) usize { return self._create_popup(self, style, popup_type, handle, msg, user_selection); } diff --git a/lib/std/os/uefi/protocols/ip6_config_protocol.zig b/lib/std/os/uefi/protocols/ip6_config_protocol.zig new file mode 100644 index 000000000..a86e2cbba --- /dev/null +++ b/lib/std/os/uefi/protocols/ip6_config_protocol.zig @@ -0,0 +1,45 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; +const Event = uefi.Event; + +pub const Ip6ConfigProtocol = extern struct { + _set_data: extern fn (*const Ip6ConfigProtocol, Ip6ConfigDataType, usize, *const c_void) usize, + _get_data: extern fn (*const Ip6ConfigProtocol, Ip6ConfigDataType, *usize, ?*const c_void) usize, + _register_data_notify: extern fn (*const Ip6ConfigProtocol, Ip6ConfigDataType, Event) usize, + _unregister_data_notify: extern fn (*const Ip6ConfigProtocol, Ip6ConfigDataType, Event) usize, + + pub fn setData(self: *const Ip6ConfigProtocol, data_type: Ip6ConfigDataType, data_size: usize, data: *const c_void) usize { + return self._set_data(self, data_type, data_size, data); + } + + pub fn getData(self: *const Ip6ConfigProtocol, data_type: Ip6ConfigDataType, data_size: *usize, data: ?*const c_void) usize { + return self._get_data(self, data_type, data_size, data); + } + + pub fn registerDataNotify(self: *const Ip6ConfigProtocol, data_type: Ip6ConfigDataType, event: Event) usize { + return self._register_data_notify(self, data_type, event); + } + + pub fn unregisterDataNotify(self: *const Ip6ConfigProtocol, data_type: Ip6ConfigDataType, event: Event) usize { + return self._unregister_data_notify(self, data_type, event); + } + + pub const guid align(8) = Guid{ + .time_low = 0x937fe521, + .time_mid = 0x95ae, + .time_high_and_version = 0x4d1a, + .clock_seq_high_and_reserved = 0x89, + .clock_seq_low = 0x29, + .node = [_]u8{ 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a }, + }; +}; + +pub const Ip6ConfigDataType = extern enum(u32) { + InterfaceInfo, + AltInterfaceId, + Policy, + DupAddrDetectTransmits, + ManualAddress, + Gateway, + DnsServer, +}; diff --git a/lib/std/os/uefi/protocols/ip6_protocol.zig b/lib/std/os/uefi/protocols/ip6_protocol.zig new file mode 100644 index 000000000..a412dc3c7 --- /dev/null +++ b/lib/std/os/uefi/protocols/ip6_protocol.zig @@ -0,0 +1,143 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; +const Event = uefi.Event; +const MacAddress = uefi.protocols.MacAddress; +const ManagedNetworkConfigData = uefi.protocols.ManagedNetworkConfigData; +const SimpleNetworkMode = uefi.protocols.SimpleNetworkMode; + +pub const Ip6Protocol = extern struct { + _get_mode_data: extern fn (*const Ip6Protocol, ?*Ip6ModeData, ?*ManagedNetworkConfigData, ?*SimpleNetworkMode) usize, + _configure: extern fn (*const Ip6Protocol, ?*const Ip6ConfigData) usize, + _groups: extern fn (*const Ip6Protocol, bool, ?*const Ip6Address) usize, + _routes: extern fn (*const Ip6Protocol, bool, ?*const Ip6Address, u8, ?*const Ip6Address) usize, + _neighbors: extern fn (*const Ip6Protocol, bool, *const Ip6Address, ?*const MacAddress, u32, bool) usize, + _transmit: extern fn (*const Ip6Protocol, *Ip6CompletionToken) usize, + _receive: extern fn (*const Ip6Protocol, *Ip6CompletionToken) usize, + _cancel: extern fn (*const Ip6Protocol, ?*Ip6CompletionToken) usize, + _poll: extern fn (*const Ip6Protocol) usize, + + /// Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + pub fn getModeData(self: *const Ip6Protocol, ip6_mode_data: ?*Ip6ModeData, mnp_config_data: ?*ManagedNetworkConfigData, snp_mode_data: ?*SimpleNetworkMode) usize { + return self._get_mode_data(self, ip6_mode_data, mnp_config_data, snp_mode_data); + } + + /// Assign IPv6 address and other configuration parameter to this EFI IPv6 Protocol driver instance. + pub fn configure(self: *const Ip6Protocol, ip6_config_data: ?*const Ip6ConfigData) usize { + return self._configure(self, ip6_config_data); + } + + /// Joins and leaves multicast groups. + pub fn groups(self: *const Ip6Protocol, join_flag: bool, group_address: ?*const Ip6Address) usize { + return self._groups(self, join_flag, group_address); + } + + /// Adds and deletes routing table entries. + pub fn routes(self: *const Ip6Protocol, delete_route: bool, destination: ?*const Ip6Address, prefix_length: u8, gateway_address: ?*const Ip6Address) usize { + return self._routes(self, delete_route, destination, prefix_length, gateway_address); + } + + /// Add or delete Neighbor cache entries. + pub fn neighbors(self: *const Ip6Protocol, delete_flag: bool, target_ip6_address: *const Ip6Address, target_link_address: ?*const MacAddress, timeout: u32, override: bool) usize { + return self._neighbors(self, delete_flag, target_ip6_address, target_link_address, timeout, override); + } + + /// Places outgoing data packets into the transmit queue. + pub fn transmit(self: *const Ip6Protocol, token: *Ip6CompletionToken) usize { + return self._transmit(self, token); + } + + /// Places a receiving request into the receiving queue. + pub fn receive(self: *const Ip6Protocol, token: *Ip6CompletionToken) usize { + return self._receive(self, token); + } + + /// Abort an asynchronous transmits or receive request. + pub fn cancel(self: *const Ip6Protocol, token: ?*Ip6CompletionToken) usize { + return self._cancel(self, token); + } + + /// Polls for incoming data packets and processes outgoing data packets. + pub fn poll(self: *const Ip6Protocol) usize { + return self._poll(self); + } + + pub const guid align(8) = Guid{ + .time_low = 0x2c8759d5, + .time_mid = 0x5c2d, + .time_high_and_version = 0x66ef, + .clock_seq_high_and_reserved = 0x92, + .clock_seq_low = 0x5f, + .node = [_]u8{ 0xb6, 0x6c, 0x10, 0x19, 0x57, 0xe2 }, + }; +}; + +pub const Ip6ModeData = extern struct { + is_started: bool, + max_packet_size: u32, + config_data: Ip6ConfigData, + is_configured: bool, + address_count: u32, + address_list: [*]Ip6AddressInfo, + group_count: u32, + group_table: [*]Ip6Address, + route_count: u32, + route_table: [*]Ip6RouteTable, + neighbor_count: u32, + neighbor_cache: [*]Ip6NeighborCache, + prefix_count: u32, + prefix_table: [*]Ip6AddressInfo, + icmp_type_count: u32, + icmp_type_list: [*]Ip6IcmpType, +}; + +pub const Ip6ConfigData = extern struct { + default_protocol: u8, + accept_any_protocol: bool, + accept_icmp_errors: bool, + accept_promiscuous: bool, + destination_address: Ip6Address, + station_address: Ip6Address, + traffic_class: u8, + hop_limit: u8, + flow_label: u32, + receive_timeout: u32, + transmit_timeout: u32, +}; + +pub const Ip6Address = [16]u8; + +pub const Ip6AddressInfo = extern struct { + address: Ip6Address, + prefix_length: u8, +}; + +pub const Ip6RouteTable = extern struct { + gateway: Ip6Address, + destination: Ip6Address, + prefix_length: u8, +}; + +pub const Ip6NeighborState = extern enum(u32) { + Incomplete, + Reachable, + Stale, + Delay, + Probe, +}; + +pub const Ip6NeighborCache = extern struct { + neighbor: Ip6Address, + link_address: MacAddress, + state: Ip6NeighborState, +}; + +pub const Ip6IcmpType = extern struct { + type: u8, + code: u8, +}; + +pub const Ip6CompletionToken = extern struct { + event: Event, + status: usize, + packet: *c_void, // union TODO +}; diff --git a/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig b/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig new file mode 100644 index 000000000..9ecbb2960 --- /dev/null +++ b/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig @@ -0,0 +1,25 @@ +const uefi = @import("std").os.uefi; +const Handle = uefi.Handle; +const Guid = uefi.Guid; + +pub const Ip6ServiceBindingProtocol = extern struct { + _create_child: extern fn (*const Ip6ServiceBindingProtocol, *?Handle) usize, + _destroy_child: extern fn (*const Ip6ServiceBindingProtocol, Handle) usize, + + pub fn createChild(self: *const Ip6ServiceBindingProtocol, handle: *?Handle) usize { + return self._create_child(self, handle); + } + + pub fn destroyChild(self: *const Ip6ServiceBindingProtocol, handle: Handle) usize { + return self._destroy_child(self, handle); + } + + pub const guid align(8) = Guid{ + .time_low = 0xec835dd3, + .time_mid = 0xfe0f, + .time_high_and_version = 0x617b, + .clock_seq_high_and_reserved = 0xa6, + .clock_seq_low = 0x21, + .node = [_]u8{ 0xb3, 0x50, 0xc3, 0xe1, 0x33, 0x88 }, + }; +}; diff --git a/lib/std/os/uefi/protocols/loaded_image_protocol.zig b/lib/std/os/uefi/protocols/loaded_image_protocol.zig new file mode 100644 index 000000000..b7b281e24 --- /dev/null +++ b/lib/std/os/uefi/protocols/loaded_image_protocol.zig @@ -0,0 +1,36 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; +const Handle = uefi.Handle; +const SystemTable = uefi.tables.SystemTable; +const MemoryType = uefi.tables.MemoryType; +const DevicePathProtocol = uefi.protocols.DevicePathProtocol; + +pub const LoadedImageProtocol = extern struct { + revision: u32, + parent_handle: Handle, + system_table: *SystemTable, + device_handle: ?Handle, + file_path: *DevicePathProtocol, + reserved: *c_void, + load_options_size: u32, + load_options: *c_void, + image_base: [*]u8, + image_size: u64, + image_code_type: MemoryType, + image_data_type: MemoryType, + _unload: extern fn (*const LoadedImageProtocol, Handle) usize, + + /// Unloads an image from memory. + pub fn unload(self: *const LoadedImageProtocol, handle: Handle) usize { + return self._unload(self, handle); + } + + pub const guid align(8) = Guid{ + .time_low = 0x5b1b31a1, + .time_mid = 0x9562, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x3f, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; +}; diff --git a/lib/std/os/uefi/protocols/managed_network_protocol.zig b/lib/std/os/uefi/protocols/managed_network_protocol.zig new file mode 100644 index 000000000..a2cf6e2ec --- /dev/null +++ b/lib/std/os/uefi/protocols/managed_network_protocol.zig @@ -0,0 +1,126 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; +const Event = uefi.Event; +const Time = uefi.Time; +const SimpleNetworkMode = uefi.protocols.SimpleNetworkMode; +const MacAddress = uefi.protocols.MacAddress; + +pub const ManagedNetworkProtocol = extern struct { + _get_mode_data: extern fn (*const ManagedNetworkProtocol, ?*ManagedNetworkConfigData, ?*SimpleNetworkMode) usize, + _configure: extern fn (*const ManagedNetworkProtocol, ?*const ManagedNetworkConfigData) usize, + _mcast_ip_to_mac: extern fn (*const ManagedNetworkProtocol, bool, *const c_void, *MacAddress) usize, + _groups: extern fn (*const ManagedNetworkProtocol, bool, ?*const MacAddress) usize, + _transmit: extern fn (*const ManagedNetworkProtocol, *const ManagedNetworkCompletionToken) usize, + _receive: extern fn (*const ManagedNetworkProtocol, *const ManagedNetworkCompletionToken) usize, + _cancel: extern fn (*const ManagedNetworkProtocol, ?*const ManagedNetworkCompletionToken) usize, + _poll: extern fn (*const ManagedNetworkProtocol) usize, + + /// Returns the operational parameters for the current MNP child driver. + /// May also support returning the underlying SNP driver mode data. + pub fn getModeData(self: *const ManagedNetworkProtocol, mnp_config_data: ?*ManagedNetworkConfigData, snp_mode_data: ?*SimpleNetworkMode) usize { + return self._get_mode_data(self, mnp_config_data, snp_mode_data); + } + + /// Sets or clears the operational parameters for the MNP child driver. + pub fn configure(self: *const ManagedNetworkProtocol, mnp_config_data: ?*const ManagedNetworkConfigData) usize { + return self._configure(self, mnp_config_data); + } + + /// Translates an IP multicast address to a hardware (MAC) multicast address. + /// This function may be unsupported in some MNP implementations. + pub fn mcastIpToMac(self: *const ManagedNetworkProtocol, ipv6flag: bool, ipaddress: *const c_void, mac_address: *MacAddress) usize { + return self._mcast_ip_to_mac(self, ipv6flag, ipaddress); + } + + /// Enables and disables receive filters for multicast address. + /// This function may be unsupported in some MNP implementations. + pub fn groups(self: *const ManagedNetworkProtocol, join_flag: bool, mac_address: ?*const MacAddress) usiz { + return self._groups(self, join_flag, mac_address); + } + + /// Places asynchronous outgoing data packets into the transmit queue. + pub fn transmit(self: *const ManagedNetworkProtocol, token: *const ManagedNetworkCompletionToken) usize { + return self._transmit(self, token); + } + + /// Places an asynchronous receiving request into the receiving queue. + pub fn receive(self: *const ManagedNetworkProtocol, token: *const ManagedNetworkCompletionToken) usize { + return self._receive(self, token); + } + + /// Aborts an asynchronous transmit or receive request. + pub fn cancel(self: *const ManagedNetworkProtocol, token: ?*const ManagedNetworkCompletionToken) usize { + return self._cancel(self, token); + } + + /// Polls for incoming data packets and processes outgoing data packets. + pub fn poll(self: *const ManagedNetworkProtocol) usize { + return self._poll(self); + } + + pub const guid align(8) = Guid{ + .time_low = 0x7ab33a91, + .time_mid = 0xace5, + .time_high_and_version = 0x4326, + .clock_seq_high_and_reserved = 0xb5, + .clock_seq_low = 0x72, + .node = [_]u8{ 0xe7, 0xee, 0x33, 0xd3, 0x9f, 0x16 }, + }; +}; + +pub const ManagedNetworkConfigData = extern struct { + received_queue_timeout_value: u32, + transmit_queue_timeout_value: u32, + protocol_type_filter: u16, + enable_unicast_receive: bool, + enable_multicast_receive: bool, + enable_broadcast_receive: bool, + enable_promiscuous_receive: bool, + flush_queues_on_reset: bool, + enable_receive_timestamps: bool, + disable_background_polling: bool, +}; + +pub const ManagedNetworkCompletionToken = extern struct { + event: Event, + status: usize, + packet: extern union { + RxData: *ManagedNetworkReceiveData, + TxData: *ManagedNetworkTransmitData, + }, +}; + +pub const ManagedNetworkReceiveData = extern struct { + timestamp: Time, + recycle_event: Event, + packet_length: u32, + header_length: u32, + address_length: u32, + data_length: u32, + broadcast_flag: bool, + multicast_flag: bool, + promiscuous_flag: bool, + protocol_type: u16, + destination_address: [*]u8, + source_address: [*]u8, + media_header: [*]u8, + packet_data: [*]u8, +}; + +pub const ManagedNetworkTransmitData = extern struct { + destination_address: ?*MacAddress, + source_address: ?*MacAddress, + protocol_type: u16, + data_length: u32, + header_length: u16, + fragment_count: u16, + + pub fn getFragments(self: *ManagedNetworkTransmitData) []ManagedNetworkFragmentData { + return @ptrCast([*]ManagedNetworkFragmentData, @ptrCast([*]u8, self) + @sizeOf(ManagedNetworkTransmitData))[0..self.fragment_count]; + } +}; + +pub const ManagedNetworkFragmentData = extern struct { + fragment_length: u32, + fragment_buffer: [*]u8, +}; diff --git a/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig b/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig new file mode 100644 index 000000000..492fe450b --- /dev/null +++ b/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig @@ -0,0 +1,25 @@ +const uefi = @import("std").os.uefi; +const Handle = uefi.Handle; +const Guid = uefi.Guid; + +pub const ManagedNetworkServiceBindingProtocol = extern struct { + _create_child: extern fn (*const ManagedNetworkServiceBindingProtocol, *?Handle) usize, + _destroy_child: extern fn (*const ManagedNetworkServiceBindingProtocol, Handle) usize, + + pub fn createChild(self: *const ManagedNetworkServiceBindingProtocol, handle: *?Handle) usize { + return self._create_child(self, handle); + } + + pub fn destroyChild(self: *const ManagedNetworkServiceBindingProtocol, handle: Handle) usize { + return self._destroy_child(self, handle); + } + + pub const guid align(8) = Guid{ + .time_low = 0xf36ff770, + .time_mid = 0xa7e1, + .time_high_and_version = 0x42cf, + .clock_seq_high_and_reserved = 0x9e, + .clock_seq_low = 0xd2, + .node = [_]u8{ 0x56, 0xf0, 0xf2, 0x71, 0xf4, 0x4c }, + }; +}; diff --git a/lib/std/os/uefi/protocols/rng_protocol.zig b/lib/std/os/uefi/protocols/rng_protocol.zig index f7c756fb0..65eb882af 100644 --- a/lib/std/os/uefi/protocols/rng_protocol.zig +++ b/lib/std/os/uefi/protocols/rng_protocol.zig @@ -1,15 +1,17 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 37.5 +/// Random Number Generator protocol pub const RNGProtocol = extern struct { _get_info: extern fn (*const RNGProtocol, *usize, [*]align(8) Guid) usize, _get_rng: extern fn (*const RNGProtocol, ?*align(8) const Guid, usize, [*]u8) usize, + /// Returns information about the random number generation implementation. pub fn getInfo(self: *const RNGProtocol, list_size: *usize, list: [*]align(8) Guid) usize { return self._get_info(self, list_size, list); } + /// Produces and returns an RNG value using either the default or specified RNG algorithm. pub fn getRNG(self: *const RNGProtocol, algo: ?*align(8) const Guid, value_length: usize, value: [*]u8) usize { return self._get_rng(self, algo, value_length, value); } diff --git a/lib/std/os/uefi/protocols/simple_network_protocol.zig b/lib/std/os/uefi/protocols/simple_network_protocol.zig new file mode 100644 index 000000000..f5e62734b --- /dev/null +++ b/lib/std/os/uefi/protocols/simple_network_protocol.zig @@ -0,0 +1,172 @@ +const uefi = @import("std").os.uefi; +const Event = uefi.Event; +const Guid = uefi.Guid; + +pub const SimpleNetworkProtocol = extern struct { + revision: u64, + _start: extern fn (*const SimpleNetworkProtocol) usize, + _stop: extern fn (*const SimpleNetworkProtocol) usize, + _initialize: extern fn (*const SimpleNetworkProtocol, usize, usize) usize, + _reset: extern fn (*const SimpleNetworkProtocol, bool) usize, + _shutdown: extern fn (*const SimpleNetworkProtocol) usize, + _receive_filters: extern fn (*const SimpleNetworkProtocol, SimpleNetworkReceiveFilter, SimpleNetworkReceiveFilter, bool, usize, ?[*]const MacAddress) usize, + _station_address: extern fn (*const SimpleNetworkProtocol, bool, ?*const MacAddress) usize, + _statistics: extern fn (*const SimpleNetworkProtocol, bool, ?*usize, ?*NetworkStatistics) usize, + _mcast_ip_to_mac: extern fn (*const SimpleNetworkProtocol, bool, *const c_void, *MacAddress) usize, + _nvdata: extern fn (*const SimpleNetworkProtocol, bool, usize, usize, [*]u8) usize, + _get_status: extern fn (*const SimpleNetworkProtocol, *SimpleNetworkInterruptStatus, ?*?[*]u8) usize, + _transmit: extern fn (*const SimpleNetworkProtocol, usize, usize, [*]const u8, ?*const MacAddress, ?*const MacAddress, ?*const u16) usize, + _receive: extern fn (*const SimpleNetworkProtocol, ?*usize, *usize, [*]u8, ?*MacAddress, ?*MacAddress, ?*u16) usize, + wait_for_packet: Event, + mode: *SimpleNetworkMode, + + /// Changes the state of a network interface from "stopped" to "started". + pub fn start(self: *const SimpleNetworkProtocol) usize { + return self._start(self); + } + + /// Changes the state of a network interface from "started" to "stopped". + pub fn stop(self: *const SimpleNetworkProtocol) usize { + return self._stop(self); + } + + /// Resets a network adapter and allocates the transmit and receive buffers required by the network interface. + pub fn initialize(self: *const SimpleNetworkProtocol, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) usize { + return self._initialize(self, extra_rx_buffer_size, extra_tx_buffer_size); + } + + /// Resets a network adapter and reinitializes it with the parameters that were provided in the previous call to initialize(). + pub fn reset(self: *const SimpleNetworkProtocol, extended_verification: bool) usize { + return self._reset(self, extended_verification); + } + + /// Resets a network adapter and leaves it in a state that is safe for another driver to initialize. + pub fn shutdown(self: *const SimpleNetworkProtocol) usize { + return self._shutdown(self); + } + + /// Manages the multicast receive filters of a network interface. + pub fn receiveFilters(self: *const SimpleNetworkProtocol, enable: SimpleNetworkReceiveFilter, disable: SimpleNetworkReceiveFilter, reset_mcast_filter: bool, mcast_filter_cnt: usize, mcast_filter: ?[*]const MacAddress) usize { + return self._receive_filters(self, enable, disable, reset_mcast_filter, mcast_filter_cnt, mcast_filter); + } + + /// Modifies or resets the current station address, if supported. + pub fn stationAddress(self: *const SimpleNetworkProtocol, reset: bool, new: ?*const MacAddress) usize { + return self._station_address(self, reset, new); + } + + /// Resets or collects the statistics on a network interface. + pub fn statistics(self: *const SimpleNetworkProtocol, reset_: bool, statistics_size: ?*usize, statistics_table: ?*NetworkStatistics) usize { + return self._statistics(self, reset_, statistics_size, statistics_table); + } + + /// Converts a multicast IP address to a multicast HW MAC address. + pub fn mcastIpToMac(self: *const SimpleNetworkProtocol, ipv6: bool, ip: *const c_void, mac: *MacAddress) usize { + return self._mcast_ip_to_mac(self, ipv6, ip, mac); + } + + /// Performs read and write operations on the NVRAM device attached to a network interface. + pub fn nvdata(self: *const SimpleNetworkProtocol, read_write: bool, offset: usize, buffer_size: usize, buffer: [*]u8) usize { + return self._nvdata(self, read_write, offset, buffer_size, buffer); + } + + /// Reads the current interrupt status and recycled transmit buffer status from a network interface. + pub fn getStatus(self: *const SimpleNetworkProtocol, interrupt_status: *SimpleNetworkInterruptStatus, tx_buf: ?*?[*]u8) usize { + return self._get_status(self, interrupt_status, tx_buf); + } + + /// Places a packet in the transmit queue of a network interface. + pub fn transmit(self: *const SimpleNetworkProtocol, header_size: usize, buffer_size: usize, buffer: [*]const u8, src_addr: ?*const MacAddress, dest_addr: ?*const MacAddress, protocol: ?*const u16) usize { + return self._transmit(self, header_size, buffer_size, buffer, src_addr, dest_addr, protocol); + } + + /// Receives a packet from a network interface. + pub fn receive(self: *const SimpleNetworkProtocol, header_size: ?*usize, buffer_size: *usize, buffer: [*]u8, src_addr: ?*MacAddress, dest_addr: ?*MacAddress, protocol: ?*u16) usize { + return self._receive(self, header_size, buffer_size, buffer, src_addr, dest_addr, protocol); + } + + pub const guid align(8) = Guid{ + .time_low = 0xa19832b9, + .time_mid = 0xac25, + .time_high_and_version = 0x11d3, + .clock_seq_high_and_reserved = 0x9a, + .clock_seq_low = 0x2d, + .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, + }; +}; + +pub const MacAddress = [32]u8; + +pub const SimpleNetworkMode = extern struct { + state: SimpleNetworkState, + hw_address_size: u32, + media_header_size: u32, + max_packet_size: u32, + nvram_size: u32, + nvram_access_size: u32, + receive_filter_mask: SimpleNetworkReceiveFilter, + receive_filter_setting: SimpleNetworkReceiveFilter, + max_mcast_filter_count: u32, + mcast_filter_count: u32, + mcast_filter: [16]MacAddress, + current_address: MacAddress, + broadcast_address: MacAddress, + permanent_address: MacAddress, + if_type: u8, + mac_address_changeable: bool, + multiple_tx_supported: bool, + media_present_supported: bool, + media_present: bool, +}; + +pub const SimpleNetworkReceiveFilter = packed struct { + receive_unicast: bool, + receive_multicast: bool, + receive_broadcast: bool, + receive_promiscuous: bool, + receive_promiscuous_multicast: bool, + _pad: u27 = undefined, +}; + +pub const SimpleNetworkState = extern enum(u32) { + Stopped, + Started, + Initialized, +}; + +pub const NetworkStatistics = extern struct { + rx_total_frames: u64, + rx_good_frames: u64, + rx_undersize_frames: u64, + rx_oversize_frames: u64, + rx_dropped_frames: u64, + rx_unicast_frames: u64, + rx_broadcast_frames: u64, + rx_multicast_frames: u64, + rx_crc_error_frames: u64, + rx_total_bytes: u64, + tx_total_frames: u64, + tx_good_frames: u64, + tx_undersize_frames: u64, + tx_oversize_frames: u64, + tx_dropped_frames: u64, + tx_unicast_frames: u64, + tx_broadcast_frames: u64, + tx_multicast_frames: u64, + tx_crc_error_frames: u64, + tx_total_bytes: u64, + collisions: u64, + unsupported_protocol: u64, + rx_duplicated_frames: u64, + rx_decryptError_frames: u64, + tx_error_frames: u64, + tx_retry_frames: u64, +}; + +pub const SimpleNetworkInterruptStatus = packed struct { + receive_interrupt: bool, + transmit_interrupt: bool, + command_interrupt: bool, + software_interrupt: bool, + _pad: u28, +}; diff --git a/lib/std/os/uefi/protocols/simple_pointer_protocol.zig b/lib/std/os/uefi/protocols/simple_pointer_protocol.zig index 369bc76aa..e0cb4ba35 100644 --- a/lib/std/os/uefi/protocols/simple_pointer_protocol.zig +++ b/lib/std/os/uefi/protocols/simple_pointer_protocol.zig @@ -2,17 +2,19 @@ const uefi = @import("std").os.uefi; const Event = uefi.Event; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.5 +/// Protocol for mice pub const SimplePointerProtocol = struct { _reset: extern fn (*const SimplePointerProtocol, bool) usize, _get_state: extern fn (*const SimplePointerProtocol, *SimplePointerState) usize, wait_for_input: Event, mode: *SimplePointerMode, + /// Resets the pointer device hardware. pub fn reset(self: *const SimplePointerProtocol, verify: bool) usize { return self._reset(self, verify); } + /// Retrieves the current state of a pointer device. pub fn getState(self: *const SimplePointerProtocol, state: *SimplePointerState) usize { return self._get_state(self, state); } diff --git a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig index 5507b8950..e2c4be784 100644 --- a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig +++ b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig @@ -2,7 +2,7 @@ const uefi = @import("std").os.uefi; const Event = uefi.Event; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.3 +/// Character input devices, e.g. Keyboard pub const SimpleTextInputExProtocol = extern struct { _reset: extern fn (*const SimpleTextInputExProtocol, bool) usize, _read_key_stroke_ex: extern fn (*const SimpleTextInputExProtocol, *KeyData) usize, @@ -11,22 +11,27 @@ pub const SimpleTextInputExProtocol = extern struct { _register_key_notify: extern fn (*const SimpleTextInputExProtocol, *const KeyData, extern fn (*const KeyData) usize, **c_void) usize, _unregister_key_notify: extern fn (*const SimpleTextInputExProtocol, *const c_void) usize, + /// Resets the input device hardware. pub fn reset(self: *const SimpleTextInputExProtocol, verify: bool) usize { return self._reset(self, verify); } + /// Reads the next keystroke from the input device. pub fn readKeyStrokeEx(self: *const SimpleTextInputExProtocol, key_data: *KeyData) usize { return self._read_key_stroke_ex(self, key_data); } + /// Set certain state for the input device. pub fn setState(self: *const SimpleTextInputExProtocol, state: *const u8) usize { return self._set_state(self, state); } + /// Register a notification function for a particular keystroke for the input device. pub fn registerKeyNotify(self: *const SimpleTextInputExProtocol, key_data: *const KeyData, notify: extern fn (*const KeyData) usize, handle: **c_void) usize { return self._register_key_notify(self, key_data, notify, handle); } + /// Remove the notification that was previously registered. pub fn unregisterKeyNotify(self: *const SimpleTextInputExProtocol, handle: *const c_void) usize { return self._unregister_key_notify(self, handle); } diff --git a/lib/std/os/uefi/protocols/simple_text_input_protocol.zig b/lib/std/os/uefi/protocols/simple_text_input_protocol.zig new file mode 100644 index 000000000..b56ff728e --- /dev/null +++ b/lib/std/os/uefi/protocols/simple_text_input_protocol.zig @@ -0,0 +1,29 @@ +const uefi = @import("std").os.uefi; +const Event = uefi.Event; +const Guid = uefi.Guid; + +/// Character input devices, e.g. Keyboard +pub const SimpleTextInputProtocol = extern struct { + _reset: extern fn (*const SimpleTextInputProtocol, bool) usize, + _read_key_stroke: extern fn (*const SimpleTextInputProtocol, *uefi.protocols.InputKey) usize, + wait_for_key: Event, + + /// Resets the input device hardware. + pub fn reset(self: *const SimpleTextInputProtocol, verify: bool) usize { + return self._reset(self, verify); + } + + /// Reads the next keystroke from the input device. + pub fn readKeyStroke(self: *const SimpleTextInputProtocol, input_key: *uefi.protocols.InputKey) usize { + return self._read_key_stroke(self, input_key); + } + + pub const guid align(8) = Guid{ + .time_low = 0x387477c1, + .time_mid = 0x69c7, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x39, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; +}; diff --git a/lib/std/os/uefi/protocols/simple_text_output_protocol.zig b/lib/std/os/uefi/protocols/simple_text_output_protocol.zig index e6b2e21c7..2a643ec83 100644 --- a/lib/std/os/uefi/protocols/simple_text_output_protocol.zig +++ b/lib/std/os/uefi/protocols/simple_text_output_protocol.zig @@ -1,11 +1,11 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 12.4 +/// Character output devices pub const SimpleTextOutputProtocol = extern struct { _reset: extern fn (*const SimpleTextOutputProtocol, bool) usize, - _output_string: extern fn (*const SimpleTextOutputProtocol, [*]const u16) usize, - _test_string: extern fn (*const SimpleTextOutputProtocol, [*]const u16) usize, + _output_string: extern fn (*const SimpleTextOutputProtocol, [*:0]const u16) usize, + _test_string: extern fn (*const SimpleTextOutputProtocol, [*:0]const u16) usize, _query_mode: extern fn (*const SimpleTextOutputProtocol, usize, *usize, *usize) usize, _set_mode: extern fn (*const SimpleTextOutputProtocol, usize) usize, _set_attribute: extern fn (*const SimpleTextOutputProtocol, usize) usize, @@ -14,38 +14,47 @@ pub const SimpleTextOutputProtocol = extern struct { _enable_cursor: extern fn (*const SimpleTextOutputProtocol, bool) usize, mode: *SimpleTextOutputMode, + /// Resets the text output device hardware. pub fn reset(self: *const SimpleTextOutputProtocol, verify: bool) usize { return self._reset(self, verify); } - pub fn outputString(self: *const SimpleTextOutputProtocol, msg: [*]const u16) usize { + /// Writes a string to the output device. + pub fn outputString(self: *const SimpleTextOutputProtocol, msg: [*:0]const u16) usize { return self._output_string(self, msg); } - pub fn testString(self: *const SimpleTextOutputProtocol, msg: [*]const u16) usize { + /// Verifies that all characters in a string can be output to the target device. + pub fn testString(self: *const SimpleTextOutputProtocol, msg: [*:0]const u16) usize { return self._test_string(self, msg); } + /// Returns information for an available text mode that the output device(s) supports. pub fn queryMode(self: *const SimpleTextOutputProtocol, mode_number: usize, columns: *usize, rows: *usize) usize { return self._query_mode(self, mode_number, columns, rows); } + /// Sets the output device(s) to a specified mode. pub fn setMode(self: *const SimpleTextOutputProtocol, mode_number: usize) usize { return self._set_mode(self, mode_number); } + /// Sets the background and foreground colors for the outputString() and clearScreen() functions. pub fn setAttribute(self: *const SimpleTextOutputProtocol, attribute: usize) usize { return self._set_attribute(self, attribute); } + /// Clears the output device(s) display to the currently selected background color. pub fn clearScreen(self: *const SimpleTextOutputProtocol) usize { return self._clear_screen(self); } + /// Sets the current coordinates of the cursor position. pub fn setCursorPosition(self: *const SimpleTextOutputProtocol, column: usize, row: usize) usize { return self._set_cursor_position(self, column, row); } + /// Makes the cursor visible or invisible. pub fn enableCursor(self: *const SimpleTextOutputProtocol, visible: bool) usize { return self._enable_cursor(self, visible); } diff --git a/lib/std/os/uefi/protocols/udp6_protocol.zig b/lib/std/os/uefi/protocols/udp6_protocol.zig new file mode 100644 index 000000000..266f2964f --- /dev/null +++ b/lib/std/os/uefi/protocols/udp6_protocol.zig @@ -0,0 +1,112 @@ +const uefi = @import("std").os.uefi; +const Guid = uefi.Guid; +const Event = uefi.Event; +const Time = uefi.Time; +const Ip6ModeData = uefi.protocols.Ip6ModeData; +const Ip6Address = uefi.protocols.Ip6Address; +const ManagedNetworkConfigData = uefi.protocols.ManagedNetworkConfigData; +const SimpleNetworkMode = uefi.protocols.SimpleNetworkMode; + +pub const Udp6Protocol = extern struct { + _get_mode_data: extern fn (*const Udp6Protocol, ?*Udp6ConfigData, ?*Ip6ModeData, ?*ManagedNetworkConfigData, ?*SimpleNetworkMode) usize, + _configure: extern fn (*const Udp6Protocol, ?*const Udp6ConfigData) usize, + _groups: extern fn (*const Udp6Protocol, bool, ?*const Ip6Address) usize, + _transmit: extern fn (*const Udp6Protocol, *Udp6CompletionToken) usize, + _receive: extern fn (*const Udp6Protocol, *Udp6CompletionToken) usize, + _cancel: extern fn (*const Udp6Protocol, ?*Udp6CompletionToken) usize, + _poll: extern fn (*const Udp6Protocol) usize, + + pub fn getModeData(self: *const Udp6Protocol, udp6_config_data: ?*Udp6ConfigData, ip6_mode_data: ?*Ip6ModeData, mnp_config_data: ?*ManagedNetworkConfigData, snp_mode_data: ?*SimpleNetworkMode) usize { + return self._get_mode_data(self, udp6_config_data, ip6_mode_data, mnp_config_data, snp_mode_data); + } + + pub fn configure(self: *const Udp6Protocol, udp6_config_data: ?*const Udp6ConfigData) usize { + return self._configure(self, udp6_config_data); + } + + pub fn groups(self: *const Udp6Protocol, join_flag: bool, multicast_address: ?*const Ip6Address) usize { + return self._groups(self, join_flag, multicast_address); + } + + pub fn transmit(self: *const Udp6Protocol, token: *Udp6CompletionToken) usize { + return self._transmit(self, token); + } + + pub fn receive(self: *const Udp6Protocol, token: *Udp6CompletionToken) usize { + return self._receive(self, token); + } + + pub fn cancel(self: *const Udp6Protocol, token: ?*Udp6CompletionToken) usize { + return self._cancel(self, token); + } + + pub fn poll(self: *const Udp6Protocol) usize { + return self._poll(self); + } + + pub const guid align(8) = uefi.Guid{ + .time_low = 0x4f948815, + .time_mid = 0xb4b9, + .time_high_and_version = 0x43cb, + .clock_seq_high_and_reserved = 0x8a, + .clock_seq_low = 0x33, + .node = [_]u8{ 0x90, 0xe0, 0x60, 0xb3, 0x49, 0x55 }, + }; +}; + +pub const Udp6ConfigData = extern struct { + accept_promiscuous: bool, + accept_any_port: bool, + allow_duplicate_port: bool, + traffic_class: u8, + hop_limit: u8, + receive_timeout: u32, + transmit_timeout: u32, + station_address: Ip6Address, + station_port: u16, + remote_address: Ip6Address, + remote_port: u16, +}; + +pub const Udp6CompletionToken = extern struct { + event: Event, + status: usize, + packet: extern union { + RxData: *Udp6ReceiveData, + TxData: *Udp6TransmitData, + }, +}; + +pub const Udp6ReceiveData = extern struct { + timestamp: Time, + recycle_signal: Event, + udp6_session: Udp6SessionData, + data_length: u32, + fragment_count: u32, + + pub fn getFragments(self: *Udp6ReceiveData) []Udp6FragmentData { + return @ptrCast([*]Udp6FragmentData, @ptrCast([*]u8, self) + @sizeOf(Udp6ReceiveData))[0..self.fragment_count]; + } +}; + +pub const Udp6TransmitData = extern struct { + udp6_session_data: ?*Udp6SessionData, + data_length: u32, + fragment_count: u32, + + pub fn getFragments(self: *Udp6TransmitData) []Udp6FragmentData { + return @ptrCast([*]Udp6FragmentData, @ptrCast([*]u8, self) + @sizeOf(Udp6TransmitData))[0..self.fragment_count]; + } +}; + +pub const Udp6SessionData = extern struct { + source_address: Ip6Address, + source_port: u16, + destination_address: Ip6Address, + destination_port: u16, +}; + +pub const Udp6FragmentData = extern struct { + fragment_length: u32, + fragment_buffer: [*]u8, +}; diff --git a/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig b/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig new file mode 100644 index 000000000..2f499b50c --- /dev/null +++ b/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig @@ -0,0 +1,25 @@ +const uefi = @import("std").os.uefi; +const Handle = uefi.Handle; +const Guid = uefi.Guid; + +pub const Udp6ServiceBindingProtocol = extern struct { + _create_child: extern fn (*const Udp6ServiceBindingProtocol, *?Handle) usize, + _destroy_child: extern fn (*const Udp6ServiceBindingProtocol, Handle) usize, + + pub fn createChild(self: *const Udp6ServiceBindingProtocol, handle: *?Handle) usize { + return self._create_child(self, handle); + } + + pub fn destroyChild(self: *const Udp6ServiceBindingProtocol, handle: Handle) usize { + return self._destroy_child(self, handle); + } + + pub const guid align(8) = Guid{ + .time_low = 0x66ed4721, + .time_mid = 0x3c98, + .time_high_and_version = 0x4d3e, + .clock_seq_high_and_reserved = 0x81, + .clock_seq_low = 0xe3, + .node = [_]u8{ 0xd0, 0x3d, 0xd3, 0x9a, 0x72, 0x54 }, + }; +}; diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig index 6deb741d0..171f3cb05 100644 --- a/lib/std/os/uefi/status.zig +++ b/lib/std/os/uefi/status.zig @@ -1,46 +1,124 @@ const high_bit = 1 << @typeInfo(usize).Int.bits - 1; -/// UEFI Specification, Version 2.8, Appendix D +/// The operation completed successfully. pub const success: usize = 0; +/// The image failed to load. pub const load_error: usize = high_bit | 1; + +/// A parameter was incorrect. pub const invalid_parameter: usize = high_bit | 2; + +/// The operation is not supported. pub const unsupported: usize = high_bit | 3; + +/// The buffer was not the proper size for the request. pub const bad_buffer_size: usize = high_bit | 4; + +/// The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs. pub const buffer_too_small: usize = high_bit | 5; + +/// There is no data pending upon return. pub const not_ready: usize = high_bit | 6; + +/// The physical device reported an error while attempting the operation. pub const device_error: usize = high_bit | 7; + +/// The device cannot be written to. pub const write_protected: usize = high_bit | 8; + +/// A resource has run out. pub const out_of_resources: usize = high_bit | 9; + +/// An inconstancy was detected on the file system causing the operating to fail. pub const volume_corrupted: usize = high_bit | 10; + +/// There is no more space on the file system. pub const volume_full: usize = high_bit | 11; + +/// The device does not contain any medium to perform the operation. pub const no_media: usize = high_bit | 12; + +/// The medium in the device has changed since the last access. pub const media_changed: usize = high_bit | 13; + +/// The item was not found. pub const not_found: usize = high_bit | 14; + +/// Access was denied. pub const access_denied: usize = high_bit | 15; + +/// The server was not found or did not respond to the request. pub const no_response: usize = high_bit | 16; + +/// A mapping to a device does not exist. pub const no_mapping: usize = high_bit | 17; + +/// The timeout time expired. pub const timeout: usize = high_bit | 18; + +/// The protocol has not been started. pub const not_started: usize = high_bit | 19; + +/// The protocol has already been started. pub const already_started: usize = high_bit | 20; + +/// The operation was aborted. pub const aborted: usize = high_bit | 21; + +/// An ICMP error occurred during the network operation. pub const icmp_error: usize = high_bit | 22; + +/// A TFTP error occurred during the network operation. pub const tftp_error: usize = high_bit | 23; + +/// A protocol error occurred during the network operation. pub const protocol_error: usize = high_bit | 24; + +/// The function encountered an internal version that was incompatible with a version requested by the caller. pub const incompatible_version: usize = high_bit | 25; + +/// The function was not performed due to a security violation. pub const security_violation: usize = high_bit | 26; + +/// A CRC error was detected. pub const crc_error: usize = high_bit | 27; + +/// Beginning or end of media was reached pub const end_of_media: usize = high_bit | 28; + +/// The end of the file was reached. pub const end_of_file: usize = high_bit | 31; + +/// The language specified was invalid. pub const invalid_language: usize = high_bit | 32; + +/// The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status. pub const compromised_data: usize = high_bit | 33; + +/// There is an address conflict address allocation pub const ip_address_conflict: usize = high_bit | 34; + +/// A HTTP error occurred during the network operation. pub const http_error: usize = high_bit | 35; +/// The string contained one or more characters that the device could not render and were skipped. pub const warn_unknown_glyph: usize = 1; + +/// The handle was closed, but the file was not deleted. pub const warn_delete_failure: usize = 2; + +/// The handle was closed, but the data to the file was not flushed properly. pub const warn_write_failure: usize = 3; + +/// The resulting buffer was too small, and the data was truncated to the buffer size. pub const warn_buffer_too_small: usize = 4; + +/// The data has not been updated within the timeframe set by localpolicy for this type of data. pub const warn_stale_data: usize = 5; + +/// The resulting buffer contains UEFI-compliant file system. pub const warn_file_system: usize = 6; + +/// The operation will be processed across a system reset. pub const warn_reset_required: usize = 7; diff --git a/lib/std/os/uefi/tables.zig b/lib/std/os/uefi/tables.zig index c66a50802..d34408dfb 100644 --- a/lib/std/os/uefi/tables.zig +++ b/lib/std/os/uefi/tables.zig @@ -1,8 +1,11 @@ pub const BootServices = @import("tables/boot_services.zig").BootServices; pub const ConfigurationTable = @import("tables/configuration_table.zig").ConfigurationTable; pub const global_variable align(8) = @import("tables/runtime_services.zig").global_variable; +pub const LocateSearchType = @import("tables/boot_services.zig").LocateSearchType; pub const MemoryDescriptor = @import("tables/boot_services.zig").MemoryDescriptor; pub const MemoryType = @import("tables/boot_services.zig").MemoryType; +pub const OpenProtocolAttributes = @import("tables/boot_services.zig").OpenProtocolAttributes; +pub const ProtocolInformationEntry = @import("tables/boot_services.zig").ProtocolInformationEntry; pub const ResetType = @import("tables/runtime_services.zig").ResetType; pub const RuntimeServices = @import("tables/runtime_services.zig").RuntimeServices; pub const SystemTable = @import("tables/system_table.zig").SystemTable; diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index 895448154..2358b4f84 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -3,57 +3,111 @@ const Event = uefi.Event; const Guid = uefi.Guid; const Handle = uefi.Handle; const TableHeader = uefi.tables.TableHeader; +const DevicePathProtocol = uefi.protocols.DevicePathProtocol; -/// UEFI Specification, Version 2.8, 4.4 -/// -/// As the boot_services table may grow with new UEFI versions, it is important to check hdr.header_size. +/// Boot services are services provided by the system's firmware until the operating system takes +/// over control over the hardware by calling exitBootServices. /// /// Boot Services must not be used after exitBootServices has been called. The only exception is /// getMemoryMap, which may be used after the first unsuccessful call to exitBootServices. /// After successfully calling exitBootServices, system_table.console_in_handle, system_table.con_in, /// system_table.console_out_handle, system_table.con_out, system_table.standard_error_handle, /// system_table.std_err, and system_table.boot_services should be set to null. After setting these -/// attributes to null, system_table.hdr.crc32 must be recomputed. See UEFI Specification, Version 2.8, 7.4. +/// attributes to null, system_table.hdr.crc32 must be recomputed. +/// +/// As the boot_services table may grow with new UEFI versions, it is important to check hdr.header_size. pub const BootServices = extern struct { hdr: TableHeader, + raiseTpl: usize, // TODO restoreTpl: usize, // TODO allocatePages: usize, // TODO freePages: usize, // TODO + + /// Returns the current memory map. getMemoryMap: extern fn (*usize, [*]MemoryDescriptor, *usize, *usize, *u32) usize, + + /// Allocates pool memory. allocatePool: extern fn (MemoryType, usize, *align(8) [*]u8) usize, - freePool: usize, // TODO - createEvent: extern fn (u32, usize, ?extern fn (Event, ?*const c_void) void, ?*const c_void, *Event) usize, + + /// Returns pool memory to the system. + freePool: extern fn ([*]align(8) u8) usize, + + /// Creates an event. + createEvent: extern fn (u32, usize, ?extern fn (Event, ?*c_void) void, ?*const c_void, *Event) usize, + + /// Sets the type of timer and the trigger time for a timer event. setTimer: extern fn (Event, TimerDelay, u64) usize, + + /// Stops execution until an event is signaled. waitForEvent: extern fn (usize, [*]const Event, *usize) usize, + + /// Signals an event. signalEvent: extern fn (Event) usize, + + /// Closes an event. closeEvent: extern fn (Event) usize, - checkEvent: usize, // TODO + + /// Checks whether an event is in the signaled state. + checkEvent: extern fn (Event) usize, + installProtocolInterface: usize, // TODO reinstallProtocolInterface: usize, // TODO uninstallProtocolInterface: usize, // TODO - handleProtocol: usize, // TODO + + /// Queries a handle to determine if it supports a specified protocol. + handleProtocol: extern fn (Handle, *align(8) const Guid, *?*c_void) usize, + reserved: *c_void, + registerProtocolNotify: usize, // TODO locateHandle: usize, // TODO locateDevicePath: usize, // TODO installConfigurationTable: usize, // TODO - imageLoad: usize, // TODO - imageStart: usize, // TODO + + /// Loads an EFI image into memory. + loadImage: extern fn (bool, Handle, ?*const DevicePathProtocol, ?[*]const u8, usize, *?Handle) usize, + + /// Transfers control to a loaded image's entry point. + startImage: extern fn (Handle, ?*usize, ?*[*]u16) usize, + + /// Terminates a loaded EFI image and returns control to boot services. exit: extern fn (Handle, usize, usize, ?*const c_void) usize, - imageUnload: usize, // TODO + + /// Unloads an image. + unloadImage: extern fn (Handle) usize, + + /// Terminates all boot services. exitBootServices: extern fn (Handle, usize) usize, + getNextMonotonicCount: usize, // TODO + + /// Induces a fine-grained stall. stall: extern fn (usize) usize, + + /// Sets the system's watchdog timer. setWatchdogTimer: extern fn (usize, u64, usize, ?[*]const u16) usize, + connectController: usize, // TODO disconnectController: usize, // TODO - openProtocol: usize, // TODO - closeProtocol: usize, // TODO - openProtocolInformation: usize, // TODO + + /// Queries a handle to determine if it supports a specified protocol. + openProtocol: extern fn (Handle, *align(8) const Guid, *?*c_void, ?Handle, ?Handle, OpenProtocolAttributes) usize, + + /// Closes a protocol on a handle that was opened using openProtocol(). + closeProtocol: extern fn (Handle, *align(8) const Guid, Handle, ?Handle) usize, + + /// Retrieves the list of agents that currently have a protocol interface opened. + openProtocolInformation: extern fn (Handle, *align(8) const Guid, *[*]ProtocolInformationEntry, *usize) usize, + protocolsPerHandle: usize, // TODO - locateHandleBuffer: usize, // TODO + + /// Returns an array of handles that support the requested protocol in a buffer allocated from pool. + locateHandleBuffer: extern fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, *[*]Handle) usize, + + /// Returns the first protocol instance that matches the given protocol. locateProtocol: extern fn (*align(8) const Guid, ?*const c_void, *?*c_void) usize, + installMultipleProtocolInterfaces: usize, // TODO uninstallMultipleProtocolInterfaces: usize, // TODO calculateCrc32: usize, // TODO @@ -125,3 +179,26 @@ pub const MemoryDescriptor = extern struct { memory_runtime: bool, }, }; + +pub const LocateSearchType = extern enum(u32) { + AllHandles, + ByRegisterNotify, + ByProtocol, +}; + +pub const OpenProtocolAttributes = packed struct { + by_handle_protocol: bool, + get_protocol: bool, + test_protocol: bool, + by_child_controller: bool, + by_driver: bool, + exclusive: bool, + _pad: u26, +}; + +pub const ProtocolInformationEntry = extern struct { + agent_handle: ?Handle, + controller_handle: ?Handle, + attributes: OpenProtocolAttributes, + open_count: u32, +}; diff --git a/lib/std/os/uefi/tables/configuration_table.zig b/lib/std/os/uefi/tables/configuration_table.zig index eb99e0847..0ac3bd73c 100644 --- a/lib/std/os/uefi/tables/configuration_table.zig +++ b/lib/std/os/uefi/tables/configuration_table.zig @@ -1,8 +1,6 @@ const uefi = @import("std").os.uefi; const Guid = uefi.Guid; -/// UEFI Specification, Version 2.8, 4.6 -/// Because GUIDs must be align(8), structs of this type also must be align(8) pub const ConfigurationTable = extern struct { vendor_guid: Guid, vendor_table: *c_void, diff --git a/lib/std/os/uefi/tables/runtime_services.zig b/lib/std/os/uefi/tables/runtime_services.zig index 53cb17db5..9e6861377 100644 --- a/lib/std/os/uefi/tables/runtime_services.zig +++ b/lib/std/os/uefi/tables/runtime_services.zig @@ -4,27 +4,40 @@ const TableHeader = uefi.tables.TableHeader; const Time = uefi.Time; const TimeCapabilities = uefi.TimeCapabilities; -/// UEFI Specification, Version 2.8, 4.5 +/// Runtime services are provided by the firmware before and after exitBootServices has been called. /// /// As the runtime_services table may grow with new UEFI versions, it is important to check hdr.header_size. /// /// Some functions may not be supported. Check the RuntimeServicesSupported variable using getVariable. -/// getVariable is one of the functions that may not be supported. See UEFI Specification, Version 2.8, 8.1. +/// getVariable is one of the functions that may not be supported. /// -/// Some functions may not be called while other functions are running. See UEFI Specification, Version 2.8, 8.1. +/// Some functions may not be called while other functions are running. pub const RuntimeServices = extern struct { hdr: TableHeader, + + /// Returns the current time and date information, and the time-keeping capabilities of the hardware platform. getTime: extern fn (*uefi.Time, ?*TimeCapabilities) usize, + setTime: usize, // TODO getWakeupTime: usize, // TODO setWakeupTime: usize, // TODO setVirtualAddressMap: usize, // TODO convertPointer: usize, // TODO - getVariable: extern fn ([*]const u16, *align(8) const Guid, ?*u32, *usize, ?*c_void) usize, + + /// Returns the value of a variable. + getVariable: extern fn ([*:0]const u16, *align(8) const Guid, ?*u32, *usize, ?*c_void) usize, + + /// Enumerates the current variable names. getNextVariableName: extern fn (*usize, [*]u16, *align(8) Guid) usize, - setVariable: extern fn ([*]const u16, *align(8) const Guid, u32, usize, *c_void) usize, + + /// Sets the value of a variable. + setVariable: extern fn ([*:0]const u16, *align(8) const Guid, u32, usize, *c_void) usize, + getNextHighMonotonicCount: usize, // TODO + + /// Resets the entire platform. resetSystem: extern fn (ResetType, usize, usize, ?*const c_void) noreturn, + updateCapsule: usize, // TODO queryCapsuleCapabilities: usize, // TODO queryVariableInfo: usize, // TODO @@ -32,7 +45,6 @@ pub const RuntimeServices = extern struct { pub const signature: u64 = 0x56524553544e5552; }; -/// UEFI Specification, Version 2.8, 8.5.1 pub const ResetType = extern enum(u32) { ResetCold, ResetWarm, @@ -40,7 +52,6 @@ pub const ResetType = extern enum(u32) { ResetPlatformSpecific, }; -/// UEFI Specification, Version 2.8, 3.3 pub const global_variable align(8) = Guid{ .time_low = 0x8be4df61, .time_mid = 0x93ca, diff --git a/lib/std/os/uefi/tables/system_table.zig b/lib/std/os/uefi/tables/system_table.zig index 23140f984..40fec34d4 100644 --- a/lib/std/os/uefi/tables/system_table.zig +++ b/lib/std/os/uefi/tables/system_table.zig @@ -3,24 +3,26 @@ const BootServices = uefi.tables.BootServices; const ConfigurationTable = uefi.tables.ConfigurationTable; const Handle = uefi.Handle; const RuntimeServices = uefi.tables.RuntimeServices; -const SimpleTextInputExProtocol = uefi.protocols.SimpleTextInputExProtocol; +const SimpleTextInputProtocol = uefi.protocols.SimpleTextInputProtocol; const SimpleTextOutputProtocol = uefi.protocols.SimpleTextOutputProtocol; const TableHeader = uefi.tables.TableHeader; -/// UEFI Specification, Version 2.8, 4.3 +/// The EFI System Table contains pointers to the runtime and boot services tables. /// /// As the system_table may grow with new UEFI versions, it is important to check hdr.header_size. /// /// After successfully calling boot_services.exitBootServices, console_in_handle, /// con_in, console_out_handle, con_out, standard_error_handle, std_err, and /// boot_services should be set to null. After setting these attributes to null, -/// hdr.crc32 must be recomputed. See UEFI Specification, Version 2.8, 7.4. +/// hdr.crc32 must be recomputed. pub const SystemTable = extern struct { hdr: TableHeader, - firmware_vendor: *u16, + + /// A null-terminated string that identifies the vendor that produces the system firmware of the platform. + firmware_vendor: [*:0]u16, firmware_revision: u32, console_in_handle: ?Handle, - con_in: ?*SimpleTextInputExProtocol, + con_in: ?*SimpleTextInputProtocol, console_out_handle: ?Handle, con_out: ?*SimpleTextOutputProtocol, standard_error_handle: ?Handle, diff --git a/lib/std/os/uefi/tables/table_header.zig b/lib/std/os/uefi/tables/table_header.zig index b955768e6..d5d409423 100644 --- a/lib/std/os/uefi/tables/table_header.zig +++ b/lib/std/os/uefi/tables/table_header.zig @@ -1,7 +1,8 @@ -/// UEFI Specification, Version 2.8, 4.2 pub const TableHeader = extern struct { signature: u64, revision: u32, + + /// The size, in bytes, of the entire table including the TableHeader header_size: u32, crc32: u32, reserved: u32, diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index 57b708395..48b591ecc 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -1,10 +1,8 @@ // Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h // and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md -const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; -pub const is_the_target = builtin.os == .wasi; pub usingnamespace @import("bits.zig"); comptime { @@ -14,20 +12,20 @@ comptime { assert(@alignOf(u16) == 2); assert(@alignOf(i32) == 4); assert(@alignOf(u32) == 4); - assert(@alignOf(i64) == 8); - assert(@alignOf(u64) == 8); + // assert(@alignOf(i64) == 8); + // assert(@alignOf(u64) == 8); } pub const iovec_t = iovec; pub const ciovec_t = iovec_const; -pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t; pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; -pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn environ_get(environ: [*]?[*:0]u8, environ_buf: [*]u8) errno_t; pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; @@ -69,7 +67,6 @@ pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; -pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; @@ -78,3 +75,8 @@ pub extern "wasi_unstable" fn sched_yield() errno_t; pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; + +/// Get the errno from a syscall return value, or 0 for no error. +pub fn getErrno(r: errno_t) usize { + return r; +} diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 7c1761a4b..9254cb1f6 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -11,41 +11,16 @@ const assert = std.debug.assert; const math = std.math; const maxInt = std.math.maxInt; -pub const is_the_target = builtin.os == .windows; pub const advapi32 = @import("windows/advapi32.zig"); pub const kernel32 = @import("windows/kernel32.zig"); pub const ntdll = @import("windows/ntdll.zig"); pub const ole32 = @import("windows/ole32.zig"); pub const shell32 = @import("windows/shell32.zig"); +pub const ws2_32 = @import("windows/ws2_32.zig"); pub usingnamespace @import("windows/bits.zig"); -/// `builtin` is missing `subsystem` when the subsystem is automatically detected, -/// so Zig standard library has the subsystem detection logic here. This should generally be -/// used rather than `builtin.subsystem`. -/// On non-windows targets, this is `null`. -pub const subsystem: ?builtin.SubSystem = blk: { - if (@hasDecl(builtin, "subsystem")) break :blk builtin.subsystem; - switch (builtin.os) { - .windows => { - if (builtin.is_test) { - break :blk builtin.SubSystem.Console; - } - const root = @import("root"); - if (@hasDecl(root, "WinMain") or - @hasDecl(root, "wWinMain") or - @hasDecl(root, "WinMainCRTStartup") or - @hasDecl(root, "wWinMainCRTStartup")) - { - break :blk builtin.SubSystem.Windows; - } else { - break :blk builtin.SubSystem.Console; - } - }, - .uefi => break :blk builtin.SubSystem.EfiApplication, - else => break :blk null, - } -}; +pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); pub const CreateFileError = error{ SharingViolation, @@ -85,7 +60,7 @@ pub fn CreateFile( } pub fn CreateFileW( - file_path_w: [*]const u16, + file_path_w: [*:0]const u16, desired_access: DWORD, share_mode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, @@ -122,6 +97,59 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C } } +pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { + const nameW = try sliceToPrefixedFileW(name); + return CreateEventExW(attributes, &nameW, flags, desired_access); +} + +pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, flags: DWORD, desired_access: DWORD) !HANDLE { + const handle = kernel32.CreateEventExW(attributes, nameW, flags, desired_access); + if (handle) |h| { + return h; + } else { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub fn DeviceIoControl( + h: HANDLE, + ioControlCode: DWORD, + in: ?[]const u8, + out: ?[]u8, + overlapped: ?*OVERLAPPED, +) !DWORD { + var bytes: DWORD = undefined; + if (kernel32.DeviceIoControl( + h, + ioControlCode, + if (in) |i| i.ptr else null, + if (in) |i| @intCast(u32, i.len) else 0, + if (out) |o| o.ptr else null, + if (out) |o| @intCast(u32, o.len) else 0, + &bytes, + overlapped, + ) == 0) { + switch (kernel32.GetLastError()) { + ERROR.IO_PENDING => if (overlapped == null) unreachable, + else => |err| return unexpectedError(err), + } + } + return bytes; +} + +pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD { + var bytes: DWORD = undefined; + if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @boolToInt(wait)) == 0) { + switch (kernel32.GetLastError()) { + ERROR.IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable, + else => |err| return unexpectedError(err), + } + } + return bytes; +} + pub const SetHandleInformationError = error{Unexpected}; pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void { @@ -161,7 +189,11 @@ pub const WaitForSingleObjectError = error{ }; pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void { - switch (kernel32.WaitForSingleObject(handle, milliseconds)) { + return WaitForSingleObjectEx(handle, milliseconds, false); +} + +pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void { + switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @boolToInt(alertable))) { WAIT_ABANDONED => return error.WaitAbandoned, WAIT_OBJECT_0 => return, WAIT_TIMEOUT => return error.WaitTimeOut, @@ -172,6 +204,34 @@ pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObj } } +pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 { + assert(handles.len < MAXIMUM_WAIT_OBJECTS); + const nCount: DWORD = @intCast(DWORD, handles.len); + switch (kernel32.WaitForMultipleObjectsEx( + nCount, + handles.ptr, + @boolToInt(waitAll), + milliseconds, + @boolToInt(alertable), + )) { + WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| { + const handle_index = n - WAIT_OBJECT_0; + assert(handle_index < nCount); + return handle_index; + }, + WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| { + const handle_index = n - WAIT_ABANDONED_0; + assert(handle_index < nCount); + return error.WaitAbandoned; + }, + WAIT_TIMEOUT => return error.WaitTimeOut, + WAIT_FAILED => switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + }, + else => return error.Unexpected, + } +} + pub const FindFirstFileError = error{ FileNotFound, InvalidUtf8, @@ -181,7 +241,7 @@ pub const FindFirstFileError = error{ }; pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE { - const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*', 0 }); + const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*' }); const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data); if (handle == INVALID_HANDLE_VALUE) { @@ -267,7 +327,7 @@ pub fn GetQueuedCompletionStatus( ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF, else => |err| { if (std.debug.runtime_safety) { - std.debug.panic("unexpected error: {}\n", err); + std.debug.panic("unexpected error: {}\n", .{err}); } }, } @@ -288,7 +348,7 @@ pub const ReadFileError = error{Unexpected}; pub fn ReadFile(in_hFile: HANDLE, buffer: []u8) ReadFileError!usize { var index: usize = 0; while (index < buffer.len) { - const want_read_count = @intCast(DWORD, math.min(DWORD(maxInt(DWORD)), buffer.len - index)); + const want_read_count = @intCast(DWORD, math.min(@as(DWORD, maxInt(DWORD)), buffer.len - index)); var amt_read: DWORD = undefined; if (kernel32.ReadFile(in_hFile, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { switch (kernel32.GetLastError()) { @@ -369,8 +429,8 @@ pub fn CreateSymbolicLink( } pub fn CreateSymbolicLinkW( - sym_link_path: [*]const u16, - target_path: [*]const u16, + sym_link_path: [*:0]const u16, + target_path: [*:0]const u16, flags: DWORD, ) CreateSymbolicLinkError!void { if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) { @@ -393,7 +453,7 @@ pub fn DeleteFile(filename: []const u8) DeleteFileError!void { return DeleteFileW(&filename_w); } -pub fn DeleteFileW(filename: [*]const u16) DeleteFileError!void { +pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void { if (kernel32.DeleteFileW(filename) == 0) { switch (kernel32.GetLastError()) { ERROR.FILE_NOT_FOUND => return error.FileNotFound, @@ -415,7 +475,7 @@ pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) Move return MoveFileExW(&old_path_w, &new_path_w, flags); } -pub fn MoveFileExW(old_path: [*]const u16, new_path: [*]const u16, flags: DWORD) MoveFileError!void { +pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void { if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) { switch (kernel32.GetLastError()) { else => |err| return unexpectedError(err), @@ -434,7 +494,7 @@ pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) Creat return CreateDirectoryW(&pathname_w, attrs); } -pub fn CreateDirectoryW(pathname: [*]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { +pub fn CreateDirectoryW(pathname: [*:0]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { if (kernel32.CreateDirectoryW(pathname, attrs) == 0) { switch (kernel32.GetLastError()) { ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, @@ -455,7 +515,7 @@ pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { return RemoveDirectoryW(&dir_path_w); } -pub fn RemoveDirectoryW(dir_path_w: [*]const u16) RemoveDirectoryError!void { +pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void { if (kernel32.RemoveDirectoryW(dir_path_w) == 0) { switch (kernel32.GetLastError()) { ERROR.PATH_NOT_FOUND => return error.FileNotFound, @@ -546,7 +606,7 @@ pub fn GetFinalPathNameByHandleW( buf_ptr: [*]u16, buf_len: DWORD, flags: DWORD, -) GetFinalPathNameByHandleError!DWORD { +) GetFinalPathNameByHandleError![:0]u16 { const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags); if (rc == 0) { switch (kernel32.GetLastError()) { @@ -558,7 +618,7 @@ pub fn GetFinalPathNameByHandleW( else => |err| return unexpectedError(err), } } - return rc; + return buf_ptr[0..rc :0]; } pub const GetFileSizeError = error{Unexpected}; @@ -584,7 +644,7 @@ pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD { return GetFileAttributesW(&filename_w); } -pub fn GetFileAttributesW(lpFileName: [*]const u16) GetFileAttributesError!DWORD { +pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD { const rc = kernel32.GetFileAttributesW(lpFileName); if (rc == INVALID_FILE_ATTRIBUTES) { switch (kernel32.GetLastError()) { @@ -597,16 +657,94 @@ pub fn GetFileAttributesW(lpFileName: [*]const u16) GetFileAttributesError!DWORD return rc; } +pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA { + var wsadata: ws2_32.WSADATA = undefined; + return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) { + 0 => wsadata, + else => |err| unexpectedWSAError(err), + }; +} + +pub fn WSACleanup() !void { + return switch (ws2_32.WSACleanup()) { + 0 => {}, + ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) { + else => |err| return unexpectedWSAError(err), + }, + else => unreachable, + }; +} + +pub fn WSASocketW( + af: i32, + socket_type: i32, + protocol: i32, + protocolInfo: ?*ws2_32.WSAPROTOCOL_INFOW, + g: ws2_32.GROUP, + dwFlags: DWORD, +) !ws2_32.SOCKET { + const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags); + if (rc == ws2_32.INVALID_SOCKET) { + switch (ws2_32.WSAGetLastError()) { + ws2_32.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, + ws2_32.WSAEMFILE => return error.ProcessFdQuotaExceeded, + ws2_32.WSAENOBUFS => return error.SystemResources, + ws2_32.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported, + else => |err| return unexpectedWSAError(err), + } + } + return rc; +} + +pub fn closesocket(s: ws2_32.SOCKET) !void { + switch (ws2_32.closesocket(s)) { + 0 => {}, + ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) { + else => |err| return unexpectedWSAError(err), + }, + else => unreachable, + } +} + +pub fn WSAIoctl( + s: ws2_32.SOCKET, + dwIoControlCode: DWORD, + inBuffer: ?[]const u8, + outBuffer: []u8, + overlapped: ?*ws2_32.WSAOVERLAPPED, + completionRoutine: ?ws2_32.WSAOVERLAPPED_COMPLETION_ROUTINE, +) !DWORD { + var bytes: DWORD = undefined; + switch (ws2_32.WSAIoctl( + s, + dwIoControlCode, + if (inBuffer) |i| i.ptr else null, + if (inBuffer) |i| @intCast(DWORD, i.len) else 0, + outBuffer.ptr, + @intCast(DWORD, outBuffer.len), + &bytes, + overlapped, + completionRoutine, + )) { + 0 => {}, + ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) { + else => |err| return unexpectedWSAError(err), + }, + else => unreachable, + } + return bytes; +} + const GetModuleFileNameError = error{Unexpected}; -pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![]u16 { +pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 { const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len); if (rc == 0) { switch (kernel32.GetLastError()) { else => |err| return unexpectedError(err), } } - return buf_ptr[0..rc]; + return buf_ptr[0..rc :0]; } pub const TerminateProcessError = error{Unexpected}; @@ -645,11 +783,11 @@ pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetCon pub const GetEnvironmentStringsError = error{OutOfMemory}; -pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*]u16 { +pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*:0]u16 { return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory; } -pub fn FreeEnvironmentStringsW(penv: [*]u16) void { +pub fn FreeEnvironmentStringsW(penv: [*:0]u16) void { assert(kernel32.FreeEnvironmentStringsW(penv) != 0); } @@ -658,7 +796,7 @@ pub const GetEnvironmentVariableError = error{ Unexpected, }; -pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) GetEnvironmentVariableError!DWORD { +pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: [*]u16, nSize: DWORD) GetEnvironmentVariableError!DWORD { const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize); if (rc == 0) { switch (kernel32.GetLastError()) { @@ -716,7 +854,7 @@ pub const LoadLibraryError = error{ Unexpected, }; -pub fn LoadLibraryW(lpLibFileName: [*]const u16) LoadLibraryError!HMODULE { +pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE { return kernel32.LoadLibraryW(lpLibFileName) orelse { switch (kernel32.GetLastError()) { ERROR.FILE_NOT_FOUND => return error.FileNotFound, @@ -792,6 +930,25 @@ pub fn SetFileTime( } } +pub fn peb() *PEB { + switch (builtin.arch) { + .i386 => { + return asm ( + \\ mov %%fs:0x18, %[ptr] + \\ mov %%ds:0x30(%[ptr]), %[ptr] + : [ptr] "=r" (-> *PEB) + ); + }, + .x86_64 => { + return asm ( + \\ mov %%gs:0x60, %[ptr] + : [ptr] "=r" (-> *PEB) + ); + }, + else => @compileError("unsupported architecture"), + } +} + /// A file time is a 64-bit value that represents the number of 100-nanosecond /// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated /// Universal Time (UTC). @@ -808,7 +965,7 @@ pub fn toSysTime(ns: i64) i64 { } pub fn fileTimeToNanoSeconds(ft: FILETIME) i64 { - const hns = @bitCast(i64, (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime); + const hns = @bitCast(i64, (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime); return fromSysTime(hns); } @@ -821,17 +978,34 @@ pub fn nanoSecondsToFileTime(ns: i64) FILETIME { }; } -pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { +pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); } -pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedSuffixedFileW(s, [_]u16{0}); +pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE:0]u16 { + return sliceToPrefixedSuffixedFileW(s, &[_]u16{}); } -pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { +/// Assumes an absolute path. +pub fn wToPrefixedFileW(s: []const u16) ![PATH_MAX_WIDE:0]u16 { // TODO https://github.com/ziglang/zig/issues/2765 - var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; + var result: [PATH_MAX_WIDE:0]u16 = undefined; + + const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: { + const prefix = [_]u16{ '\\', '?', '?', '\\' }; + mem.copy(u16, result[0..], &prefix); + break :blk prefix.len; + }; + const end_index = start_index + s.len; + if (end_index + 1 > result.len) return error.NameTooLong; + mem.copy(u16, result[start_index..], s); + result[end_index] = 0; + return result; +} + +pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len:0]u16 { + // TODO https://github.com/ziglang/zig/issues/2765 + var result: [PATH_MAX_WIDE + suffix.len:0]u16 = undefined; // > File I/O functions in the Windows API convert "/" to "\" as part of // > converting the name to an NT-style name, except when using the "\\?\" // > prefix as detailed in the following sections. @@ -844,15 +1018,15 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) else => {}, } } - const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: { - const prefix = [_]u16{ '\\', '\\', '?', '\\' }; - mem.copy(u16, result[0..], prefix); + const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { + const prefix = [_]u16{ '\\', '?', '?', '\\' }; + mem.copy(u16, result[0..], &prefix); break :blk prefix.len; }; const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); - assert(end_index <= result.len); if (end_index + suffix.len > result.len) return error.NameTooLong; mem.copy(u16, result[end_index..], suffix); + result[end_index + suffix.len] = 0; return result; } @@ -869,17 +1043,21 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError { var buf_u8: [614]u8 = undefined; var len = kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), buf_u16[0..].ptr, buf_u16.len / @sizeOf(TCHAR), null); _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable; - std.debug.warn("error.Unexpected: GetLastError({}): {}\n", err, buf_u8[0..len]); + std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ err, buf_u8[0..len] }); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; } +pub fn unexpectedWSAError(err: c_int) std.os.UnexpectedError { + return unexpectedError(@intCast(DWORD, err)); +} + /// Call this when you made a windows NtDll call /// and you get an unexpected status. pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { if (std.os.unexpected_error_tracing) { - std.debug.warn("error.Unexpected NTSTATUS={}\n", status); + std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", .{status}); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; diff --git a/lib/std/os/windows/advapi32.zig b/lib/std/os/windows/advapi32.zig index 940f10994..6364c5bdd 100644 --- a/lib/std/os/windows/advapi32.zig +++ b/lib/std/os/windows/advapi32.zig @@ -1,23 +1,23 @@ usingnamespace @import("bits.zig"); -pub extern "advapi32" stdcallcc fn RegOpenKeyExW( +pub extern "advapi32" fn RegOpenKeyExW( hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, phkResult: *HKEY, -) LSTATUS; +) callconv(.Stdcall) LSTATUS; -pub extern "advapi32" stdcallcc fn RegQueryValueExW( +pub extern "advapi32" fn RegQueryValueExW( hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD, -) LSTATUS; +) callconv(.Stdcall) LSTATUS; // RtlGenRandom is known as SystemFunction036 under advapi32 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ -pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: ULONG) BOOL; +pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(.Stdcall) BOOL; pub const RtlGenRandom = SystemFunction036; diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index ddfdd27e1..328732e0c 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -19,7 +19,6 @@ pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; /// The standard error device. Initially, this is the active console screen buffer, CONOUT$. pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; -pub const SHORT = c_short; pub const BOOL = c_int; pub const BOOLEAN = BYTE; pub const BYTE = u8; @@ -34,17 +33,17 @@ pub const FARPROC = *@OpaqueType(); pub const INT = c_int; pub const LPBYTE = *BYTE; pub const LPCH = *CHAR; -pub const LPCSTR = [*]const CHAR; -pub const LPCTSTR = [*]const TCHAR; +pub const LPCSTR = [*:0]const CHAR; +pub const LPCTSTR = [*:0]const TCHAR; pub const LPCVOID = *const c_void; pub const LPDWORD = *DWORD; -pub const LPSTR = [*]CHAR; +pub const LPSTR = [*:0]CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPVOID = *c_void; -pub const LPWSTR = [*]WCHAR; -pub const LPCWSTR = [*]const WCHAR; +pub const LPWSTR = [*:0]WCHAR; +pub const LPCWSTR = [*:0]const WCHAR; pub const PVOID = *c_void; -pub const PWSTR = [*]WCHAR; +pub const PWSTR = [*:0]WCHAR; pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; @@ -54,6 +53,8 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const USHORT = u16; +pub const SHORT = i16; pub const ULONG = u32; pub const LONG = i32; pub const ULONGLONG = u64; @@ -67,9 +68,116 @@ pub const va_list = *@OpaqueType(); pub const TRUE = 1; pub const FALSE = 0; +pub const DEVICE_TYPE = ULONG; +pub const FILE_DEVICE_BEEP: DEVICE_TYPE = 0x0001; +pub const FILE_DEVICE_CD_ROM: DEVICE_TYPE = 0x0002; +pub const FILE_DEVICE_CD_ROM_FILE_SYSTEM: DEVICE_TYPE = 0x0003; +pub const FILE_DEVICE_CONTROLLER: DEVICE_TYPE = 0x0004; +pub const FILE_DEVICE_DATALINK: DEVICE_TYPE = 0x0005; +pub const FILE_DEVICE_DFS: DEVICE_TYPE = 0x0006; +pub const FILE_DEVICE_DISK: DEVICE_TYPE = 0x0007; +pub const FILE_DEVICE_DISK_FILE_SYSTEM: DEVICE_TYPE = 0x0008; +pub const FILE_DEVICE_FILE_SYSTEM: DEVICE_TYPE = 0x0009; +pub const FILE_DEVICE_INPORT_PORT: DEVICE_TYPE = 0x000a; +pub const FILE_DEVICE_KEYBOARD: DEVICE_TYPE = 0x000b; +pub const FILE_DEVICE_MAILSLOT: DEVICE_TYPE = 0x000c; +pub const FILE_DEVICE_MIDI_IN: DEVICE_TYPE = 0x000d; +pub const FILE_DEVICE_MIDI_OUT: DEVICE_TYPE = 0x000e; +pub const FILE_DEVICE_MOUSE: DEVICE_TYPE = 0x000f; +pub const FILE_DEVICE_MULTI_UNC_PROVIDER: DEVICE_TYPE = 0x0010; +pub const FILE_DEVICE_NAMED_PIPE: DEVICE_TYPE = 0x0011; +pub const FILE_DEVICE_NETWORK: DEVICE_TYPE = 0x0012; +pub const FILE_DEVICE_NETWORK_BROWSER: DEVICE_TYPE = 0x0013; +pub const FILE_DEVICE_NETWORK_FILE_SYSTEM: DEVICE_TYPE = 0x0014; +pub const FILE_DEVICE_NULL: DEVICE_TYPE = 0x0015; +pub const FILE_DEVICE_PARALLEL_PORT: DEVICE_TYPE = 0x0016; +pub const FILE_DEVICE_PHYSICAL_NETCARD: DEVICE_TYPE = 0x0017; +pub const FILE_DEVICE_PRINTER: DEVICE_TYPE = 0x0018; +pub const FILE_DEVICE_SCANNER: DEVICE_TYPE = 0x0019; +pub const FILE_DEVICE_SERIAL_MOUSE_PORT: DEVICE_TYPE = 0x001a; +pub const FILE_DEVICE_SERIAL_PORT: DEVICE_TYPE = 0x001b; +pub const FILE_DEVICE_SCREEN: DEVICE_TYPE = 0x001c; +pub const FILE_DEVICE_SOUND: DEVICE_TYPE = 0x001d; +pub const FILE_DEVICE_STREAMS: DEVICE_TYPE = 0x001e; +pub const FILE_DEVICE_TAPE: DEVICE_TYPE = 0x001f; +pub const FILE_DEVICE_TAPE_FILE_SYSTEM: DEVICE_TYPE = 0x0020; +pub const FILE_DEVICE_TRANSPORT: DEVICE_TYPE = 0x0021; +pub const FILE_DEVICE_UNKNOWN: DEVICE_TYPE = 0x0022; +pub const FILE_DEVICE_VIDEO: DEVICE_TYPE = 0x0023; +pub const FILE_DEVICE_VIRTUAL_DISK: DEVICE_TYPE = 0x0024; +pub const FILE_DEVICE_WAVE_IN: DEVICE_TYPE = 0x0025; +pub const FILE_DEVICE_WAVE_OUT: DEVICE_TYPE = 0x0026; +pub const FILE_DEVICE_8042_PORT: DEVICE_TYPE = 0x0027; +pub const FILE_DEVICE_NETWORK_REDIRECTOR: DEVICE_TYPE = 0x0028; +pub const FILE_DEVICE_BATTERY: DEVICE_TYPE = 0x0029; +pub const FILE_DEVICE_BUS_EXTENDER: DEVICE_TYPE = 0x002a; +pub const FILE_DEVICE_MODEM: DEVICE_TYPE = 0x002b; +pub const FILE_DEVICE_VDM: DEVICE_TYPE = 0x002c; +pub const FILE_DEVICE_MASS_STORAGE: DEVICE_TYPE = 0x002d; +pub const FILE_DEVICE_SMB: DEVICE_TYPE = 0x002e; +pub const FILE_DEVICE_KS: DEVICE_TYPE = 0x002f; +pub const FILE_DEVICE_CHANGER: DEVICE_TYPE = 0x0030; +pub const FILE_DEVICE_SMARTCARD: DEVICE_TYPE = 0x0031; +pub const FILE_DEVICE_ACPI: DEVICE_TYPE = 0x0032; +pub const FILE_DEVICE_DVD: DEVICE_TYPE = 0x0033; +pub const FILE_DEVICE_FULLSCREEN_VIDEO: DEVICE_TYPE = 0x0034; +pub const FILE_DEVICE_DFS_FILE_SYSTEM: DEVICE_TYPE = 0x0035; +pub const FILE_DEVICE_DFS_VOLUME: DEVICE_TYPE = 0x0036; +pub const FILE_DEVICE_SERENUM: DEVICE_TYPE = 0x0037; +pub const FILE_DEVICE_TERMSRV: DEVICE_TYPE = 0x0038; +pub const FILE_DEVICE_KSEC: DEVICE_TYPE = 0x0039; +pub const FILE_DEVICE_FIPS: DEVICE_TYPE = 0x003a; +pub const FILE_DEVICE_INFINIBAND: DEVICE_TYPE = 0x003b; +// TODO: missing values? +pub const FILE_DEVICE_VMBUS: DEVICE_TYPE = 0x003e; +pub const FILE_DEVICE_CRYPT_PROVIDER: DEVICE_TYPE = 0x003f; +pub const FILE_DEVICE_WPD: DEVICE_TYPE = 0x0040; +pub const FILE_DEVICE_BLUETOOTH: DEVICE_TYPE = 0x0041; +pub const FILE_DEVICE_MT_COMPOSITE: DEVICE_TYPE = 0x0042; +pub const FILE_DEVICE_MT_TRANSPORT: DEVICE_TYPE = 0x0043; +pub const FILE_DEVICE_BIOMETRIC: DEVICE_TYPE = 0x0044; +pub const FILE_DEVICE_PMI: DEVICE_TYPE = 0x0045; +pub const FILE_DEVICE_EHSTOR: DEVICE_TYPE = 0x0046; +pub const FILE_DEVICE_DEVAPI: DEVICE_TYPE = 0x0047; +pub const FILE_DEVICE_GPIO: DEVICE_TYPE = 0x0048; +pub const FILE_DEVICE_USBEX: DEVICE_TYPE = 0x0049; +pub const FILE_DEVICE_CONSOLE: DEVICE_TYPE = 0x0050; +pub const FILE_DEVICE_NFP: DEVICE_TYPE = 0x0051; +pub const FILE_DEVICE_SYSENV: DEVICE_TYPE = 0x0052; +pub const FILE_DEVICE_VIRTUAL_BLOCK: DEVICE_TYPE = 0x0053; +pub const FILE_DEVICE_POINT_OF_SERVICE: DEVICE_TYPE = 0x0054; +pub const FILE_DEVICE_STORAGE_REPLICATION: DEVICE_TYPE = 0x0055; +pub const FILE_DEVICE_TRUST_ENV: DEVICE_TYPE = 0x0056; +pub const FILE_DEVICE_UCM: DEVICE_TYPE = 0x0057; +pub const FILE_DEVICE_UCMTCPCI: DEVICE_TYPE = 0x0058; +pub const FILE_DEVICE_PERSISTENT_MEMORY: DEVICE_TYPE = 0x0059; +pub const FILE_DEVICE_NVDIMM: DEVICE_TYPE = 0x005a; +pub const FILE_DEVICE_HOLOGRAPHIC: DEVICE_TYPE = 0x005b; +pub const FILE_DEVICE_SDFXHCI: DEVICE_TYPE = 0x005c; + +/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes +pub const TransferType = enum(u2) { + METHOD_BUFFERED = 0, + METHOD_IN_DIRECT = 1, + METHOD_OUT_DIRECT = 2, + METHOD_NEITHER = 3, +}; + +pub const FILE_ANY_ACCESS = 0; +pub const FILE_READ_ACCESS = 1; +pub const FILE_WRITE_ACCESS = 2; + +/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes +pub fn CTL_CODE(deviceType: u16, function: u12, method: TransferType, access: u2) DWORD { + return (@as(DWORD, deviceType) << 16) | + (@as(DWORD, access) << 14) | + (@as(DWORD, function) << 2) | + @enumToInt(method); +} + pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); -pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); +pub const INVALID_FILE_ATTRIBUTES = @as(DWORD, maxInt(DWORD)); pub const FILE_ALL_INFORMATION = extern struct { BasicInformation: FILE_BASIC_INFORMATION, @@ -129,7 +237,11 @@ pub const FILE_NAME_INFORMATION = extern struct { }; pub const IO_STATUS_BLOCK = extern struct { - Status: usize, + // "DUMMYUNIONNAME" expands to "u" + u: extern union { + Status: NTSTATUS, + Pointer: ?*c_void, + }, Information: ULONG_PTR, }; @@ -300,6 +412,62 @@ pub const FILE_SHARE_DELETE = 0x00000004; pub const FILE_SHARE_READ = 0x00000001; pub const FILE_SHARE_WRITE = 0x00000002; +pub const DELETE = 0x00010000; +pub const READ_CONTROL = 0x00020000; +pub const WRITE_DAC = 0x00040000; +pub const WRITE_OWNER = 0x00080000; +pub const SYNCHRONIZE = 0x00100000; +pub const STANDARD_RIGHTS_READ = READ_CONTROL; +pub const STANDARD_RIGHTS_WRITE = READ_CONTROL; +pub const STANDARD_RIGHTS_EXECUTE = READ_CONTROL; +pub const STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER; + +// disposition for NtCreateFile +pub const FILE_SUPERSEDE = 0; +pub const FILE_OPEN = 1; +pub const FILE_CREATE = 2; +pub const FILE_OPEN_IF = 3; +pub const FILE_OVERWRITE = 4; +pub const FILE_OVERWRITE_IF = 5; +pub const FILE_MAXIMUM_DISPOSITION = 5; + +// flags for NtCreateFile and NtOpenFile +pub const FILE_READ_DATA = 0x00000001; +pub const FILE_LIST_DIRECTORY = 0x00000001; +pub const FILE_WRITE_DATA = 0x00000002; +pub const FILE_ADD_FILE = 0x00000002; +pub const FILE_APPEND_DATA = 0x00000004; +pub const FILE_ADD_SUBDIRECTORY = 0x00000004; +pub const FILE_CREATE_PIPE_INSTANCE = 0x00000004; +pub const FILE_READ_EA = 0x00000008; +pub const FILE_WRITE_EA = 0x00000010; +pub const FILE_EXECUTE = 0x00000020; +pub const FILE_TRAVERSE = 0x00000020; +pub const FILE_DELETE_CHILD = 0x00000040; +pub const FILE_READ_ATTRIBUTES = 0x00000080; +pub const FILE_WRITE_ATTRIBUTES = 0x00000100; + +pub const FILE_DIRECTORY_FILE = 0x00000001; +pub const FILE_WRITE_THROUGH = 0x00000002; +pub const FILE_SEQUENTIAL_ONLY = 0x00000004; +pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008; +pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010; +pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020; +pub const FILE_NON_DIRECTORY_FILE = 0x00000040; +pub const FILE_CREATE_TREE_CONNECTION = 0x00000080; +pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100; +pub const FILE_NO_EA_KNOWLEDGE = 0x00000200; +pub const FILE_OPEN_FOR_RECOVERY = 0x00000400; +pub const FILE_RANDOM_ACCESS = 0x00000800; +pub const FILE_DELETE_ON_CLOSE = 0x00001000; +pub const FILE_OPEN_BY_FILE_ID = 0x00002000; +pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; +pub const FILE_NO_COMPRESSION = 0x00008000; +pub const FILE_RESERVE_OPFILTER = 0x00100000; +pub const FILE_TRANSACTED_MODE = 0x00200000; +pub const FILE_OPEN_OFFLINE_FILE = 0x00400000; +pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000; + pub const CREATE_ALWAYS = 2; pub const CREATE_NEW = 1; pub const OPEN_ALWAYS = 4; @@ -326,6 +494,13 @@ pub const FILE_ATTRIBUTE_SYSTEM = 0x4; pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; +// flags for CreateEvent +pub const CREATE_EVENT_INITIAL_SET = 0x00000002; +pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; + +pub const EVENT_ALL_ACCESS = 0x1F0003; +pub const EVENT_MODIFY_STATE = 0x0002; + pub const PROCESS_INFORMATION = extern struct { hProcess: HANDLE, hThread: HANDLE, @@ -371,7 +546,10 @@ pub const STARTF_USESTDHANDLES = 0x00000100; pub const INFINITE = 4294967295; +pub const MAXIMUM_WAIT_OBJECTS = 64; + pub const WAIT_ABANDONED = 0x00000080; +pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0; pub const WAIT_OBJECT_0 = 0x00000000; pub const WAIT_TIMEOUT = 0x00000102; pub const WAIT_FAILED = 0xFFFFFFFF; @@ -533,16 +711,16 @@ pub const KF_FLAG_SIMPLE_IDLIST = 256; pub const KF_FLAG_ALIAS_ONLY = -2147483648; pub const S_OK = 0; -pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); -pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); -pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); -pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); -pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); -pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); -pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); -pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); -pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); -pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); +pub const E_NOTIMPL = @bitCast(c_long, @as(c_ulong, 0x80004001)); +pub const E_NOINTERFACE = @bitCast(c_long, @as(c_ulong, 0x80004002)); +pub const E_POINTER = @bitCast(c_long, @as(c_ulong, 0x80004003)); +pub const E_ABORT = @bitCast(c_long, @as(c_ulong, 0x80004004)); +pub const E_FAIL = @bitCast(c_long, @as(c_ulong, 0x80004005)); +pub const E_UNEXPECTED = @bitCast(c_long, @as(c_ulong, 0x8000FFFF)); +pub const E_ACCESSDENIED = @bitCast(c_long, @as(c_ulong, 0x80070005)); +pub const E_HANDLE = @bitCast(c_long, @as(c_ulong, 0x80070006)); +pub const E_OUTOFMEMORY = @bitCast(c_long, @as(c_ulong, 0x8007000E)); +pub const E_INVALIDARG = @bitCast(c_long, @as(c_ulong, 0x80070057)); pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; @@ -610,8 +788,6 @@ pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void; -pub const FILE_LIST_DIRECTORY = 1; - pub const FILE_NOTIFY_CHANGE_CREATION = 64; pub const FILE_NOTIFY_CHANGE_SIZE = 8; pub const FILE_NOTIFY_CHANGE_SECURITY = 256; @@ -716,19 +892,123 @@ pub const EXCEPTION_POINTERS = extern struct { ContextRecord: *c_void, }; -pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_POINTERS) c_long; +pub const VECTORED_EXCEPTION_HANDLER = fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(.Stdcall) c_long; pub const OBJECT_ATTRIBUTES = extern struct { Length: ULONG, - RootDirectory: HANDLE, + RootDirectory: ?HANDLE, ObjectName: *UNICODE_STRING, Attributes: ULONG, SecurityDescriptor: ?*c_void, SecurityQualityOfService: ?*c_void, }; +pub const OBJ_INHERIT = 0x00000002; +pub const OBJ_PERMANENT = 0x00000010; +pub const OBJ_EXCLUSIVE = 0x00000020; +pub const OBJ_CASE_INSENSITIVE = 0x00000040; +pub const OBJ_OPENIF = 0x00000080; +pub const OBJ_OPENLINK = 0x00000100; +pub const OBJ_KERNEL_HANDLE = 0x00000200; +pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; + pub const UNICODE_STRING = extern struct { - Length: USHORT, - MaximumLength: USHORT, + Length: c_ushort, + MaximumLength: c_ushort, Buffer: [*]WCHAR, }; + +pub const PEB = extern struct { + Reserved1: [2]BYTE, + BeingDebugged: BYTE, + Reserved2: [1]BYTE, + Reserved3: [2]PVOID, + Ldr: *PEB_LDR_DATA, + ProcessParameters: *RTL_USER_PROCESS_PARAMETERS, + Reserved4: [3]PVOID, + AtlThunkSListPtr: PVOID, + Reserved5: PVOID, + Reserved6: ULONG, + Reserved7: PVOID, + Reserved8: ULONG, + AtlThunkSListPtr32: ULONG, + Reserved9: [45]PVOID, + Reserved10: [96]BYTE, + PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE, + Reserved11: [128]BYTE, + Reserved12: [1]PVOID, + SessionId: ULONG, +}; + +pub const PEB_LDR_DATA = extern struct { + Reserved1: [8]BYTE, + Reserved2: [3]PVOID, + InMemoryOrderModuleList: LIST_ENTRY, +}; + +pub const RTL_USER_PROCESS_PARAMETERS = extern struct { + AllocationSize: ULONG, + Size: ULONG, + Flags: ULONG, + DebugFlags: ULONG, + ConsoleHandle: HANDLE, + ConsoleFlags: ULONG, + hStdInput: HANDLE, + hStdOutput: HANDLE, + hStdError: HANDLE, + CurrentDirectory: CURDIR, + DllPath: UNICODE_STRING, + ImagePathName: UNICODE_STRING, + CommandLine: UNICODE_STRING, + Environment: [*]WCHAR, + dwX: ULONG, + dwY: ULONG, + dwXSize: ULONG, + dwYSize: ULONG, + dwXCountChars: ULONG, + dwYCountChars: ULONG, + dwFillAttribute: ULONG, + dwFlags: ULONG, + dwShowWindow: ULONG, + WindowTitle: UNICODE_STRING, + Desktop: UNICODE_STRING, + ShellInfo: UNICODE_STRING, + RuntimeInfo: UNICODE_STRING, + DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR, +}; + +pub const RTL_DRIVE_LETTER_CURDIR = extern struct { + Flags: c_ushort, + Length: c_ushort, + TimeStamp: ULONG, + DosPath: UNICODE_STRING, +}; + +pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void; + +pub const FILE_BOTH_DIR_INFORMATION = extern struct { + NextEntryOffset: ULONG, + FileIndex: ULONG, + CreationTime: LARGE_INTEGER, + LastAccessTime: LARGE_INTEGER, + LastWriteTime: LARGE_INTEGER, + ChangeTime: LARGE_INTEGER, + EndOfFile: LARGE_INTEGER, + AllocationSize: LARGE_INTEGER, + FileAttributes: ULONG, + FileNameLength: ULONG, + EaSize: ULONG, + ShortNameLength: CHAR, + ShortName: [12]WCHAR, + FileName: [1]WCHAR, +}; +pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION; + +pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void; + +pub const CURDIR = extern struct { + DosPath: UNICODE_STRING, + Handle: HANDLE, +}; + +pub const DUPLICATE_SAME_ACCESS = 2; diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index 2ae73ad45..9ec40240b 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -1,32 +1,39 @@ usingnamespace @import("bits.zig"); -pub extern "kernel32" stdcallcc fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) ?*c_void; -pub extern "kernel32" stdcallcc fn RemoveVectoredExceptionHandler(Handle: HANDLE) c_ulong; +pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(.Stdcall) ?*c_void; +pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(.Stdcall) c_ulong; -pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; +pub extern "kernel32" fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; +pub extern "kernel32" fn CloseHandle(hObject: HANDLE) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; +pub extern "kernel32" fn CreateDirectoryW(lpPathName: [*:0]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn CreateFileW( - lpFileName: [*]const u16, // TODO null terminated pointer type +pub extern "kernel32" fn CreateEventExW( + lpEventAttributes: ?*SECURITY_ATTRIBUTES, + lpName: [*:0]const u16, + dwFlags: DWORD, + dwDesiredAccess: DWORD, +) callconv(.Stdcall) ?HANDLE; + +pub extern "kernel32" fn CreateFileW( + lpFileName: [*:0]const u16, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE, -) HANDLE; +) callconv(.Stdcall) HANDLE; -pub extern "kernel32" stdcallcc fn CreatePipe( +pub extern "kernel32" fn CreatePipe( hReadPipe: *HANDLE, hWritePipe: *HANDLE, lpPipeAttributes: *const SECURITY_ATTRIBUTES, nSize: DWORD, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn CreateProcessW( +pub extern "kernel32" fn CreateProcessW( lpApplicationName: ?LPWSTR, lpCommandLine: LPWSTR, lpProcessAttributes: ?*SECURITY_ATTRIBUTES, @@ -37,111 +44,124 @@ pub extern "kernel32" stdcallcc fn CreateProcessW( lpCurrentDirectory: ?LPWSTR, lpStartupInfo: *STARTUPINFOW, lpProcessInformation: *PROCESS_INFORMATION, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN; +pub extern "kernel32" fn CreateSymbolicLinkW(lpSymlinkFileName: [*:0]const u16, lpTargetFileName: [*:0]const u16, dwFlags: DWORD) callconv(.Stdcall) BOOLEAN; -pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; +pub extern "kernel32" fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) callconv(.Stdcall) ?HANDLE; -pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; +pub extern "kernel32" fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) callconv(.Stdcall) ?HANDLE; -pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; +pub extern "kernel32" fn DeviceIoControl( + h: HANDLE, + dwIoControlCode: DWORD, + lpInBuffer: ?*const c_void, + nInBufferSize: DWORD, + lpOutBuffer: ?LPVOID, + nOutBufferSize: DWORD, + lpBytesReturned: ?*DWORD, + lpOverlapped: ?*OVERLAPPED, +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; +pub extern "kernel32" fn DeleteFileW(lpFileName: [*:0]const u16) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE; -pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL; +pub extern "kernel32" fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn FormatMessageW(dwFlags: DWORD, lpSource: ?LPVOID, dwMessageId: DWORD, dwLanguageId: DWORD, lpBuffer: LPWSTR, nSize: DWORD, Arguments: ?*va_list) DWORD; +pub extern "kernel32" fn ExitProcess(exit_code: UINT) callconv(.Stdcall) noreturn; -pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsW(penv: [*]u16) BOOL; +pub extern "kernel32" fn FindFirstFileW(lpFileName: [*:0]const u16, lpFindFileData: *WIN32_FIND_DATAW) callconv(.Stdcall) HANDLE; +pub extern "kernel32" fn FindClose(hFindFile: HANDLE) callconv(.Stdcall) BOOL; +pub extern "kernel32" fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; +pub extern "kernel32" fn FormatMessageW(dwFlags: DWORD, lpSource: ?LPVOID, dwMessageId: DWORD, dwLanguageId: DWORD, lpBuffer: [*]u16, nSize: DWORD, Arguments: ?*va_list) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; +pub extern "kernel32" fn FreeEnvironmentStringsW(penv: [*:0]u16) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL; +pub extern "kernel32" fn GetCommandLineA() callconv(.Stdcall) LPSTR; -pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; +pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; -pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD; +pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsW() ?[*]u16; +pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) DWORD; +pub extern "kernel32" fn GetCurrentThread() callconv(.Stdcall) HANDLE; +pub extern "kernel32" fn GetCurrentThreadId() callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; +pub extern "kernel32" fn GetEnvironmentStringsW() callconv(.Stdcall) ?[*:0]u16; -pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; +pub extern "kernel32" fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: [*]u16, nSize: DWORD) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; +pub extern "kernel32" fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; +pub extern "kernel32" fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE; +pub extern "kernel32" fn GetFileAttributesW(lpFileName: [*]const WCHAR) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetLastError() DWORD; +pub extern "kernel32" fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetFileInformationByHandle( +pub extern "kernel32" fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) callconv(.Stdcall) HMODULE; + +pub extern "kernel32" fn GetLastError() callconv(.Stdcall) DWORD; + +pub extern "kernel32" fn GetFileInformationByHandle( hFile: HANDLE, lpFileInformation: *BY_HANDLE_FILE_INFORMATION, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( +pub extern "kernel32" fn GetFileInformationByHandleEx( in_hFile: HANDLE, in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: *c_void, in_dwBufferSize: DWORD, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( +pub extern "kernel32" fn GetFinalPathNameByHandleW( hFile: HANDLE, lpszFilePath: [*]u16, cchFilePath: DWORD, dwFlags: DWORD, -) DWORD; +) callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL; +pub extern "kernel32" fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; -pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; +pub extern "kernel32" fn GetProcessHeap() callconv(.Stdcall) ?HANDLE; +pub extern "kernel32" fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; -pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; +pub extern "kernel32" fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) callconv(.Stdcall) void; +pub extern "kernel32" fn GetSystemTimeAsFileTime(*FILETIME) callconv(.Stdcall) void; -pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; +pub extern "kernel32" fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) callconv(.Stdcall) ?HANDLE; +pub extern "kernel32" fn HeapDestroy(hHeap: HANDLE) callconv(.Stdcall) BOOL; +pub extern "kernel32" fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) callconv(.Stdcall) ?*c_void; +pub extern "kernel32" fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) callconv(.Stdcall) SIZE_T; +pub extern "kernel32" fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) callconv(.Stdcall) SIZE_T; +pub extern "kernel32" fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; +pub extern "kernel32" fn GetStdHandle(in_nStdHandle: DWORD) callconv(.Stdcall) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) callconv(.Stdcall) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; +pub extern "kernel32" fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; +pub extern "kernel32" fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) ?LPVOID; -pub extern "kernel32" stdcallcc fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) BOOL; +pub extern "kernel32" fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) callconv(.Stdcall) ?LPVOID; +pub extern "kernel32" fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn MoveFileExW( - lpExistingFileName: [*]const u16, - lpNewFileName: [*]const u16, +pub extern "kernel32" fn MoveFileExW( + lpExistingFileName: [*:0]const u16, + lpNewFileName: [*:0]const u16, dwFlags: DWORD, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; +pub extern "kernel32" fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; +pub extern "kernel32" fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; +pub extern "kernel32" fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW( +pub extern "kernel32" fn ReadDirectoryChangesW( hDirectory: HANDLE, lpBuffer: [*]align(@alignOf(FILE_NOTIFY_INFORMATION)) u8, nBufferLength: DWORD, @@ -150,65 +170,79 @@ pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW( lpBytesReturned: ?*DWORD, lpOverlapped: ?*OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn ReadFile( +pub extern "kernel32" fn ReadFile( in_hFile: HANDLE, out_lpBuffer: [*]u8, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL; +pub extern "kernel32" fn RemoveDirectoryW(lpPathName: [*:0]const u16) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL; +pub extern "kernel32" fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn SetFilePointerEx( +pub extern "kernel32" fn SetFilePointerEx( in_fFile: HANDLE, in_liDistanceToMove: LARGE_INTEGER, out_opt_ldNewFilePointer: ?*LARGE_INTEGER, in_dwMoveMethod: DWORD, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn SetFileTime( +pub extern "kernel32" fn SetFileTime( hFile: HANDLE, lpCreationTime: ?*const FILETIME, lpLastAccessTime: ?*const FILETIME, lpLastWriteTime: ?*const FILETIME, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; +pub extern "kernel32" fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; +pub extern "kernel32" fn Sleep(dwMilliseconds: DWORD) callconv(.Stdcall) void; -pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; +pub extern "kernel32" fn SwitchToThread() callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD; +pub extern "kernel32" fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL; +pub extern "kernel32" fn TlsAlloc() callconv(.Stdcall) DWORD; -pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; +pub extern "kernel32" fn TlsFree(dwTlsIndex: DWORD) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn WriteFile( +pub extern "kernel32" fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) callconv(.Stdcall) DWORD; + +pub extern "kernel32" fn WaitForSingleObjectEx(hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL) callconv(.Stdcall) DWORD; + +pub extern "kernel32" fn WaitForMultipleObjects(nCount: DWORD, lpHandle: [*]const HANDLE, bWaitAll: BOOL, dwMilliseconds: DWORD) callconv(.Stdcall) DWORD; + +pub extern "kernel32" fn WaitForMultipleObjectsEx( + nCount: DWORD, + lpHandle: [*]const HANDLE, + bWaitAll: BOOL, + dwMilliseconds: DWORD, + bAlertable: BOOL, +) callconv(.Stdcall) DWORD; + +pub extern "kernel32" fn WriteFile( in_hFile: HANDLE, in_lpBuffer: [*]const u8, in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; +) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; +pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE; +pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(.Stdcall) ?HMODULE; -pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName: [*]const u8) ?FARPROC; +pub extern "kernel32" fn GetProcAddress(hModule: HMODULE, lpProcName: [*]const u8) callconv(.Stdcall) ?FARPROC; -pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; +pub extern "kernel32" fn FreeLibrary(hModule: HMODULE) callconv(.Stdcall) BOOL; -pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void; +pub extern "kernel32" fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void; +pub extern "kernel32" fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void; +pub extern "kernel32" fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; +pub extern "kernel32" fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) callconv(.Stdcall) BOOL; diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index bfc98aba8..08f22386f 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -1,24 +1,75 @@ usingnamespace @import("bits.zig"); -pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; -pub extern "NtDll" stdcallcc fn NtQueryInformationFile( +pub extern "NtDll" fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) callconv(.Stdcall) WORD; +pub extern "NtDll" fn NtQueryInformationFile( FileHandle: HANDLE, IoStatusBlock: *IO_STATUS_BLOCK, FileInformation: *c_void, Length: ULONG, FileInformationClass: FILE_INFORMATION_CLASS, -) NTSTATUS; -pub extern "NtDll" stdcallcc fn NtCreateFile( +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtCreateFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: *OBJECT_ATTRIBUTES, IoStatusBlock: *IO_STATUS_BLOCK, - AllocationSize: *LARGE_INTEGER, + AllocationSize: ?*LARGE_INTEGER, FileAttributes: ULONG, ShareAccess: ULONG, CreateDisposition: ULONG, CreateOptions: ULONG, - EaBuffer: *c_void, + EaBuffer: ?*c_void, EaLength: ULONG, -) NTSTATUS; -pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS; +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtDeviceIoControlFile( + FileHandle: HANDLE, + Event: ?HANDLE, + ApcRoutine: ?IO_APC_ROUTINE, + ApcContext: ?*c_void, + IoStatusBlock: *IO_STATUS_BLOCK, + IoControlCode: ULONG, + InputBuffer: ?*const c_void, + InputBufferLength: ULONG, + OutputBuffer: ?PVOID, + OutputBufferLength: ULONG, +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn RtlDosPathNameToNtPathName_U( + DosPathName: [*:0]const u16, + NtPathName: *UNICODE_STRING, + NtFileNamePart: ?*?[*:0]const u16, + DirectoryInfo: ?*CURDIR, +) callconv(.Stdcall) BOOL; +pub extern "NtDll" fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) callconv(.Stdcall) void; + +pub extern "NtDll" fn NtQueryDirectoryFile( + FileHandle: HANDLE, + Event: ?HANDLE, + ApcRoutine: ?IO_APC_ROUTINE, + ApcContext: ?*c_void, + IoStatusBlock: *IO_STATUS_BLOCK, + FileInformation: *c_void, + Length: ULONG, + FileInformationClass: FILE_INFORMATION_CLASS, + ReturnSingleEntry: BOOLEAN, + FileName: ?*UNICODE_STRING, + RestartScan: BOOLEAN, +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtCreateKeyedEvent( + KeyedEventHandle: *HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: ?PVOID, + Flags: ULONG, +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtReleaseKeyedEvent( + EventHandle: HANDLE, + Key: *const c_void, + Alertable: BOOLEAN, + Timeout: ?*LARGE_INTEGER, +) callconv(.Stdcall) NTSTATUS; +pub extern "NtDll" fn NtWaitForKeyedEvent( + EventHandle: HANDLE, + Key: *const c_void, + Alertable: BOOLEAN, + Timeout: ?*LARGE_INTEGER, +) callconv(.Stdcall) NTSTATUS; diff --git a/lib/std/os/windows/ole32.zig b/lib/std/os/windows/ole32.zig index 39c12d074..a47208907 100644 --- a/lib/std/os/windows/ole32.zig +++ b/lib/std/os/windows/ole32.zig @@ -1,6 +1,6 @@ usingnamespace @import("bits.zig"); -pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; -pub extern "ole32" stdcallcc fn CoUninitialize() void; -pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD; -pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; +pub extern "ole32" fn CoTaskMemFree(pv: LPVOID) callconv(.Stdcall) void; +pub extern "ole32" fn CoUninitialize() callconv(.Stdcall) void; +pub extern "ole32" fn CoGetCurrentProcess() callconv(.Stdcall) DWORD; +pub extern "ole32" fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) callconv(.Stdcall) HRESULT; diff --git a/lib/std/os/windows/shell32.zig b/lib/std/os/windows/shell32.zig index c178997aa..762589c6d 100644 --- a/lib/std/os/windows/shell32.zig +++ b/lib/std/os/windows/shell32.zig @@ -1,3 +1,3 @@ usingnamespace @import("bits.zig"); -pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; +pub extern "shell32" fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*:0]WCHAR) callconv(.Stdcall) HRESULT; diff --git a/lib/std/os/windows/status.zig b/lib/std/os/windows/status.zig index b9fd2b495..71c1fbeac 100644 --- a/lib/std/os/windows/status.zig +++ b/lib/std/os/windows/status.zig @@ -1,5 +1,1670 @@ /// The operation completed successfully. pub const SUCCESS = 0x00000000; +pub const WAIT_0 = 0x00000000; +pub const WAIT_1 = 0x00000001; +pub const WAIT_2 = 0x00000002; +pub const WAIT_3 = 0x00000003; +pub const WAIT_63 = 0x0000003F; +pub const ABANDONED = 0x00000080; +pub const ABANDONED_WAIT_0 = 0x00000080; +pub const ABANDONED_WAIT_63 = 0x000000BF; +pub const USER_APC = 0x000000C0; +pub const ALERTED = 0x00000101; + +/// The given Timeout interval expired. +pub const TIMEOUT = 0x00000102; + +/// The operation that was requested is pending completion. +pub const PENDING = 0x00000103; + +pub const REPARSE = 0x00000104; +pub const MORE_ENTRIES = 0x00000105; +pub const NOT_ALL_ASSIGNED = 0x00000106; +pub const SOME_NOT_MAPPED = 0x00000107; +pub const OPLOCK_BREAK_IN_PROGRESS = 0x00000108; +pub const VOLUME_MOUNTED = 0x00000109; +pub const RXACT_COMMITTED = 0x0000010A; +pub const NOTIFY_CLEANUP = 0x0000010B; +pub const NOTIFY_ENUM_DIR = 0x0000010C; +pub const NO_QUOTAS_FOR_ACCOUNT = 0x0000010D; +pub const PRIMARY_TRANSPORT_CONNECT_FAILED = 0x0000010E; +pub const PAGE_FAULT_TRANSITION = 0x00000110; +pub const PAGE_FAULT_DEMAND_ZERO = 0x00000111; +pub const PAGE_FAULT_COPY_ON_WRITE = 0x00000112; +pub const PAGE_FAULT_GUARD_PAGE = 0x00000113; +pub const PAGE_FAULT_PAGING_FILE = 0x00000114; +pub const CACHE_PAGE_LOCKED = 0x00000115; +pub const CRASH_DUMP = 0x00000116; +pub const BUFFER_ALL_ZEROS = 0x00000117; +pub const REPARSE_OBJECT = 0x00000118; +pub const RESOURCE_REQUIREMENTS_CHANGED = 0x00000119; +pub const TRANSLATION_COMPLETE = 0x00000120; +pub const DS_MEMBERSHIP_EVALUATED_LOCALLY = 0x00000121; +pub const NOTHING_TO_TERMINATE = 0x00000122; +pub const PROCESS_NOT_IN_JOB = 0x00000123; +pub const PROCESS_IN_JOB = 0x00000124; +pub const VOLSNAP_HIBERNATE_READY = 0x00000125; +pub const FSFILTER_OP_COMPLETED_SUCCESSFULLY = 0x00000126; +pub const INTERRUPT_VECTOR_ALREADY_CONNECTED = 0x00000127; +pub const INTERRUPT_STILL_CONNECTED = 0x00000128; +pub const PROCESS_CLONED = 0x00000129; +pub const FILE_LOCKED_WITH_ONLY_READERS = 0x0000012A; +pub const FILE_LOCKED_WITH_WRITERS = 0x0000012B; +pub const RESOURCEMANAGER_READ_ONLY = 0x00000202; +pub const WAIT_FOR_OPLOCK = 0x00000367; +pub const FLT_IO_COMPLETE = 0x001C0001; +pub const FILE_NOT_AVAILABLE = 0xC0000467; +pub const OBJECT_NAME_EXISTS = 0x40000000; +pub const THREAD_WAS_SUSPENDED = 0x40000001; +pub const WORKING_SET_LIMIT_RANGE = 0x40000002; +pub const IMAGE_NOT_AT_BASE = 0x40000003; +pub const RXACT_STATE_CREATED = 0x40000004; +pub const SEGMENT_NOTIFICATION = 0x40000005; +pub const LOCAL_USER_SESSION_KEY = 0x40000006; +pub const BAD_CURRENT_DIRECTORY = 0x40000007; +pub const SERIAL_MORE_WRITES = 0x40000008; +pub const REGISTRY_RECOVERED = 0x40000009; +pub const FT_READ_RECOVERY_FROM_BACKUP = 0x4000000A; +pub const FT_WRITE_RECOVERY = 0x4000000B; +pub const SERIAL_COUNTER_TIMEOUT = 0x4000000C; +pub const NULL_LM_PASSWORD = 0x4000000D; +pub const IMAGE_MACHINE_TYPE_MISMATCH = 0x4000000E; +pub const RECEIVE_PARTIAL = 0x4000000F; +pub const RECEIVE_EXPEDITED = 0x40000010; +pub const RECEIVE_PARTIAL_EXPEDITED = 0x40000011; +pub const EVENT_DONE = 0x40000012; +pub const EVENT_PENDING = 0x40000013; +pub const CHECKING_FILE_SYSTEM = 0x40000014; +pub const FATAL_APP_EXIT = 0x40000015; +pub const PREDEFINED_HANDLE = 0x40000016; +pub const WAS_UNLOCKED = 0x40000017; +pub const SERVICE_NOTIFICATION = 0x40000018; +pub const WAS_LOCKED = 0x40000019; +pub const LOG_HARD_ERROR = 0x4000001A; +pub const ALREADY_WIN32 = 0x4000001B; +pub const WX86_UNSIMULATE = 0x4000001C; +pub const WX86_CONTINUE = 0x4000001D; +pub const WX86_SINGLE_STEP = 0x4000001E; +pub const WX86_BREAKPOINT = 0x4000001F; +pub const WX86_EXCEPTION_CONTINUE = 0x40000020; +pub const WX86_EXCEPTION_LASTCHANCE = 0x40000021; +pub const WX86_EXCEPTION_CHAIN = 0x40000022; +pub const IMAGE_MACHINE_TYPE_MISMATCH_EXE = 0x40000023; +pub const NO_YIELD_PERFORMED = 0x40000024; +pub const TIMER_RESUME_IGNORED = 0x40000025; +pub const ARBITRATION_UNHANDLED = 0x40000026; +pub const CARDBUS_NOT_SUPPORTED = 0x40000027; +pub const WX86_CREATEWX86TIB = 0x40000028; +pub const MP_PROCESSOR_MISMATCH = 0x40000029; +pub const HIBERNATED = 0x4000002A; +pub const RESUME_HIBERNATION = 0x4000002B; +pub const FIRMWARE_UPDATED = 0x4000002C; +pub const DRIVERS_LEAKING_LOCKED_PAGES = 0x4000002D; +pub const MESSAGE_RETRIEVED = 0x4000002E; +pub const SYSTEM_POWERSTATE_TRANSITION = 0x4000002F; +pub const ALPC_CHECK_COMPLETION_LIST = 0x40000030; +pub const SYSTEM_POWERSTATE_COMPLEX_TRANSITION = 0x40000031; +pub const ACCESS_AUDIT_BY_POLICY = 0x40000032; +pub const ABANDON_HIBERFILE = 0x40000033; +pub const BIZRULES_NOT_ENABLED = 0x40000034; +pub const WAKE_SYSTEM = 0x40000294; +pub const DS_SHUTTING_DOWN = 0x40000370; +pub const CTX_CDM_CONNECT = 0x400A0004; +pub const CTX_CDM_DISCONNECT = 0x400A0005; +pub const SXS_RELEASE_ACTIVATION_CONTEXT = 0x4015000D; +pub const RECOVERY_NOT_NEEDED = 0x40190034; +pub const RM_ALREADY_STARTED = 0x40190035; +pub const LOG_NO_RESTART = 0x401A000C; +pub const VIDEO_DRIVER_DEBUG_REPORT_REQUEST = 0x401B00EC; +pub const GRAPHICS_PARTIAL_DATA_POPULATED = 0x401E000A; +pub const GRAPHICS_DRIVER_MISMATCH = 0x401E0117; +pub const GRAPHICS_MODE_NOT_PINNED = 0x401E0307; +pub const GRAPHICS_NO_PREFERRED_MODE = 0x401E031E; +pub const GRAPHICS_DATASET_IS_EMPTY = 0x401E034B; +pub const GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET = 0x401E034C; +pub const GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED = 0x401E0351; +pub const GRAPHICS_UNKNOWN_CHILD_STATUS = 0x401E042F; +pub const GRAPHICS_LEADLINK_START_DEFERRED = 0x401E0437; +pub const GRAPHICS_POLLING_TOO_FREQUENTLY = 0x401E0439; +pub const GRAPHICS_START_DEFERRED = 0x401E043A; +pub const NDIS_INDICATION_REQUIRED = 0x40230001; +pub const GUARD_PAGE_VIOLATION = 0x80000001; +pub const DATATYPE_MISALIGNMENT = 0x80000002; +pub const BREAKPOINT = 0x80000003; +pub const SINGLE_STEP = 0x80000004; + /// The data was too large to fit into the specified buffer. pub const BUFFER_OVERFLOW = 0x80000005; +pub const NO_MORE_FILES = 0x80000006; +pub const WAKE_SYSTEM_DEBUGGER = 0x80000007; +pub const HANDLES_CLOSED = 0x8000000A; +pub const NO_INHERITANCE = 0x8000000B; +pub const GUID_SUBSTITUTION_MADE = 0x8000000C; +pub const PARTIAL_COPY = 0x8000000D; +pub const DEVICE_PAPER_EMPTY = 0x8000000E; +pub const DEVICE_POWERED_OFF = 0x8000000F; +pub const DEVICE_OFF_LINE = 0x80000010; +pub const DEVICE_BUSY = 0x80000011; +pub const NO_MORE_EAS = 0x80000012; +pub const INVALID_EA_NAME = 0x80000013; +pub const EA_LIST_INCONSISTENT = 0x80000014; +pub const INVALID_EA_FLAG = 0x80000015; +pub const VERIFY_REQUIRED = 0x80000016; +pub const EXTRANEOUS_INFORMATION = 0x80000017; +pub const RXACT_COMMIT_NECESSARY = 0x80000018; +pub const NO_MORE_ENTRIES = 0x8000001A; +pub const FILEMARK_DETECTED = 0x8000001B; +pub const MEDIA_CHANGED = 0x8000001C; +pub const BUS_RESET = 0x8000001D; +pub const END_OF_MEDIA = 0x8000001E; +pub const BEGINNING_OF_MEDIA = 0x8000001F; +pub const MEDIA_CHECK = 0x80000020; +pub const SETMARK_DETECTED = 0x80000021; +pub const NO_DATA_DETECTED = 0x80000022; +pub const REDIRECTOR_HAS_OPEN_HANDLES = 0x80000023; +pub const SERVER_HAS_OPEN_HANDLES = 0x80000024; +pub const ALREADY_DISCONNECTED = 0x80000025; +pub const LONGJUMP = 0x80000026; +pub const CLEANER_CARTRIDGE_INSTALLED = 0x80000027; +pub const PLUGPLAY_QUERY_VETOED = 0x80000028; +pub const UNWIND_CONSOLIDATE = 0x80000029; +pub const REGISTRY_HIVE_RECOVERED = 0x8000002A; +pub const DLL_MIGHT_BE_INSECURE = 0x8000002B; +pub const DLL_MIGHT_BE_INCOMPATIBLE = 0x8000002C; +pub const STOPPED_ON_SYMLINK = 0x8000002D; +pub const DEVICE_REQUIRES_CLEANING = 0x80000288; +pub const DEVICE_DOOR_OPEN = 0x80000289; +pub const DATA_LOST_REPAIR = 0x80000803; +pub const CLUSTER_NODE_ALREADY_UP = 0x80130001; +pub const CLUSTER_NODE_ALREADY_DOWN = 0x80130002; +pub const CLUSTER_NETWORK_ALREADY_ONLINE = 0x80130003; +pub const CLUSTER_NETWORK_ALREADY_OFFLINE = 0x80130004; +pub const CLUSTER_NODE_ALREADY_MEMBER = 0x80130005; +pub const COULD_NOT_RESIZE_LOG = 0x80190009; +pub const NO_TXF_METADATA = 0x80190029; +pub const CANT_RECOVER_WITH_HANDLE_OPEN = 0x80190031; +pub const TXF_METADATA_ALREADY_PRESENT = 0x80190041; +pub const TRANSACTION_SCOPE_CALLBACKS_NOT_SET = 0x80190042; +pub const VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED = 0x801B00EB; +pub const FLT_BUFFER_TOO_SMALL = 0x801C0001; +pub const FVE_PARTIAL_METADATA = 0x80210001; +pub const FVE_TRANSIENT_STATE = 0x80210002; +pub const UNSUCCESSFUL = 0xC0000001; +pub const NOT_IMPLEMENTED = 0xC0000002; +pub const INVALID_INFO_CLASS = 0xC0000003; +pub const INFO_LENGTH_MISMATCH = 0xC0000004; +pub const ACCESS_VIOLATION = 0xC0000005; +pub const IN_PAGE_ERROR = 0xC0000006; +pub const PAGEFILE_QUOTA = 0xC0000007; +pub const INVALID_HANDLE = 0xC0000008; +pub const BAD_INITIAL_STACK = 0xC0000009; +pub const BAD_INITIAL_PC = 0xC000000A; +pub const INVALID_CID = 0xC000000B; +pub const TIMER_NOT_CANCELED = 0xC000000C; +pub const INVALID_PARAMETER = 0xC000000D; +pub const NO_SUCH_DEVICE = 0xC000000E; +pub const NO_SUCH_FILE = 0xC000000F; +pub const INVALID_DEVICE_REQUEST = 0xC0000010; +pub const END_OF_FILE = 0xC0000011; +pub const WRONG_VOLUME = 0xC0000012; +pub const NO_MEDIA_IN_DEVICE = 0xC0000013; +pub const UNRECOGNIZED_MEDIA = 0xC0000014; +pub const NONEXISTENT_SECTOR = 0xC0000015; +pub const MORE_PROCESSING_REQUIRED = 0xC0000016; +pub const NO_MEMORY = 0xC0000017; +pub const CONFLICTING_ADDRESSES = 0xC0000018; +pub const NOT_MAPPED_VIEW = 0xC0000019; +pub const UNABLE_TO_FREE_VM = 0xC000001A; +pub const UNABLE_TO_DELETE_SECTION = 0xC000001B; +pub const INVALID_SYSTEM_SERVICE = 0xC000001C; +pub const ILLEGAL_INSTRUCTION = 0xC000001D; +pub const INVALID_LOCK_SEQUENCE = 0xC000001E; +pub const INVALID_VIEW_SIZE = 0xC000001F; +pub const INVALID_FILE_FOR_SECTION = 0xC0000020; +pub const ALREADY_COMMITTED = 0xC0000021; +pub const ACCESS_DENIED = 0xC0000022; +pub const BUFFER_TOO_SMALL = 0xC0000023; +pub const OBJECT_TYPE_MISMATCH = 0xC0000024; +pub const NONCONTINUABLE_EXCEPTION = 0xC0000025; +pub const INVALID_DISPOSITION = 0xC0000026; +pub const UNWIND = 0xC0000027; +pub const BAD_STACK = 0xC0000028; +pub const INVALID_UNWIND_TARGET = 0xC0000029; +pub const NOT_LOCKED = 0xC000002A; +pub const PARITY_ERROR = 0xC000002B; +pub const UNABLE_TO_DECOMMIT_VM = 0xC000002C; +pub const NOT_COMMITTED = 0xC000002D; +pub const INVALID_PORT_ATTRIBUTES = 0xC000002E; +pub const PORT_MESSAGE_TOO_LONG = 0xC000002F; +pub const INVALID_PARAMETER_MIX = 0xC0000030; +pub const INVALID_QUOTA_LOWER = 0xC0000031; +pub const DISK_CORRUPT_ERROR = 0xC0000032; +pub const OBJECT_NAME_INVALID = 0xC0000033; +pub const OBJECT_NAME_NOT_FOUND = 0xC0000034; +pub const OBJECT_NAME_COLLISION = 0xC0000035; +pub const PORT_DISCONNECTED = 0xC0000037; +pub const DEVICE_ALREADY_ATTACHED = 0xC0000038; +pub const OBJECT_PATH_INVALID = 0xC0000039; +pub const OBJECT_PATH_NOT_FOUND = 0xC000003A; +pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B; +pub const DATA_OVERRUN = 0xC000003C; +pub const DATA_LATE_ERROR = 0xC000003D; +pub const DATA_ERROR = 0xC000003E; +pub const CRC_ERROR = 0xC000003F; +pub const SECTION_TOO_BIG = 0xC0000040; +pub const PORT_CONNECTION_REFUSED = 0xC0000041; +pub const INVALID_PORT_HANDLE = 0xC0000042; +pub const SHARING_VIOLATION = 0xC0000043; +pub const QUOTA_EXCEEDED = 0xC0000044; +pub const INVALID_PAGE_PROTECTION = 0xC0000045; +pub const MUTANT_NOT_OWNED = 0xC0000046; +pub const SEMAPHORE_LIMIT_EXCEEDED = 0xC0000047; +pub const PORT_ALREADY_SET = 0xC0000048; +pub const SECTION_NOT_IMAGE = 0xC0000049; +pub const SUSPEND_COUNT_EXCEEDED = 0xC000004A; +pub const THREAD_IS_TERMINATING = 0xC000004B; +pub const BAD_WORKING_SET_LIMIT = 0xC000004C; +pub const INCOMPATIBLE_FILE_MAP = 0xC000004D; +pub const SECTION_PROTECTION = 0xC000004E; +pub const EAS_NOT_SUPPORTED = 0xC000004F; +pub const EA_TOO_LARGE = 0xC0000050; +pub const NONEXISTENT_EA_ENTRY = 0xC0000051; +pub const NO_EAS_ON_FILE = 0xC0000052; +pub const EA_CORRUPT_ERROR = 0xC0000053; +pub const FILE_LOCK_CONFLICT = 0xC0000054; +pub const LOCK_NOT_GRANTED = 0xC0000055; +pub const DELETE_PENDING = 0xC0000056; +pub const CTL_FILE_NOT_SUPPORTED = 0xC0000057; +pub const UNKNOWN_REVISION = 0xC0000058; +pub const REVISION_MISMATCH = 0xC0000059; +pub const INVALID_OWNER = 0xC000005A; +pub const INVALID_PRIMARY_GROUP = 0xC000005B; +pub const NO_IMPERSONATION_TOKEN = 0xC000005C; +pub const CANT_DISABLE_MANDATORY = 0xC000005D; +pub const NO_LOGON_SERVERS = 0xC000005E; +pub const NO_SUCH_LOGON_SESSION = 0xC000005F; +pub const NO_SUCH_PRIVILEGE = 0xC0000060; +pub const PRIVILEGE_NOT_HELD = 0xC0000061; +pub const INVALID_ACCOUNT_NAME = 0xC0000062; +pub const USER_EXISTS = 0xC0000063; +pub const NO_SUCH_USER = 0xC0000064; +pub const GROUP_EXISTS = 0xC0000065; +pub const NO_SUCH_GROUP = 0xC0000066; +pub const MEMBER_IN_GROUP = 0xC0000067; +pub const MEMBER_NOT_IN_GROUP = 0xC0000068; +pub const LAST_ADMIN = 0xC0000069; +pub const WRONG_PASSWORD = 0xC000006A; +pub const ILL_FORMED_PASSWORD = 0xC000006B; +pub const PASSWORD_RESTRICTION = 0xC000006C; +pub const LOGON_FAILURE = 0xC000006D; +pub const ACCOUNT_RESTRICTION = 0xC000006E; +pub const INVALID_LOGON_HOURS = 0xC000006F; +pub const INVALID_WORKSTATION = 0xC0000070; +pub const PASSWORD_EXPIRED = 0xC0000071; +pub const ACCOUNT_DISABLED = 0xC0000072; +pub const NONE_MAPPED = 0xC0000073; +pub const TOO_MANY_LUIDS_REQUESTED = 0xC0000074; +pub const LUIDS_EXHAUSTED = 0xC0000075; +pub const INVALID_SUB_AUTHORITY = 0xC0000076; +pub const INVALID_ACL = 0xC0000077; +pub const INVALID_SID = 0xC0000078; +pub const INVALID_SECURITY_DESCR = 0xC0000079; +pub const PROCEDURE_NOT_FOUND = 0xC000007A; +pub const INVALID_IMAGE_FORMAT = 0xC000007B; +pub const NO_TOKEN = 0xC000007C; +pub const BAD_INHERITANCE_ACL = 0xC000007D; +pub const RANGE_NOT_LOCKED = 0xC000007E; +pub const DISK_FULL = 0xC000007F; +pub const SERVER_DISABLED = 0xC0000080; +pub const SERVER_NOT_DISABLED = 0xC0000081; +pub const TOO_MANY_GUIDS_REQUESTED = 0xC0000082; +pub const GUIDS_EXHAUSTED = 0xC0000083; +pub const INVALID_ID_AUTHORITY = 0xC0000084; +pub const AGENTS_EXHAUSTED = 0xC0000085; +pub const INVALID_VOLUME_LABEL = 0xC0000086; +pub const SECTION_NOT_EXTENDED = 0xC0000087; +pub const NOT_MAPPED_DATA = 0xC0000088; +pub const RESOURCE_DATA_NOT_FOUND = 0xC0000089; +pub const RESOURCE_TYPE_NOT_FOUND = 0xC000008A; +pub const RESOURCE_NAME_NOT_FOUND = 0xC000008B; +pub const ARRAY_BOUNDS_EXCEEDED = 0xC000008C; +pub const FLOAT_DENORMAL_OPERAND = 0xC000008D; +pub const FLOAT_DIVIDE_BY_ZERO = 0xC000008E; +pub const FLOAT_INEXACT_RESULT = 0xC000008F; +pub const FLOAT_INVALID_OPERATION = 0xC0000090; +pub const FLOAT_OVERFLOW = 0xC0000091; +pub const FLOAT_STACK_CHECK = 0xC0000092; +pub const FLOAT_UNDERFLOW = 0xC0000093; +pub const INTEGER_DIVIDE_BY_ZERO = 0xC0000094; +pub const INTEGER_OVERFLOW = 0xC0000095; +pub const PRIVILEGED_INSTRUCTION = 0xC0000096; +pub const TOO_MANY_PAGING_FILES = 0xC0000097; +pub const FILE_INVALID = 0xC0000098; +pub const ALLOTTED_SPACE_EXCEEDED = 0xC0000099; +pub const INSUFFICIENT_RESOURCES = 0xC000009A; +pub const DFS_EXIT_PATH_FOUND = 0xC000009B; +pub const DEVICE_DATA_ERROR = 0xC000009C; +pub const DEVICE_NOT_CONNECTED = 0xC000009D; +pub const FREE_VM_NOT_AT_BASE = 0xC000009F; +pub const MEMORY_NOT_ALLOCATED = 0xC00000A0; +pub const WORKING_SET_QUOTA = 0xC00000A1; +pub const MEDIA_WRITE_PROTECTED = 0xC00000A2; +pub const DEVICE_NOT_READY = 0xC00000A3; +pub const INVALID_GROUP_ATTRIBUTES = 0xC00000A4; +pub const BAD_IMPERSONATION_LEVEL = 0xC00000A5; +pub const CANT_OPEN_ANONYMOUS = 0xC00000A6; +pub const BAD_VALIDATION_CLASS = 0xC00000A7; +pub const BAD_TOKEN_TYPE = 0xC00000A8; +pub const BAD_MASTER_BOOT_RECORD = 0xC00000A9; +pub const INSTRUCTION_MISALIGNMENT = 0xC00000AA; +pub const INSTANCE_NOT_AVAILABLE = 0xC00000AB; +pub const PIPE_NOT_AVAILABLE = 0xC00000AC; +pub const INVALID_PIPE_STATE = 0xC00000AD; +pub const PIPE_BUSY = 0xC00000AE; +pub const ILLEGAL_FUNCTION = 0xC00000AF; +pub const PIPE_DISCONNECTED = 0xC00000B0; +pub const PIPE_CLOSING = 0xC00000B1; +pub const PIPE_CONNECTED = 0xC00000B2; +pub const PIPE_LISTENING = 0xC00000B3; +pub const INVALID_READ_MODE = 0xC00000B4; +pub const IO_TIMEOUT = 0xC00000B5; +pub const FILE_FORCED_CLOSED = 0xC00000B6; +pub const PROFILING_NOT_STARTED = 0xC00000B7; +pub const PROFILING_NOT_STOPPED = 0xC00000B8; +pub const COULD_NOT_INTERPRET = 0xC00000B9; +pub const FILE_IS_A_DIRECTORY = 0xC00000BA; +pub const NOT_SUPPORTED = 0xC00000BB; +pub const REMOTE_NOT_LISTENING = 0xC00000BC; +pub const DUPLICATE_NAME = 0xC00000BD; +pub const BAD_NETWORK_PATH = 0xC00000BE; +pub const NETWORK_BUSY = 0xC00000BF; +pub const DEVICE_DOES_NOT_EXIST = 0xC00000C0; +pub const TOO_MANY_COMMANDS = 0xC00000C1; +pub const ADAPTER_HARDWARE_ERROR = 0xC00000C2; +pub const INVALID_NETWORK_RESPONSE = 0xC00000C3; +pub const UNEXPECTED_NETWORK_ERROR = 0xC00000C4; +pub const BAD_REMOTE_ADAPTER = 0xC00000C5; +pub const PRINT_QUEUE_FULL = 0xC00000C6; +pub const NO_SPOOL_SPACE = 0xC00000C7; +pub const PRINT_CANCELLED = 0xC00000C8; +pub const NETWORK_NAME_DELETED = 0xC00000C9; +pub const NETWORK_ACCESS_DENIED = 0xC00000CA; +pub const BAD_DEVICE_TYPE = 0xC00000CB; +pub const BAD_NETWORK_NAME = 0xC00000CC; +pub const TOO_MANY_NAMES = 0xC00000CD; +pub const TOO_MANY_SESSIONS = 0xC00000CE; +pub const SHARING_PAUSED = 0xC00000CF; +pub const REQUEST_NOT_ACCEPTED = 0xC00000D0; +pub const REDIRECTOR_PAUSED = 0xC00000D1; +pub const NET_WRITE_FAULT = 0xC00000D2; +pub const PROFILING_AT_LIMIT = 0xC00000D3; +pub const NOT_SAME_DEVICE = 0xC00000D4; +pub const FILE_RENAMED = 0xC00000D5; +pub const VIRTUAL_CIRCUIT_CLOSED = 0xC00000D6; +pub const NO_SECURITY_ON_OBJECT = 0xC00000D7; +pub const CANT_WAIT = 0xC00000D8; +pub const PIPE_EMPTY = 0xC00000D9; +pub const CANT_ACCESS_DOMAIN_INFO = 0xC00000DA; +pub const CANT_TERMINATE_SELF = 0xC00000DB; +pub const INVALID_SERVER_STATE = 0xC00000DC; +pub const INVALID_DOMAIN_STATE = 0xC00000DD; +pub const INVALID_DOMAIN_ROLE = 0xC00000DE; +pub const NO_SUCH_DOMAIN = 0xC00000DF; +pub const DOMAIN_EXISTS = 0xC00000E0; +pub const DOMAIN_LIMIT_EXCEEDED = 0xC00000E1; +pub const OPLOCK_NOT_GRANTED = 0xC00000E2; +pub const INVALID_OPLOCK_PROTOCOL = 0xC00000E3; +pub const INTERNAL_DB_CORRUPTION = 0xC00000E4; +pub const INTERNAL_ERROR = 0xC00000E5; +pub const GENERIC_NOT_MAPPED = 0xC00000E6; +pub const BAD_DESCRIPTOR_FORMAT = 0xC00000E7; +pub const INVALID_USER_BUFFER = 0xC00000E8; +pub const UNEXPECTED_IO_ERROR = 0xC00000E9; +pub const UNEXPECTED_MM_CREATE_ERR = 0xC00000EA; +pub const UNEXPECTED_MM_MAP_ERROR = 0xC00000EB; +pub const UNEXPECTED_MM_EXTEND_ERR = 0xC00000EC; +pub const NOT_LOGON_PROCESS = 0xC00000ED; +pub const LOGON_SESSION_EXISTS = 0xC00000EE; +pub const INVALID_PARAMETER_1 = 0xC00000EF; +pub const INVALID_PARAMETER_2 = 0xC00000F0; +pub const INVALID_PARAMETER_3 = 0xC00000F1; +pub const INVALID_PARAMETER_4 = 0xC00000F2; +pub const INVALID_PARAMETER_5 = 0xC00000F3; +pub const INVALID_PARAMETER_6 = 0xC00000F4; +pub const INVALID_PARAMETER_7 = 0xC00000F5; +pub const INVALID_PARAMETER_8 = 0xC00000F6; +pub const INVALID_PARAMETER_9 = 0xC00000F7; +pub const INVALID_PARAMETER_10 = 0xC00000F8; +pub const INVALID_PARAMETER_11 = 0xC00000F9; +pub const INVALID_PARAMETER_12 = 0xC00000FA; +pub const REDIRECTOR_NOT_STARTED = 0xC00000FB; +pub const REDIRECTOR_STARTED = 0xC00000FC; +pub const STACK_OVERFLOW = 0xC00000FD; +pub const NO_SUCH_PACKAGE = 0xC00000FE; +pub const BAD_FUNCTION_TABLE = 0xC00000FF; +pub const VARIABLE_NOT_FOUND = 0xC0000100; +pub const DIRECTORY_NOT_EMPTY = 0xC0000101; +pub const FILE_CORRUPT_ERROR = 0xC0000102; +pub const NOT_A_DIRECTORY = 0xC0000103; +pub const BAD_LOGON_SESSION_STATE = 0xC0000104; +pub const LOGON_SESSION_COLLISION = 0xC0000105; +pub const NAME_TOO_LONG = 0xC0000106; +pub const FILES_OPEN = 0xC0000107; +pub const CONNECTION_IN_USE = 0xC0000108; +pub const MESSAGE_NOT_FOUND = 0xC0000109; +pub const PROCESS_IS_TERMINATING = 0xC000010A; +pub const INVALID_LOGON_TYPE = 0xC000010B; +pub const NO_GUID_TRANSLATION = 0xC000010C; +pub const CANNOT_IMPERSONATE = 0xC000010D; +pub const IMAGE_ALREADY_LOADED = 0xC000010E; +pub const NO_LDT = 0xC0000117; +pub const INVALID_LDT_SIZE = 0xC0000118; +pub const INVALID_LDT_OFFSET = 0xC0000119; +pub const INVALID_LDT_DESCRIPTOR = 0xC000011A; +pub const INVALID_IMAGE_NE_FORMAT = 0xC000011B; +pub const RXACT_INVALID_STATE = 0xC000011C; +pub const RXACT_COMMIT_FAILURE = 0xC000011D; +pub const MAPPED_FILE_SIZE_ZERO = 0xC000011E; +pub const TOO_MANY_OPENED_FILES = 0xC000011F; +pub const CANCELLED = 0xC0000120; +pub const CANNOT_DELETE = 0xC0000121; +pub const INVALID_COMPUTER_NAME = 0xC0000122; +pub const FILE_DELETED = 0xC0000123; +pub const SPECIAL_ACCOUNT = 0xC0000124; +pub const SPECIAL_GROUP = 0xC0000125; +pub const SPECIAL_USER = 0xC0000126; +pub const MEMBERS_PRIMARY_GROUP = 0xC0000127; +pub const FILE_CLOSED = 0xC0000128; +pub const TOO_MANY_THREADS = 0xC0000129; +pub const THREAD_NOT_IN_PROCESS = 0xC000012A; +pub const TOKEN_ALREADY_IN_USE = 0xC000012B; +pub const PAGEFILE_QUOTA_EXCEEDED = 0xC000012C; +pub const COMMITMENT_LIMIT = 0xC000012D; +pub const INVALID_IMAGE_LE_FORMAT = 0xC000012E; +pub const INVALID_IMAGE_NOT_MZ = 0xC000012F; +pub const INVALID_IMAGE_PROTECT = 0xC0000130; +pub const INVALID_IMAGE_WIN_16 = 0xC0000131; +pub const LOGON_SERVER_CONFLICT = 0xC0000132; +pub const TIME_DIFFERENCE_AT_DC = 0xC0000133; +pub const SYNCHRONIZATION_REQUIRED = 0xC0000134; +pub const DLL_NOT_FOUND = 0xC0000135; +pub const OPEN_FAILED = 0xC0000136; +pub const IO_PRIVILEGE_FAILED = 0xC0000137; +pub const ORDINAL_NOT_FOUND = 0xC0000138; +pub const ENTRYPOINT_NOT_FOUND = 0xC0000139; +pub const CONTROL_C_EXIT = 0xC000013A; +pub const LOCAL_DISCONNECT = 0xC000013B; +pub const REMOTE_DISCONNECT = 0xC000013C; +pub const REMOTE_RESOURCES = 0xC000013D; +pub const LINK_FAILED = 0xC000013E; +pub const LINK_TIMEOUT = 0xC000013F; +pub const INVALID_CONNECTION = 0xC0000140; +pub const INVALID_ADDRESS = 0xC0000141; +pub const DLL_INIT_FAILED = 0xC0000142; +pub const MISSING_SYSTEMFILE = 0xC0000143; +pub const UNHANDLED_EXCEPTION = 0xC0000144; +pub const APP_INIT_FAILURE = 0xC0000145; +pub const PAGEFILE_CREATE_FAILED = 0xC0000146; +pub const NO_PAGEFILE = 0xC0000147; +pub const INVALID_LEVEL = 0xC0000148; +pub const WRONG_PASSWORD_CORE = 0xC0000149; +pub const ILLEGAL_FLOAT_CONTEXT = 0xC000014A; +pub const PIPE_BROKEN = 0xC000014B; +pub const REGISTRY_CORRUPT = 0xC000014C; +pub const REGISTRY_IO_FAILED = 0xC000014D; +pub const NO_EVENT_PAIR = 0xC000014E; +pub const UNRECOGNIZED_VOLUME = 0xC000014F; +pub const SERIAL_NO_DEVICE_INITED = 0xC0000150; +pub const NO_SUCH_ALIAS = 0xC0000151; +pub const MEMBER_NOT_IN_ALIAS = 0xC0000152; +pub const MEMBER_IN_ALIAS = 0xC0000153; +pub const ALIAS_EXISTS = 0xC0000154; +pub const LOGON_NOT_GRANTED = 0xC0000155; +pub const TOO_MANY_SECRETS = 0xC0000156; +pub const SECRET_TOO_LONG = 0xC0000157; +pub const INTERNAL_DB_ERROR = 0xC0000158; +pub const FULLSCREEN_MODE = 0xC0000159; +pub const TOO_MANY_CONTEXT_IDS = 0xC000015A; +pub const LOGON_TYPE_NOT_GRANTED = 0xC000015B; +pub const NOT_REGISTRY_FILE = 0xC000015C; +pub const NT_CROSS_ENCRYPTION_REQUIRED = 0xC000015D; +pub const DOMAIN_CTRLR_CONFIG_ERROR = 0xC000015E; +pub const FT_MISSING_MEMBER = 0xC000015F; +pub const ILL_FORMED_SERVICE_ENTRY = 0xC0000160; +pub const ILLEGAL_CHARACTER = 0xC0000161; +pub const UNMAPPABLE_CHARACTER = 0xC0000162; +pub const UNDEFINED_CHARACTER = 0xC0000163; +pub const FLOPPY_VOLUME = 0xC0000164; +pub const FLOPPY_ID_MARK_NOT_FOUND = 0xC0000165; +pub const FLOPPY_WRONG_CYLINDER = 0xC0000166; +pub const FLOPPY_UNKNOWN_ERROR = 0xC0000167; +pub const FLOPPY_BAD_REGISTERS = 0xC0000168; +pub const DISK_RECALIBRATE_FAILED = 0xC0000169; +pub const DISK_OPERATION_FAILED = 0xC000016A; +pub const DISK_RESET_FAILED = 0xC000016B; +pub const SHARED_IRQ_BUSY = 0xC000016C; +pub const FT_ORPHANING = 0xC000016D; +pub const BIOS_FAILED_TO_CONNECT_INTERRUPT = 0xC000016E; +pub const PARTITION_FAILURE = 0xC0000172; +pub const INVALID_BLOCK_LENGTH = 0xC0000173; +pub const DEVICE_NOT_PARTITIONED = 0xC0000174; +pub const UNABLE_TO_LOCK_MEDIA = 0xC0000175; +pub const UNABLE_TO_UNLOAD_MEDIA = 0xC0000176; +pub const EOM_OVERFLOW = 0xC0000177; +pub const NO_MEDIA = 0xC0000178; +pub const NO_SUCH_MEMBER = 0xC000017A; +pub const INVALID_MEMBER = 0xC000017B; +pub const KEY_DELETED = 0xC000017C; +pub const NO_LOG_SPACE = 0xC000017D; +pub const TOO_MANY_SIDS = 0xC000017E; +pub const LM_CROSS_ENCRYPTION_REQUIRED = 0xC000017F; +pub const KEY_HAS_CHILDREN = 0xC0000180; +pub const CHILD_MUST_BE_VOLATILE = 0xC0000181; +pub const DEVICE_CONFIGURATION_ERROR = 0xC0000182; +pub const DRIVER_INTERNAL_ERROR = 0xC0000183; +pub const INVALID_DEVICE_STATE = 0xC0000184; +pub const IO_DEVICE_ERROR = 0xC0000185; +pub const DEVICE_PROTOCOL_ERROR = 0xC0000186; +pub const BACKUP_CONTROLLER = 0xC0000187; +pub const LOG_FILE_FULL = 0xC0000188; +pub const TOO_LATE = 0xC0000189; +pub const NO_TRUST_LSA_SECRET = 0xC000018A; +pub const NO_TRUST_SAM_ACCOUNT = 0xC000018B; +pub const TRUSTED_DOMAIN_FAILURE = 0xC000018C; +pub const TRUSTED_RELATIONSHIP_FAILURE = 0xC000018D; +pub const EVENTLOG_FILE_CORRUPT = 0xC000018E; +pub const EVENTLOG_CANT_START = 0xC000018F; +pub const TRUST_FAILURE = 0xC0000190; +pub const MUTANT_LIMIT_EXCEEDED = 0xC0000191; +pub const NETLOGON_NOT_STARTED = 0xC0000192; +pub const ACCOUNT_EXPIRED = 0xC0000193; +pub const POSSIBLE_DEADLOCK = 0xC0000194; +pub const NETWORK_CREDENTIAL_CONFLICT = 0xC0000195; +pub const REMOTE_SESSION_LIMIT = 0xC0000196; +pub const EVENTLOG_FILE_CHANGED = 0xC0000197; +pub const NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xC0000198; +pub const NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xC0000199; +pub const NOLOGON_SERVER_TRUST_ACCOUNT = 0xC000019A; +pub const DOMAIN_TRUST_INCONSISTENT = 0xC000019B; +pub const FS_DRIVER_REQUIRED = 0xC000019C; +pub const IMAGE_ALREADY_LOADED_AS_DLL = 0xC000019D; +pub const INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 0xC000019E; +pub const SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 0xC000019F; +pub const SECURITY_STREAM_IS_INCONSISTENT = 0xC00001A0; +pub const INVALID_LOCK_RANGE = 0xC00001A1; +pub const INVALID_ACE_CONDITION = 0xC00001A2; +pub const IMAGE_SUBSYSTEM_NOT_PRESENT = 0xC00001A3; +pub const NOTIFICATION_GUID_ALREADY_DEFINED = 0xC00001A4; +pub const NETWORK_OPEN_RESTRICTION = 0xC0000201; +pub const NO_USER_SESSION_KEY = 0xC0000202; +pub const USER_SESSION_DELETED = 0xC0000203; +pub const RESOURCE_LANG_NOT_FOUND = 0xC0000204; +pub const INSUFF_SERVER_RESOURCES = 0xC0000205; +pub const INVALID_BUFFER_SIZE = 0xC0000206; +pub const INVALID_ADDRESS_COMPONENT = 0xC0000207; +pub const INVALID_ADDRESS_WILDCARD = 0xC0000208; +pub const TOO_MANY_ADDRESSES = 0xC0000209; +pub const ADDRESS_ALREADY_EXISTS = 0xC000020A; +pub const ADDRESS_CLOSED = 0xC000020B; +pub const CONNECTION_DISCONNECTED = 0xC000020C; +pub const CONNECTION_RESET = 0xC000020D; +pub const TOO_MANY_NODES = 0xC000020E; +pub const TRANSACTION_ABORTED = 0xC000020F; +pub const TRANSACTION_TIMED_OUT = 0xC0000210; +pub const TRANSACTION_NO_RELEASE = 0xC0000211; +pub const TRANSACTION_NO_MATCH = 0xC0000212; +pub const TRANSACTION_RESPONDED = 0xC0000213; +pub const TRANSACTION_INVALID_ID = 0xC0000214; +pub const TRANSACTION_INVALID_TYPE = 0xC0000215; +pub const NOT_SERVER_SESSION = 0xC0000216; +pub const NOT_CLIENT_SESSION = 0xC0000217; +pub const CANNOT_LOAD_REGISTRY_FILE = 0xC0000218; +pub const DEBUG_ATTACH_FAILED = 0xC0000219; +pub const SYSTEM_PROCESS_TERMINATED = 0xC000021A; +pub const DATA_NOT_ACCEPTED = 0xC000021B; +pub const NO_BROWSER_SERVERS_FOUND = 0xC000021C; +pub const VDM_HARD_ERROR = 0xC000021D; +pub const DRIVER_CANCEL_TIMEOUT = 0xC000021E; +pub const REPLY_MESSAGE_MISMATCH = 0xC000021F; +pub const MAPPED_ALIGNMENT = 0xC0000220; +pub const IMAGE_CHECKSUM_MISMATCH = 0xC0000221; +pub const LOST_WRITEBEHIND_DATA = 0xC0000222; +pub const CLIENT_SERVER_PARAMETERS_INVALID = 0xC0000223; +pub const PASSWORD_MUST_CHANGE = 0xC0000224; +pub const NOT_FOUND = 0xC0000225; +pub const NOT_TINY_STREAM = 0xC0000226; +pub const RECOVERY_FAILURE = 0xC0000227; +pub const STACK_OVERFLOW_READ = 0xC0000228; +pub const FAIL_CHECK = 0xC0000229; +pub const DUPLICATE_OBJECTID = 0xC000022A; +pub const OBJECTID_EXISTS = 0xC000022B; +pub const CONVERT_TO_LARGE = 0xC000022C; +pub const RETRY = 0xC000022D; +pub const FOUND_OUT_OF_SCOPE = 0xC000022E; +pub const ALLOCATE_BUCKET = 0xC000022F; +pub const PROPSET_NOT_FOUND = 0xC0000230; +pub const MARSHALL_OVERFLOW = 0xC0000231; +pub const INVALID_VARIANT = 0xC0000232; +pub const DOMAIN_CONTROLLER_NOT_FOUND = 0xC0000233; +pub const ACCOUNT_LOCKED_OUT = 0xC0000234; +pub const HANDLE_NOT_CLOSABLE = 0xC0000235; +pub const CONNECTION_REFUSED = 0xC0000236; +pub const GRACEFUL_DISCONNECT = 0xC0000237; +pub const ADDRESS_ALREADY_ASSOCIATED = 0xC0000238; +pub const ADDRESS_NOT_ASSOCIATED = 0xC0000239; +pub const CONNECTION_INVALID = 0xC000023A; +pub const CONNECTION_ACTIVE = 0xC000023B; +pub const NETWORK_UNREACHABLE = 0xC000023C; +pub const HOST_UNREACHABLE = 0xC000023D; +pub const PROTOCOL_UNREACHABLE = 0xC000023E; +pub const PORT_UNREACHABLE = 0xC000023F; +pub const REQUEST_ABORTED = 0xC0000240; +pub const CONNECTION_ABORTED = 0xC0000241; +pub const BAD_COMPRESSION_BUFFER = 0xC0000242; +pub const USER_MAPPED_FILE = 0xC0000243; +pub const AUDIT_FAILED = 0xC0000244; +pub const TIMER_RESOLUTION_NOT_SET = 0xC0000245; +pub const CONNECTION_COUNT_LIMIT = 0xC0000246; +pub const LOGIN_TIME_RESTRICTION = 0xC0000247; +pub const LOGIN_WKSTA_RESTRICTION = 0xC0000248; +pub const IMAGE_MP_UP_MISMATCH = 0xC0000249; +pub const INSUFFICIENT_LOGON_INFO = 0xC0000250; +pub const BAD_DLL_ENTRYPOINT = 0xC0000251; +pub const BAD_SERVICE_ENTRYPOINT = 0xC0000252; +pub const LPC_REPLY_LOST = 0xC0000253; +pub const IP_ADDRESS_CONFLICT1 = 0xC0000254; +pub const IP_ADDRESS_CONFLICT2 = 0xC0000255; +pub const REGISTRY_QUOTA_LIMIT = 0xC0000256; +pub const PATH_NOT_COVERED = 0xC0000257; +pub const NO_CALLBACK_ACTIVE = 0xC0000258; +pub const LICENSE_QUOTA_EXCEEDED = 0xC0000259; +pub const PWD_TOO_SHORT = 0xC000025A; +pub const PWD_TOO_RECENT = 0xC000025B; +pub const PWD_HISTORY_CONFLICT = 0xC000025C; +pub const PLUGPLAY_NO_DEVICE = 0xC000025E; +pub const UNSUPPORTED_COMPRESSION = 0xC000025F; +pub const INVALID_HW_PROFILE = 0xC0000260; +pub const INVALID_PLUGPLAY_DEVICE_PATH = 0xC0000261; +pub const DRIVER_ORDINAL_NOT_FOUND = 0xC0000262; +pub const DRIVER_ENTRYPOINT_NOT_FOUND = 0xC0000263; +pub const RESOURCE_NOT_OWNED = 0xC0000264; +pub const TOO_MANY_LINKS = 0xC0000265; +pub const QUOTA_LIST_INCONSISTENT = 0xC0000266; +pub const FILE_IS_OFFLINE = 0xC0000267; +pub const EVALUATION_EXPIRATION = 0xC0000268; +pub const ILLEGAL_DLL_RELOCATION = 0xC0000269; +pub const LICENSE_VIOLATION = 0xC000026A; +pub const DLL_INIT_FAILED_LOGOFF = 0xC000026B; +pub const DRIVER_UNABLE_TO_LOAD = 0xC000026C; +pub const DFS_UNAVAILABLE = 0xC000026D; +pub const VOLUME_DISMOUNTED = 0xC000026E; +pub const WX86_INTERNAL_ERROR = 0xC000026F; +pub const WX86_FLOAT_STACK_CHECK = 0xC0000270; +pub const VALIDATE_CONTINUE = 0xC0000271; +pub const NO_MATCH = 0xC0000272; +pub const NO_MORE_MATCHES = 0xC0000273; +pub const NOT_A_REPARSE_POINT = 0xC0000275; +pub const IO_REPARSE_TAG_INVALID = 0xC0000276; +pub const IO_REPARSE_TAG_MISMATCH = 0xC0000277; +pub const IO_REPARSE_DATA_INVALID = 0xC0000278; +pub const IO_REPARSE_TAG_NOT_HANDLED = 0xC0000279; +pub const REPARSE_POINT_NOT_RESOLVED = 0xC0000280; +pub const DIRECTORY_IS_A_REPARSE_POINT = 0xC0000281; +pub const RANGE_LIST_CONFLICT = 0xC0000282; +pub const SOURCE_ELEMENT_EMPTY = 0xC0000283; +pub const DESTINATION_ELEMENT_FULL = 0xC0000284; +pub const ILLEGAL_ELEMENT_ADDRESS = 0xC0000285; +pub const MAGAZINE_NOT_PRESENT = 0xC0000286; +pub const REINITIALIZATION_NEEDED = 0xC0000287; +pub const ENCRYPTION_FAILED = 0xC000028A; +pub const DECRYPTION_FAILED = 0xC000028B; +pub const RANGE_NOT_FOUND = 0xC000028C; +pub const NO_RECOVERY_POLICY = 0xC000028D; +pub const NO_EFS = 0xC000028E; +pub const WRONG_EFS = 0xC000028F; +pub const NO_USER_KEYS = 0xC0000290; +pub const FILE_NOT_ENCRYPTED = 0xC0000291; +pub const NOT_EXPORT_FORMAT = 0xC0000292; +pub const FILE_ENCRYPTED = 0xC0000293; +pub const WMI_GUID_NOT_FOUND = 0xC0000295; +pub const WMI_INSTANCE_NOT_FOUND = 0xC0000296; +pub const WMI_ITEMID_NOT_FOUND = 0xC0000297; +pub const WMI_TRY_AGAIN = 0xC0000298; +pub const SHARED_POLICY = 0xC0000299; +pub const POLICY_OBJECT_NOT_FOUND = 0xC000029A; +pub const POLICY_ONLY_IN_DS = 0xC000029B; +pub const VOLUME_NOT_UPGRADED = 0xC000029C; +pub const REMOTE_STORAGE_NOT_ACTIVE = 0xC000029D; +pub const REMOTE_STORAGE_MEDIA_ERROR = 0xC000029E; +pub const NO_TRACKING_SERVICE = 0xC000029F; +pub const SERVER_SID_MISMATCH = 0xC00002A0; +pub const DS_NO_ATTRIBUTE_OR_VALUE = 0xC00002A1; +pub const DS_INVALID_ATTRIBUTE_SYNTAX = 0xC00002A2; +pub const DS_ATTRIBUTE_TYPE_UNDEFINED = 0xC00002A3; +pub const DS_ATTRIBUTE_OR_VALUE_EXISTS = 0xC00002A4; +pub const DS_BUSY = 0xC00002A5; +pub const DS_UNAVAILABLE = 0xC00002A6; +pub const DS_NO_RIDS_ALLOCATED = 0xC00002A7; +pub const DS_NO_MORE_RIDS = 0xC00002A8; +pub const DS_INCORRECT_ROLE_OWNER = 0xC00002A9; +pub const DS_RIDMGR_INIT_ERROR = 0xC00002AA; +pub const DS_OBJ_CLASS_VIOLATION = 0xC00002AB; +pub const DS_CANT_ON_NON_LEAF = 0xC00002AC; +pub const DS_CANT_ON_RDN = 0xC00002AD; +pub const DS_CANT_MOD_OBJ_CLASS = 0xC00002AE; +pub const DS_CROSS_DOM_MOVE_FAILED = 0xC00002AF; +pub const DS_GC_NOT_AVAILABLE = 0xC00002B0; +pub const DIRECTORY_SERVICE_REQUIRED = 0xC00002B1; +pub const REPARSE_ATTRIBUTE_CONFLICT = 0xC00002B2; +pub const CANT_ENABLE_DENY_ONLY = 0xC00002B3; +pub const FLOAT_MULTIPLE_FAULTS = 0xC00002B4; +pub const FLOAT_MULTIPLE_TRAPS = 0xC00002B5; +pub const DEVICE_REMOVED = 0xC00002B6; +pub const JOURNAL_DELETE_IN_PROGRESS = 0xC00002B7; +pub const JOURNAL_NOT_ACTIVE = 0xC00002B8; +pub const NOINTERFACE = 0xC00002B9; +pub const DS_ADMIN_LIMIT_EXCEEDED = 0xC00002C1; +pub const DRIVER_FAILED_SLEEP = 0xC00002C2; +pub const MUTUAL_AUTHENTICATION_FAILED = 0xC00002C3; +pub const CORRUPT_SYSTEM_FILE = 0xC00002C4; +pub const DATATYPE_MISALIGNMENT_ERROR = 0xC00002C5; +pub const WMI_READ_ONLY = 0xC00002C6; +pub const WMI_SET_FAILURE = 0xC00002C7; +pub const COMMITMENT_MINIMUM = 0xC00002C8; +pub const REG_NAT_CONSUMPTION = 0xC00002C9; +pub const TRANSPORT_FULL = 0xC00002CA; +pub const DS_SAM_INIT_FAILURE = 0xC00002CB; +pub const ONLY_IF_CONNECTED = 0xC00002CC; +pub const DS_SENSITIVE_GROUP_VIOLATION = 0xC00002CD; +pub const PNP_RESTART_ENUMERATION = 0xC00002CE; +pub const JOURNAL_ENTRY_DELETED = 0xC00002CF; +pub const DS_CANT_MOD_PRIMARYGROUPID = 0xC00002D0; +pub const SYSTEM_IMAGE_BAD_SIGNATURE = 0xC00002D1; +pub const PNP_REBOOT_REQUIRED = 0xC00002D2; +pub const POWER_STATE_INVALID = 0xC00002D3; +pub const DS_INVALID_GROUP_TYPE = 0xC00002D4; +pub const DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN = 0xC00002D5; +pub const DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN = 0xC00002D6; +pub const DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER = 0xC00002D7; +pub const DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER = 0xC00002D8; +pub const DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER = 0xC00002D9; +pub const DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER = 0xC00002DA; +pub const DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER = 0xC00002DB; +pub const DS_HAVE_PRIMARY_MEMBERS = 0xC00002DC; +pub const WMI_NOT_SUPPORTED = 0xC00002DD; +pub const INSUFFICIENT_POWER = 0xC00002DE; +pub const SAM_NEED_BOOTKEY_PASSWORD = 0xC00002DF; +pub const SAM_NEED_BOOTKEY_FLOPPY = 0xC00002E0; +pub const DS_CANT_START = 0xC00002E1; +pub const DS_INIT_FAILURE = 0xC00002E2; +pub const SAM_INIT_FAILURE = 0xC00002E3; +pub const DS_GC_REQUIRED = 0xC00002E4; +pub const DS_LOCAL_MEMBER_OF_LOCAL_ONLY = 0xC00002E5; +pub const DS_NO_FPO_IN_UNIVERSAL_GROUPS = 0xC00002E6; +pub const DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED = 0xC00002E7; +pub const CURRENT_DOMAIN_NOT_ALLOWED = 0xC00002E9; +pub const CANNOT_MAKE = 0xC00002EA; +pub const SYSTEM_SHUTDOWN = 0xC00002EB; +pub const DS_INIT_FAILURE_CONSOLE = 0xC00002EC; +pub const DS_SAM_INIT_FAILURE_CONSOLE = 0xC00002ED; +pub const UNFINISHED_CONTEXT_DELETED = 0xC00002EE; +pub const NO_TGT_REPLY = 0xC00002EF; +pub const OBJECTID_NOT_FOUND = 0xC00002F0; +pub const NO_IP_ADDRESSES = 0xC00002F1; +pub const WRONG_CREDENTIAL_HANDLE = 0xC00002F2; +pub const CRYPTO_SYSTEM_INVALID = 0xC00002F3; +pub const MAX_REFERRALS_EXCEEDED = 0xC00002F4; +pub const MUST_BE_KDC = 0xC00002F5; +pub const STRONG_CRYPTO_NOT_SUPPORTED = 0xC00002F6; +pub const TOO_MANY_PRINCIPALS = 0xC00002F7; +pub const NO_PA_DATA = 0xC00002F8; +pub const PKINIT_NAME_MISMATCH = 0xC00002F9; +pub const SMARTCARD_LOGON_REQUIRED = 0xC00002FA; +pub const KDC_INVALID_REQUEST = 0xC00002FB; +pub const KDC_UNABLE_TO_REFER = 0xC00002FC; +pub const KDC_UNKNOWN_ETYPE = 0xC00002FD; +pub const SHUTDOWN_IN_PROGRESS = 0xC00002FE; +pub const SERVER_SHUTDOWN_IN_PROGRESS = 0xC00002FF; +pub const NOT_SUPPORTED_ON_SBS = 0xC0000300; +pub const WMI_GUID_DISCONNECTED = 0xC0000301; +pub const WMI_ALREADY_DISABLED = 0xC0000302; +pub const WMI_ALREADY_ENABLED = 0xC0000303; +pub const MFT_TOO_FRAGMENTED = 0xC0000304; +pub const COPY_PROTECTION_FAILURE = 0xC0000305; +pub const CSS_AUTHENTICATION_FAILURE = 0xC0000306; +pub const CSS_KEY_NOT_PRESENT = 0xC0000307; +pub const CSS_KEY_NOT_ESTABLISHED = 0xC0000308; +pub const CSS_SCRAMBLED_SECTOR = 0xC0000309; +pub const CSS_REGION_MISMATCH = 0xC000030A; +pub const CSS_RESETS_EXHAUSTED = 0xC000030B; +pub const PKINIT_FAILURE = 0xC0000320; +pub const SMARTCARD_SUBSYSTEM_FAILURE = 0xC0000321; +pub const NO_KERB_KEY = 0xC0000322; +pub const HOST_DOWN = 0xC0000350; +pub const UNSUPPORTED_PREAUTH = 0xC0000351; +pub const EFS_ALG_BLOB_TOO_BIG = 0xC0000352; +pub const PORT_NOT_SET = 0xC0000353; +pub const DEBUGGER_INACTIVE = 0xC0000354; +pub const DS_VERSION_CHECK_FAILURE = 0xC0000355; +pub const AUDITING_DISABLED = 0xC0000356; +pub const PRENT4_MACHINE_ACCOUNT = 0xC0000357; +pub const DS_AG_CANT_HAVE_UNIVERSAL_MEMBER = 0xC0000358; +pub const INVALID_IMAGE_WIN_32 = 0xC0000359; +pub const INVALID_IMAGE_WIN_64 = 0xC000035A; +pub const BAD_BINDINGS = 0xC000035B; +pub const NETWORK_SESSION_EXPIRED = 0xC000035C; +pub const APPHELP_BLOCK = 0xC000035D; +pub const ALL_SIDS_FILTERED = 0xC000035E; +pub const NOT_SAFE_MODE_DRIVER = 0xC000035F; +pub const ACCESS_DISABLED_BY_POLICY_DEFAULT = 0xC0000361; +pub const ACCESS_DISABLED_BY_POLICY_PATH = 0xC0000362; +pub const ACCESS_DISABLED_BY_POLICY_PUBLISHER = 0xC0000363; +pub const ACCESS_DISABLED_BY_POLICY_OTHER = 0xC0000364; +pub const FAILED_DRIVER_ENTRY = 0xC0000365; +pub const DEVICE_ENUMERATION_ERROR = 0xC0000366; +pub const MOUNT_POINT_NOT_RESOLVED = 0xC0000368; +pub const INVALID_DEVICE_OBJECT_PARAMETER = 0xC0000369; +pub const MCA_OCCURED = 0xC000036A; +pub const DRIVER_BLOCKED_CRITICAL = 0xC000036B; +pub const DRIVER_BLOCKED = 0xC000036C; +pub const DRIVER_DATABASE_ERROR = 0xC000036D; +pub const SYSTEM_HIVE_TOO_LARGE = 0xC000036E; +pub const INVALID_IMPORT_OF_NON_DLL = 0xC000036F; +pub const NO_SECRETS = 0xC0000371; +pub const ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY = 0xC0000372; +pub const FAILED_STACK_SWITCH = 0xC0000373; +pub const HEAP_CORRUPTION = 0xC0000374; +pub const SMARTCARD_WRONG_PIN = 0xC0000380; +pub const SMARTCARD_CARD_BLOCKED = 0xC0000381; +pub const SMARTCARD_CARD_NOT_AUTHENTICATED = 0xC0000382; +pub const SMARTCARD_NO_CARD = 0xC0000383; +pub const SMARTCARD_NO_KEY_CONTAINER = 0xC0000384; +pub const SMARTCARD_NO_CERTIFICATE = 0xC0000385; +pub const SMARTCARD_NO_KEYSET = 0xC0000386; +pub const SMARTCARD_IO_ERROR = 0xC0000387; +pub const DOWNGRADE_DETECTED = 0xC0000388; +pub const SMARTCARD_CERT_REVOKED = 0xC0000389; +pub const ISSUING_CA_UNTRUSTED = 0xC000038A; +pub const REVOCATION_OFFLINE_C = 0xC000038B; +pub const PKINIT_CLIENT_FAILURE = 0xC000038C; +pub const SMARTCARD_CERT_EXPIRED = 0xC000038D; +pub const DRIVER_FAILED_PRIOR_UNLOAD = 0xC000038E; +pub const SMARTCARD_SILENT_CONTEXT = 0xC000038F; +pub const PER_USER_TRUST_QUOTA_EXCEEDED = 0xC0000401; +pub const ALL_USER_TRUST_QUOTA_EXCEEDED = 0xC0000402; +pub const USER_DELETE_TRUST_QUOTA_EXCEEDED = 0xC0000403; +pub const DS_NAME_NOT_UNIQUE = 0xC0000404; +pub const DS_DUPLICATE_ID_FOUND = 0xC0000405; +pub const DS_GROUP_CONVERSION_ERROR = 0xC0000406; +pub const VOLSNAP_PREPARE_HIBERNATE = 0xC0000407; +pub const USER2USER_REQUIRED = 0xC0000408; +pub const STACK_BUFFER_OVERRUN = 0xC0000409; +pub const NO_S4U_PROT_SUPPORT = 0xC000040A; +pub const CROSSREALM_DELEGATION_FAILURE = 0xC000040B; +pub const REVOCATION_OFFLINE_KDC = 0xC000040C; +pub const ISSUING_CA_UNTRUSTED_KDC = 0xC000040D; +pub const KDC_CERT_EXPIRED = 0xC000040E; +pub const KDC_CERT_REVOKED = 0xC000040F; +pub const PARAMETER_QUOTA_EXCEEDED = 0xC0000410; +pub const HIBERNATION_FAILURE = 0xC0000411; +pub const DELAY_LOAD_FAILED = 0xC0000412; +pub const AUTHENTICATION_FIREWALL_FAILED = 0xC0000413; +pub const VDM_DISALLOWED = 0xC0000414; +pub const HUNG_DISPLAY_DRIVER_THREAD = 0xC0000415; +pub const INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE = 0xC0000416; +pub const INVALID_CRUNTIME_PARAMETER = 0xC0000417; +pub const NTLM_BLOCKED = 0xC0000418; +pub const DS_SRC_SID_EXISTS_IN_FOREST = 0xC0000419; +pub const DS_DOMAIN_NAME_EXISTS_IN_FOREST = 0xC000041A; +pub const DS_FLAT_NAME_EXISTS_IN_FOREST = 0xC000041B; +pub const INVALID_USER_PRINCIPAL_NAME = 0xC000041C; +pub const ASSERTION_FAILURE = 0xC0000420; +pub const VERIFIER_STOP = 0xC0000421; +pub const CALLBACK_POP_STACK = 0xC0000423; +pub const INCOMPATIBLE_DRIVER_BLOCKED = 0xC0000424; +pub const HIVE_UNLOADED = 0xC0000425; +pub const COMPRESSION_DISABLED = 0xC0000426; +pub const FILE_SYSTEM_LIMITATION = 0xC0000427; +pub const INVALID_IMAGE_HASH = 0xC0000428; +pub const NOT_CAPABLE = 0xC0000429; +pub const REQUEST_OUT_OF_SEQUENCE = 0xC000042A; +pub const IMPLEMENTATION_LIMIT = 0xC000042B; +pub const ELEVATION_REQUIRED = 0xC000042C; +pub const NO_SECURITY_CONTEXT = 0xC000042D; +pub const PKU2U_CERT_FAILURE = 0xC000042E; +pub const BEYOND_VDL = 0xC0000432; +pub const ENCOUNTERED_WRITE_IN_PROGRESS = 0xC0000433; +pub const PTE_CHANGED = 0xC0000434; +pub const PURGE_FAILED = 0xC0000435; +pub const CRED_REQUIRES_CONFIRMATION = 0xC0000440; +pub const CS_ENCRYPTION_INVALID_SERVER_RESPONSE = 0xC0000441; +pub const CS_ENCRYPTION_UNSUPPORTED_SERVER = 0xC0000442; +pub const CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE = 0xC0000443; +pub const CS_ENCRYPTION_NEW_ENCRYPTED_FILE = 0xC0000444; +pub const CS_ENCRYPTION_FILE_NOT_CSE = 0xC0000445; +pub const INVALID_LABEL = 0xC0000446; +pub const DRIVER_PROCESS_TERMINATED = 0xC0000450; +pub const AMBIGUOUS_SYSTEM_DEVICE = 0xC0000451; +pub const SYSTEM_DEVICE_NOT_FOUND = 0xC0000452; +pub const RESTART_BOOT_APPLICATION = 0xC0000453; +pub const INSUFFICIENT_NVRAM_RESOURCES = 0xC0000454; +pub const INVALID_TASK_NAME = 0xC0000500; +pub const INVALID_TASK_INDEX = 0xC0000501; +pub const THREAD_ALREADY_IN_TASK = 0xC0000502; +pub const CALLBACK_BYPASS = 0xC0000503; +pub const FAIL_FAST_EXCEPTION = 0xC0000602; +pub const IMAGE_CERT_REVOKED = 0xC0000603; +pub const PORT_CLOSED = 0xC0000700; +pub const MESSAGE_LOST = 0xC0000701; +pub const INVALID_MESSAGE = 0xC0000702; +pub const REQUEST_CANCELED = 0xC0000703; +pub const RECURSIVE_DISPATCH = 0xC0000704; +pub const LPC_RECEIVE_BUFFER_EXPECTED = 0xC0000705; +pub const LPC_INVALID_CONNECTION_USAGE = 0xC0000706; +pub const LPC_REQUESTS_NOT_ALLOWED = 0xC0000707; +pub const RESOURCE_IN_USE = 0xC0000708; +pub const HARDWARE_MEMORY_ERROR = 0xC0000709; +pub const THREADPOOL_HANDLE_EXCEPTION = 0xC000070A; +pub const THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED = 0xC000070B; +pub const THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED = 0xC000070C; +pub const THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED = 0xC000070D; +pub const THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED = 0xC000070E; +pub const THREADPOOL_RELEASED_DURING_OPERATION = 0xC000070F; +pub const CALLBACK_RETURNED_WHILE_IMPERSONATING = 0xC0000710; +pub const APC_RETURNED_WHILE_IMPERSONATING = 0xC0000711; +pub const PROCESS_IS_PROTECTED = 0xC0000712; +pub const MCA_EXCEPTION = 0xC0000713; +pub const CERTIFICATE_MAPPING_NOT_UNIQUE = 0xC0000714; +pub const SYMLINK_CLASS_DISABLED = 0xC0000715; +pub const INVALID_IDN_NORMALIZATION = 0xC0000716; +pub const NO_UNICODE_TRANSLATION = 0xC0000717; +pub const ALREADY_REGISTERED = 0xC0000718; +pub const CONTEXT_MISMATCH = 0xC0000719; +pub const PORT_ALREADY_HAS_COMPLETION_LIST = 0xC000071A; +pub const CALLBACK_RETURNED_THREAD_PRIORITY = 0xC000071B; +pub const INVALID_THREAD = 0xC000071C; +pub const CALLBACK_RETURNED_TRANSACTION = 0xC000071D; +pub const CALLBACK_RETURNED_LDR_LOCK = 0xC000071E; +pub const CALLBACK_RETURNED_LANG = 0xC000071F; +pub const CALLBACK_RETURNED_PRI_BACK = 0xC0000720; +pub const DISK_REPAIR_DISABLED = 0xC0000800; +pub const DS_DOMAIN_RENAME_IN_PROGRESS = 0xC0000801; +pub const DISK_QUOTA_EXCEEDED = 0xC0000802; +pub const CONTENT_BLOCKED = 0xC0000804; +pub const BAD_CLUSTERS = 0xC0000805; +pub const VOLUME_DIRTY = 0xC0000806; +pub const FILE_CHECKED_OUT = 0xC0000901; +pub const CHECKOUT_REQUIRED = 0xC0000902; +pub const BAD_FILE_TYPE = 0xC0000903; +pub const FILE_TOO_LARGE = 0xC0000904; +pub const FORMS_AUTH_REQUIRED = 0xC0000905; +pub const VIRUS_INFECTED = 0xC0000906; +pub const VIRUS_DELETED = 0xC0000907; +pub const BAD_MCFG_TABLE = 0xC0000908; +pub const CANNOT_BREAK_OPLOCK = 0xC0000909; +pub const WOW_ASSERTION = 0xC0009898; +pub const INVALID_SIGNATURE = 0xC000A000; +pub const HMAC_NOT_SUPPORTED = 0xC000A001; +pub const IPSEC_QUEUE_OVERFLOW = 0xC000A010; +pub const ND_QUEUE_OVERFLOW = 0xC000A011; +pub const HOPLIMIT_EXCEEDED = 0xC000A012; +pub const PROTOCOL_NOT_SUPPORTED = 0xC000A013; +pub const LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED = 0xC000A080; +pub const LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR = 0xC000A081; +pub const LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR = 0xC000A082; +pub const XML_PARSE_ERROR = 0xC000A083; +pub const XMLDSIG_ERROR = 0xC000A084; +pub const WRONG_COMPARTMENT = 0xC000A085; +pub const AUTHIP_FAILURE = 0xC000A086; +pub const DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS = 0xC000A087; +pub const DS_OID_NOT_FOUND = 0xC000A088; +pub const HASH_NOT_SUPPORTED = 0xC000A100; +pub const HASH_NOT_PRESENT = 0xC000A101; +pub const PNP_BAD_MPS_TABLE = 0xC0040035; +pub const PNP_TRANSLATION_FAILED = 0xC0040036; +pub const PNP_IRQ_TRANSLATION_FAILED = 0xC0040037; +pub const PNP_INVALID_ID = 0xC0040038; +pub const IO_REISSUE_AS_CACHED = 0xC0040039; +pub const CTX_WINSTATION_NAME_INVALID = 0xC00A0001; +pub const CTX_INVALID_PD = 0xC00A0002; +pub const CTX_PD_NOT_FOUND = 0xC00A0003; +pub const CTX_CLOSE_PENDING = 0xC00A0006; +pub const CTX_NO_OUTBUF = 0xC00A0007; +pub const CTX_MODEM_INF_NOT_FOUND = 0xC00A0008; +pub const CTX_INVALID_MODEMNAME = 0xC00A0009; +pub const CTX_RESPONSE_ERROR = 0xC00A000A; +pub const CTX_MODEM_RESPONSE_TIMEOUT = 0xC00A000B; +pub const CTX_MODEM_RESPONSE_NO_CARRIER = 0xC00A000C; +pub const CTX_MODEM_RESPONSE_NO_DIALTONE = 0xC00A000D; +pub const CTX_MODEM_RESPONSE_BUSY = 0xC00A000E; +pub const CTX_MODEM_RESPONSE_VOICE = 0xC00A000F; +pub const CTX_TD_ERROR = 0xC00A0010; +pub const CTX_LICENSE_CLIENT_INVALID = 0xC00A0012; +pub const CTX_LICENSE_NOT_AVAILABLE = 0xC00A0013; +pub const CTX_LICENSE_EXPIRED = 0xC00A0014; +pub const CTX_WINSTATION_NOT_FOUND = 0xC00A0015; +pub const CTX_WINSTATION_NAME_COLLISION = 0xC00A0016; +pub const CTX_WINSTATION_BUSY = 0xC00A0017; +pub const CTX_BAD_VIDEO_MODE = 0xC00A0018; +pub const CTX_GRAPHICS_INVALID = 0xC00A0022; +pub const CTX_NOT_CONSOLE = 0xC00A0024; +pub const CTX_CLIENT_QUERY_TIMEOUT = 0xC00A0026; +pub const CTX_CONSOLE_DISCONNECT = 0xC00A0027; +pub const CTX_CONSOLE_CONNECT = 0xC00A0028; +pub const CTX_SHADOW_DENIED = 0xC00A002A; +pub const CTX_WINSTATION_ACCESS_DENIED = 0xC00A002B; +pub const CTX_INVALID_WD = 0xC00A002E; +pub const CTX_WD_NOT_FOUND = 0xC00A002F; +pub const CTX_SHADOW_INVALID = 0xC00A0030; +pub const CTX_SHADOW_DISABLED = 0xC00A0031; +pub const RDP_PROTOCOL_ERROR = 0xC00A0032; +pub const CTX_CLIENT_LICENSE_NOT_SET = 0xC00A0033; +pub const CTX_CLIENT_LICENSE_IN_USE = 0xC00A0034; +pub const CTX_SHADOW_ENDED_BY_MODE_CHANGE = 0xC00A0035; +pub const CTX_SHADOW_NOT_RUNNING = 0xC00A0036; +pub const CTX_LOGON_DISABLED = 0xC00A0037; +pub const CTX_SECURITY_LAYER_ERROR = 0xC00A0038; +pub const TS_INCOMPATIBLE_SESSIONS = 0xC00A0039; +pub const MUI_FILE_NOT_FOUND = 0xC00B0001; +pub const MUI_INVALID_FILE = 0xC00B0002; +pub const MUI_INVALID_RC_CONFIG = 0xC00B0003; +pub const MUI_INVALID_LOCALE_NAME = 0xC00B0004; +pub const MUI_INVALID_ULTIMATEFALLBACK_NAME = 0xC00B0005; +pub const MUI_FILE_NOT_LOADED = 0xC00B0006; +pub const RESOURCE_ENUM_USER_STOP = 0xC00B0007; +pub const CLUSTER_INVALID_NODE = 0xC0130001; +pub const CLUSTER_NODE_EXISTS = 0xC0130002; +pub const CLUSTER_JOIN_IN_PROGRESS = 0xC0130003; +pub const CLUSTER_NODE_NOT_FOUND = 0xC0130004; +pub const CLUSTER_LOCAL_NODE_NOT_FOUND = 0xC0130005; +pub const CLUSTER_NETWORK_EXISTS = 0xC0130006; +pub const CLUSTER_NETWORK_NOT_FOUND = 0xC0130007; +pub const CLUSTER_NETINTERFACE_EXISTS = 0xC0130008; +pub const CLUSTER_NETINTERFACE_NOT_FOUND = 0xC0130009; +pub const CLUSTER_INVALID_REQUEST = 0xC013000A; +pub const CLUSTER_INVALID_NETWORK_PROVIDER = 0xC013000B; +pub const CLUSTER_NODE_DOWN = 0xC013000C; +pub const CLUSTER_NODE_UNREACHABLE = 0xC013000D; +pub const CLUSTER_NODE_NOT_MEMBER = 0xC013000E; +pub const CLUSTER_JOIN_NOT_IN_PROGRESS = 0xC013000F; +pub const CLUSTER_INVALID_NETWORK = 0xC0130010; +pub const CLUSTER_NO_NET_ADAPTERS = 0xC0130011; +pub const CLUSTER_NODE_UP = 0xC0130012; +pub const CLUSTER_NODE_PAUSED = 0xC0130013; +pub const CLUSTER_NODE_NOT_PAUSED = 0xC0130014; +pub const CLUSTER_NO_SECURITY_CONTEXT = 0xC0130015; +pub const CLUSTER_NETWORK_NOT_INTERNAL = 0xC0130016; +pub const CLUSTER_POISONED = 0xC0130017; +pub const ACPI_INVALID_OPCODE = 0xC0140001; +pub const ACPI_STACK_OVERFLOW = 0xC0140002; +pub const ACPI_ASSERT_FAILED = 0xC0140003; +pub const ACPI_INVALID_INDEX = 0xC0140004; +pub const ACPI_INVALID_ARGUMENT = 0xC0140005; +pub const ACPI_FATAL = 0xC0140006; +pub const ACPI_INVALID_SUPERNAME = 0xC0140007; +pub const ACPI_INVALID_ARGTYPE = 0xC0140008; +pub const ACPI_INVALID_OBJTYPE = 0xC0140009; +pub const ACPI_INVALID_TARGETTYPE = 0xC014000A; +pub const ACPI_INCORRECT_ARGUMENT_COUNT = 0xC014000B; +pub const ACPI_ADDRESS_NOT_MAPPED = 0xC014000C; +pub const ACPI_INVALID_EVENTTYPE = 0xC014000D; +pub const ACPI_HANDLER_COLLISION = 0xC014000E; +pub const ACPI_INVALID_DATA = 0xC014000F; +pub const ACPI_INVALID_REGION = 0xC0140010; +pub const ACPI_INVALID_ACCESS_SIZE = 0xC0140011; +pub const ACPI_ACQUIRE_GLOBAL_LOCK = 0xC0140012; +pub const ACPI_ALREADY_INITIALIZED = 0xC0140013; +pub const ACPI_NOT_INITIALIZED = 0xC0140014; +pub const ACPI_INVALID_MUTEX_LEVEL = 0xC0140015; +pub const ACPI_MUTEX_NOT_OWNED = 0xC0140016; +pub const ACPI_MUTEX_NOT_OWNER = 0xC0140017; +pub const ACPI_RS_ACCESS = 0xC0140018; +pub const ACPI_INVALID_TABLE = 0xC0140019; +pub const ACPI_REG_HANDLER_FAILED = 0xC0140020; +pub const ACPI_POWER_REQUEST_FAILED = 0xC0140021; +pub const SXS_SECTION_NOT_FOUND = 0xC0150001; +pub const SXS_CANT_GEN_ACTCTX = 0xC0150002; +pub const SXS_INVALID_ACTCTXDATA_FORMAT = 0xC0150003; +pub const SXS_ASSEMBLY_NOT_FOUND = 0xC0150004; +pub const SXS_MANIFEST_FORMAT_ERROR = 0xC0150005; +pub const SXS_MANIFEST_PARSE_ERROR = 0xC0150006; +pub const SXS_ACTIVATION_CONTEXT_DISABLED = 0xC0150007; +pub const SXS_KEY_NOT_FOUND = 0xC0150008; +pub const SXS_VERSION_CONFLICT = 0xC0150009; +pub const SXS_WRONG_SECTION_TYPE = 0xC015000A; +pub const SXS_THREAD_QUERIES_DISABLED = 0xC015000B; +pub const SXS_ASSEMBLY_MISSING = 0xC015000C; +pub const SXS_PROCESS_DEFAULT_ALREADY_SET = 0xC015000E; +pub const SXS_EARLY_DEACTIVATION = 0xC015000F; +pub const SXS_INVALID_DEACTIVATION = 0xC0150010; +pub const SXS_MULTIPLE_DEACTIVATION = 0xC0150011; +pub const SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY = 0xC0150012; +pub const SXS_PROCESS_TERMINATION_REQUESTED = 0xC0150013; +pub const SXS_CORRUPT_ACTIVATION_STACK = 0xC0150014; +pub const SXS_CORRUPTION = 0xC0150015; +pub const SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE = 0xC0150016; +pub const SXS_INVALID_IDENTITY_ATTRIBUTE_NAME = 0xC0150017; +pub const SXS_IDENTITY_DUPLICATE_ATTRIBUTE = 0xC0150018; +pub const SXS_IDENTITY_PARSE_ERROR = 0xC0150019; +pub const SXS_COMPONENT_STORE_CORRUPT = 0xC015001A; +pub const SXS_FILE_HASH_MISMATCH = 0xC015001B; +pub const SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT = 0xC015001C; +pub const SXS_IDENTITIES_DIFFERENT = 0xC015001D; +pub const SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT = 0xC015001E; +pub const SXS_FILE_NOT_PART_OF_ASSEMBLY = 0xC015001F; +pub const ADVANCED_INSTALLER_FAILED = 0xC0150020; +pub const XML_ENCODING_MISMATCH = 0xC0150021; +pub const SXS_MANIFEST_TOO_BIG = 0xC0150022; +pub const SXS_SETTING_NOT_REGISTERED = 0xC0150023; +pub const SXS_TRANSACTION_CLOSURE_INCOMPLETE = 0xC0150024; +pub const SMI_PRIMITIVE_INSTALLER_FAILED = 0xC0150025; +pub const GENERIC_COMMAND_FAILED = 0xC0150026; +pub const SXS_FILE_HASH_MISSING = 0xC0150027; +pub const TRANSACTIONAL_CONFLICT = 0xC0190001; +pub const INVALID_TRANSACTION = 0xC0190002; +pub const TRANSACTION_NOT_ACTIVE = 0xC0190003; +pub const TM_INITIALIZATION_FAILED = 0xC0190004; +pub const RM_NOT_ACTIVE = 0xC0190005; +pub const RM_METADATA_CORRUPT = 0xC0190006; +pub const TRANSACTION_NOT_JOINED = 0xC0190007; +pub const DIRECTORY_NOT_RM = 0xC0190008; +pub const TRANSACTIONS_UNSUPPORTED_REMOTE = 0xC019000A; +pub const LOG_RESIZE_INVALID_SIZE = 0xC019000B; +pub const REMOTE_FILE_VERSION_MISMATCH = 0xC019000C; +pub const CRM_PROTOCOL_ALREADY_EXISTS = 0xC019000F; +pub const TRANSACTION_PROPAGATION_FAILED = 0xC0190010; +pub const CRM_PROTOCOL_NOT_FOUND = 0xC0190011; +pub const TRANSACTION_SUPERIOR_EXISTS = 0xC0190012; +pub const TRANSACTION_REQUEST_NOT_VALID = 0xC0190013; +pub const TRANSACTION_NOT_REQUESTED = 0xC0190014; +pub const TRANSACTION_ALREADY_ABORTED = 0xC0190015; +pub const TRANSACTION_ALREADY_COMMITTED = 0xC0190016; +pub const TRANSACTION_INVALID_MARSHALL_BUFFER = 0xC0190017; +pub const CURRENT_TRANSACTION_NOT_VALID = 0xC0190018; +pub const LOG_GROWTH_FAILED = 0xC0190019; +pub const OBJECT_NO_LONGER_EXISTS = 0xC0190021; +pub const STREAM_MINIVERSION_NOT_FOUND = 0xC0190022; +pub const STREAM_MINIVERSION_NOT_VALID = 0xC0190023; +pub const MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION = 0xC0190024; +pub const CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT = 0xC0190025; +pub const CANT_CREATE_MORE_STREAM_MINIVERSIONS = 0xC0190026; +pub const HANDLE_NO_LONGER_VALID = 0xC0190028; +pub const LOG_CORRUPTION_DETECTED = 0xC0190030; +pub const RM_DISCONNECTED = 0xC0190032; +pub const ENLISTMENT_NOT_SUPERIOR = 0xC0190033; +pub const FILE_IDENTITY_NOT_PERSISTENT = 0xC0190036; +pub const CANT_BREAK_TRANSACTIONAL_DEPENDENCY = 0xC0190037; +pub const CANT_CROSS_RM_BOUNDARY = 0xC0190038; +pub const TXF_DIR_NOT_EMPTY = 0xC0190039; +pub const INDOUBT_TRANSACTIONS_EXIST = 0xC019003A; +pub const TM_VOLATILE = 0xC019003B; +pub const ROLLBACK_TIMER_EXPIRED = 0xC019003C; +pub const TXF_ATTRIBUTE_CORRUPT = 0xC019003D; +pub const EFS_NOT_ALLOWED_IN_TRANSACTION = 0xC019003E; +pub const TRANSACTIONAL_OPEN_NOT_ALLOWED = 0xC019003F; +pub const TRANSACTED_MAPPING_UNSUPPORTED_REMOTE = 0xC0190040; +pub const TRANSACTION_REQUIRED_PROMOTION = 0xC0190043; +pub const CANNOT_EXECUTE_FILE_IN_TRANSACTION = 0xC0190044; +pub const TRANSACTIONS_NOT_FROZEN = 0xC0190045; +pub const TRANSACTION_FREEZE_IN_PROGRESS = 0xC0190046; +pub const NOT_SNAPSHOT_VOLUME = 0xC0190047; +pub const NO_SAVEPOINT_WITH_OPEN_FILES = 0xC0190048; +pub const SPARSE_NOT_ALLOWED_IN_TRANSACTION = 0xC0190049; +pub const TM_IDENTITY_MISMATCH = 0xC019004A; +pub const FLOATED_SECTION = 0xC019004B; +pub const CANNOT_ACCEPT_TRANSACTED_WORK = 0xC019004C; +pub const CANNOT_ABORT_TRANSACTIONS = 0xC019004D; +pub const TRANSACTION_NOT_FOUND = 0xC019004E; +pub const RESOURCEMANAGER_NOT_FOUND = 0xC019004F; +pub const ENLISTMENT_NOT_FOUND = 0xC0190050; +pub const TRANSACTIONMANAGER_NOT_FOUND = 0xC0190051; +pub const TRANSACTIONMANAGER_NOT_ONLINE = 0xC0190052; +pub const TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION = 0xC0190053; +pub const TRANSACTION_NOT_ROOT = 0xC0190054; +pub const TRANSACTION_OBJECT_EXPIRED = 0xC0190055; +pub const COMPRESSION_NOT_ALLOWED_IN_TRANSACTION = 0xC0190056; +pub const TRANSACTION_RESPONSE_NOT_ENLISTED = 0xC0190057; +pub const TRANSACTION_RECORD_TOO_LONG = 0xC0190058; +pub const NO_LINK_TRACKING_IN_TRANSACTION = 0xC0190059; +pub const OPERATION_NOT_SUPPORTED_IN_TRANSACTION = 0xC019005A; +pub const TRANSACTION_INTEGRITY_VIOLATED = 0xC019005B; +pub const EXPIRED_HANDLE = 0xC0190060; +pub const TRANSACTION_NOT_ENLISTED = 0xC0190061; +pub const LOG_SECTOR_INVALID = 0xC01A0001; +pub const LOG_SECTOR_PARITY_INVALID = 0xC01A0002; +pub const LOG_SECTOR_REMAPPED = 0xC01A0003; +pub const LOG_BLOCK_INCOMPLETE = 0xC01A0004; +pub const LOG_INVALID_RANGE = 0xC01A0005; +pub const LOG_BLOCKS_EXHAUSTED = 0xC01A0006; +pub const LOG_READ_CONTEXT_INVALID = 0xC01A0007; +pub const LOG_RESTART_INVALID = 0xC01A0008; +pub const LOG_BLOCK_VERSION = 0xC01A0009; +pub const LOG_BLOCK_INVALID = 0xC01A000A; +pub const LOG_READ_MODE_INVALID = 0xC01A000B; +pub const LOG_METADATA_CORRUPT = 0xC01A000D; +pub const LOG_METADATA_INVALID = 0xC01A000E; +pub const LOG_METADATA_INCONSISTENT = 0xC01A000F; +pub const LOG_RESERVATION_INVALID = 0xC01A0010; +pub const LOG_CANT_DELETE = 0xC01A0011; +pub const LOG_CONTAINER_LIMIT_EXCEEDED = 0xC01A0012; +pub const LOG_START_OF_LOG = 0xC01A0013; +pub const LOG_POLICY_ALREADY_INSTALLED = 0xC01A0014; +pub const LOG_POLICY_NOT_INSTALLED = 0xC01A0015; +pub const LOG_POLICY_INVALID = 0xC01A0016; +pub const LOG_POLICY_CONFLICT = 0xC01A0017; +pub const LOG_PINNED_ARCHIVE_TAIL = 0xC01A0018; +pub const LOG_RECORD_NONEXISTENT = 0xC01A0019; +pub const LOG_RECORDS_RESERVED_INVALID = 0xC01A001A; +pub const LOG_SPACE_RESERVED_INVALID = 0xC01A001B; +pub const LOG_TAIL_INVALID = 0xC01A001C; +pub const LOG_FULL = 0xC01A001D; +pub const LOG_MULTIPLEXED = 0xC01A001E; +pub const LOG_DEDICATED = 0xC01A001F; +pub const LOG_ARCHIVE_NOT_IN_PROGRESS = 0xC01A0020; +pub const LOG_ARCHIVE_IN_PROGRESS = 0xC01A0021; +pub const LOG_EPHEMERAL = 0xC01A0022; +pub const LOG_NOT_ENOUGH_CONTAINERS = 0xC01A0023; +pub const LOG_CLIENT_ALREADY_REGISTERED = 0xC01A0024; +pub const LOG_CLIENT_NOT_REGISTERED = 0xC01A0025; +pub const LOG_FULL_HANDLER_IN_PROGRESS = 0xC01A0026; +pub const LOG_CONTAINER_READ_FAILED = 0xC01A0027; +pub const LOG_CONTAINER_WRITE_FAILED = 0xC01A0028; +pub const LOG_CONTAINER_OPEN_FAILED = 0xC01A0029; +pub const LOG_CONTAINER_STATE_INVALID = 0xC01A002A; +pub const LOG_STATE_INVALID = 0xC01A002B; +pub const LOG_PINNED = 0xC01A002C; +pub const LOG_METADATA_FLUSH_FAILED = 0xC01A002D; +pub const LOG_INCONSISTENT_SECURITY = 0xC01A002E; +pub const LOG_APPENDED_FLUSH_FAILED = 0xC01A002F; +pub const LOG_PINNED_RESERVATION = 0xC01A0030; +pub const VIDEO_HUNG_DISPLAY_DRIVER_THREAD = 0xC01B00EA; +pub const FLT_NO_HANDLER_DEFINED = 0xC01C0001; +pub const FLT_CONTEXT_ALREADY_DEFINED = 0xC01C0002; +pub const FLT_INVALID_ASYNCHRONOUS_REQUEST = 0xC01C0003; +pub const FLT_DISALLOW_FAST_IO = 0xC01C0004; +pub const FLT_INVALID_NAME_REQUEST = 0xC01C0005; +pub const FLT_NOT_SAFE_TO_POST_OPERATION = 0xC01C0006; +pub const FLT_NOT_INITIALIZED = 0xC01C0007; +pub const FLT_FILTER_NOT_READY = 0xC01C0008; +pub const FLT_POST_OPERATION_CLEANUP = 0xC01C0009; +pub const FLT_INTERNAL_ERROR = 0xC01C000A; +pub const FLT_DELETING_OBJECT = 0xC01C000B; +pub const FLT_MUST_BE_NONPAGED_POOL = 0xC01C000C; +pub const FLT_DUPLICATE_ENTRY = 0xC01C000D; +pub const FLT_CBDQ_DISABLED = 0xC01C000E; +pub const FLT_DO_NOT_ATTACH = 0xC01C000F; +pub const FLT_DO_NOT_DETACH = 0xC01C0010; +pub const FLT_INSTANCE_ALTITUDE_COLLISION = 0xC01C0011; +pub const FLT_INSTANCE_NAME_COLLISION = 0xC01C0012; +pub const FLT_FILTER_NOT_FOUND = 0xC01C0013; +pub const FLT_VOLUME_NOT_FOUND = 0xC01C0014; +pub const FLT_INSTANCE_NOT_FOUND = 0xC01C0015; +pub const FLT_CONTEXT_ALLOCATION_NOT_FOUND = 0xC01C0016; +pub const FLT_INVALID_CONTEXT_REGISTRATION = 0xC01C0017; +pub const FLT_NAME_CACHE_MISS = 0xC01C0018; +pub const FLT_NO_DEVICE_OBJECT = 0xC01C0019; +pub const FLT_VOLUME_ALREADY_MOUNTED = 0xC01C001A; +pub const FLT_ALREADY_ENLISTED = 0xC01C001B; +pub const FLT_CONTEXT_ALREADY_LINKED = 0xC01C001C; +pub const FLT_NO_WAITER_FOR_REPLY = 0xC01C0020; +pub const MONITOR_NO_DESCRIPTOR = 0xC01D0001; +pub const MONITOR_UNKNOWN_DESCRIPTOR_FORMAT = 0xC01D0002; +pub const MONITOR_INVALID_DESCRIPTOR_CHECKSUM = 0xC01D0003; +pub const MONITOR_INVALID_STANDARD_TIMING_BLOCK = 0xC01D0004; +pub const MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED = 0xC01D0005; +pub const MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK = 0xC01D0006; +pub const MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK = 0xC01D0007; +pub const MONITOR_NO_MORE_DESCRIPTOR_DATA = 0xC01D0008; +pub const MONITOR_INVALID_DETAILED_TIMING_BLOCK = 0xC01D0009; +pub const MONITOR_INVALID_MANUFACTURE_DATE = 0xC01D000A; +pub const GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER = 0xC01E0000; +pub const GRAPHICS_INSUFFICIENT_DMA_BUFFER = 0xC01E0001; +pub const GRAPHICS_INVALID_DISPLAY_ADAPTER = 0xC01E0002; +pub const GRAPHICS_ADAPTER_WAS_RESET = 0xC01E0003; +pub const GRAPHICS_INVALID_DRIVER_MODEL = 0xC01E0004; +pub const GRAPHICS_PRESENT_MODE_CHANGED = 0xC01E0005; +pub const GRAPHICS_PRESENT_OCCLUDED = 0xC01E0006; +pub const GRAPHICS_PRESENT_DENIED = 0xC01E0007; +pub const GRAPHICS_CANNOTCOLORCONVERT = 0xC01E0008; +pub const GRAPHICS_PRESENT_REDIRECTION_DISABLED = 0xC01E000B; +pub const GRAPHICS_PRESENT_UNOCCLUDED = 0xC01E000C; +pub const GRAPHICS_NO_VIDEO_MEMORY = 0xC01E0100; +pub const GRAPHICS_CANT_LOCK_MEMORY = 0xC01E0101; +pub const GRAPHICS_ALLOCATION_BUSY = 0xC01E0102; +pub const GRAPHICS_TOO_MANY_REFERENCES = 0xC01E0103; +pub const GRAPHICS_TRY_AGAIN_LATER = 0xC01E0104; +pub const GRAPHICS_TRY_AGAIN_NOW = 0xC01E0105; +pub const GRAPHICS_ALLOCATION_INVALID = 0xC01E0106; +pub const GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE = 0xC01E0107; +pub const GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED = 0xC01E0108; +pub const GRAPHICS_CANT_EVICT_PINNED_ALLOCATION = 0xC01E0109; +pub const GRAPHICS_INVALID_ALLOCATION_USAGE = 0xC01E0110; +pub const GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION = 0xC01E0111; +pub const GRAPHICS_ALLOCATION_CLOSED = 0xC01E0112; +pub const GRAPHICS_INVALID_ALLOCATION_INSTANCE = 0xC01E0113; +pub const GRAPHICS_INVALID_ALLOCATION_HANDLE = 0xC01E0114; +pub const GRAPHICS_WRONG_ALLOCATION_DEVICE = 0xC01E0115; +pub const GRAPHICS_ALLOCATION_CONTENT_LOST = 0xC01E0116; +pub const GRAPHICS_GPU_EXCEPTION_ON_DEVICE = 0xC01E0200; +pub const GRAPHICS_INVALID_VIDPN_TOPOLOGY = 0xC01E0300; +pub const GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED = 0xC01E0301; +pub const GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED = 0xC01E0302; +pub const GRAPHICS_INVALID_VIDPN = 0xC01E0303; +pub const GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE = 0xC01E0304; +pub const GRAPHICS_INVALID_VIDEO_PRESENT_TARGET = 0xC01E0305; +pub const GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED = 0xC01E0306; +pub const GRAPHICS_INVALID_VIDPN_SOURCEMODESET = 0xC01E0308; +pub const GRAPHICS_INVALID_VIDPN_TARGETMODESET = 0xC01E0309; +pub const GRAPHICS_INVALID_FREQUENCY = 0xC01E030A; +pub const GRAPHICS_INVALID_ACTIVE_REGION = 0xC01E030B; +pub const GRAPHICS_INVALID_TOTAL_REGION = 0xC01E030C; +pub const GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE = 0xC01E0310; +pub const GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE = 0xC01E0311; +pub const GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET = 0xC01E0312; +pub const GRAPHICS_PATH_ALREADY_IN_TOPOLOGY = 0xC01E0313; +pub const GRAPHICS_MODE_ALREADY_IN_MODESET = 0xC01E0314; +pub const GRAPHICS_INVALID_VIDEOPRESENTSOURCESET = 0xC01E0315; +pub const GRAPHICS_INVALID_VIDEOPRESENTTARGETSET = 0xC01E0316; +pub const GRAPHICS_SOURCE_ALREADY_IN_SET = 0xC01E0317; +pub const GRAPHICS_TARGET_ALREADY_IN_SET = 0xC01E0318; +pub const GRAPHICS_INVALID_VIDPN_PRESENT_PATH = 0xC01E0319; +pub const GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY = 0xC01E031A; +pub const GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET = 0xC01E031B; +pub const GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE = 0xC01E031C; +pub const GRAPHICS_FREQUENCYRANGE_NOT_IN_SET = 0xC01E031D; +pub const GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET = 0xC01E031F; +pub const GRAPHICS_STALE_MODESET = 0xC01E0320; +pub const GRAPHICS_INVALID_MONITOR_SOURCEMODESET = 0xC01E0321; +pub const GRAPHICS_INVALID_MONITOR_SOURCE_MODE = 0xC01E0322; +pub const GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN = 0xC01E0323; +pub const GRAPHICS_MODE_ID_MUST_BE_UNIQUE = 0xC01E0324; +pub const GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION = 0xC01E0325; +pub const GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES = 0xC01E0326; +pub const GRAPHICS_PATH_NOT_IN_TOPOLOGY = 0xC01E0327; +pub const GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE = 0xC01E0328; +pub const GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET = 0xC01E0329; +pub const GRAPHICS_INVALID_MONITORDESCRIPTORSET = 0xC01E032A; +pub const GRAPHICS_INVALID_MONITORDESCRIPTOR = 0xC01E032B; +pub const GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET = 0xC01E032C; +pub const GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET = 0xC01E032D; +pub const GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE = 0xC01E032E; +pub const GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE = 0xC01E032F; +pub const GRAPHICS_RESOURCES_NOT_RELATED = 0xC01E0330; +pub const GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE = 0xC01E0331; +pub const GRAPHICS_TARGET_ID_MUST_BE_UNIQUE = 0xC01E0332; +pub const GRAPHICS_NO_AVAILABLE_VIDPN_TARGET = 0xC01E0333; +pub const GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER = 0xC01E0334; +pub const GRAPHICS_NO_VIDPNMGR = 0xC01E0335; +pub const GRAPHICS_NO_ACTIVE_VIDPN = 0xC01E0336; +pub const GRAPHICS_STALE_VIDPN_TOPOLOGY = 0xC01E0337; +pub const GRAPHICS_MONITOR_NOT_CONNECTED = 0xC01E0338; +pub const GRAPHICS_SOURCE_NOT_IN_TOPOLOGY = 0xC01E0339; +pub const GRAPHICS_INVALID_PRIMARYSURFACE_SIZE = 0xC01E033A; +pub const GRAPHICS_INVALID_VISIBLEREGION_SIZE = 0xC01E033B; +pub const GRAPHICS_INVALID_STRIDE = 0xC01E033C; +pub const GRAPHICS_INVALID_PIXELFORMAT = 0xC01E033D; +pub const GRAPHICS_INVALID_COLORBASIS = 0xC01E033E; +pub const GRAPHICS_INVALID_PIXELVALUEACCESSMODE = 0xC01E033F; +pub const GRAPHICS_TARGET_NOT_IN_TOPOLOGY = 0xC01E0340; +pub const GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT = 0xC01E0341; +pub const GRAPHICS_VIDPN_SOURCE_IN_USE = 0xC01E0342; +pub const GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN = 0xC01E0343; +pub const GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL = 0xC01E0344; +pub const GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION = 0xC01E0345; +pub const GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED = 0xC01E0346; +pub const GRAPHICS_INVALID_GAMMA_RAMP = 0xC01E0347; +pub const GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED = 0xC01E0348; +pub const GRAPHICS_MULTISAMPLING_NOT_SUPPORTED = 0xC01E0349; +pub const GRAPHICS_MODE_NOT_IN_MODESET = 0xC01E034A; +pub const GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON = 0xC01E034D; +pub const GRAPHICS_INVALID_PATH_CONTENT_TYPE = 0xC01E034E; +pub const GRAPHICS_INVALID_COPYPROTECTION_TYPE = 0xC01E034F; +pub const GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS = 0xC01E0350; +pub const GRAPHICS_INVALID_SCANLINE_ORDERING = 0xC01E0352; +pub const GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED = 0xC01E0353; +pub const GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS = 0xC01E0354; +pub const GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT = 0xC01E0355; +pub const GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM = 0xC01E0356; +pub const GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN = 0xC01E0357; +pub const GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT = 0xC01E0358; +pub const GRAPHICS_MAX_NUM_PATHS_REACHED = 0xC01E0359; +pub const GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION = 0xC01E035A; +pub const GRAPHICS_INVALID_CLIENT_TYPE = 0xC01E035B; +pub const GRAPHICS_CLIENTVIDPN_NOT_SET = 0xC01E035C; +pub const GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED = 0xC01E0400; +pub const GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED = 0xC01E0401; +pub const GRAPHICS_NOT_A_LINKED_ADAPTER = 0xC01E0430; +pub const GRAPHICS_LEADLINK_NOT_ENUMERATED = 0xC01E0431; +pub const GRAPHICS_CHAINLINKS_NOT_ENUMERATED = 0xC01E0432; +pub const GRAPHICS_ADAPTER_CHAIN_NOT_READY = 0xC01E0433; +pub const GRAPHICS_CHAINLINKS_NOT_STARTED = 0xC01E0434; +pub const GRAPHICS_CHAINLINKS_NOT_POWERED_ON = 0xC01E0435; +pub const GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE = 0xC01E0436; +pub const GRAPHICS_NOT_POST_DEVICE_DRIVER = 0xC01E0438; +pub const GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED = 0xC01E043B; +pub const GRAPHICS_OPM_NOT_SUPPORTED = 0xC01E0500; +pub const GRAPHICS_COPP_NOT_SUPPORTED = 0xC01E0501; +pub const GRAPHICS_UAB_NOT_SUPPORTED = 0xC01E0502; +pub const GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS = 0xC01E0503; +pub const GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL = 0xC01E0504; +pub const GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST = 0xC01E0505; +pub const GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME = 0xC01E0506; +pub const GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP = 0xC01E0507; +pub const GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED = 0xC01E0508; +pub const GRAPHICS_OPM_INVALID_POINTER = 0xC01E050A; +pub const GRAPHICS_OPM_INTERNAL_ERROR = 0xC01E050B; +pub const GRAPHICS_OPM_INVALID_HANDLE = 0xC01E050C; +pub const GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE = 0xC01E050D; +pub const GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH = 0xC01E050E; +pub const GRAPHICS_OPM_SPANNING_MODE_ENABLED = 0xC01E050F; +pub const GRAPHICS_OPM_THEATER_MODE_ENABLED = 0xC01E0510; +pub const GRAPHICS_PVP_HFS_FAILED = 0xC01E0511; +pub const GRAPHICS_OPM_INVALID_SRM = 0xC01E0512; +pub const GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP = 0xC01E0513; +pub const GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP = 0xC01E0514; +pub const GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA = 0xC01E0515; +pub const GRAPHICS_OPM_HDCP_SRM_NEVER_SET = 0xC01E0516; +pub const GRAPHICS_OPM_RESOLUTION_TOO_HIGH = 0xC01E0517; +pub const GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE = 0xC01E0518; +pub const GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS = 0xC01E051A; +pub const GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS = 0xC01E051B; +pub const GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS = 0xC01E051C; +pub const GRAPHICS_OPM_INVALID_INFORMATION_REQUEST = 0xC01E051D; +pub const GRAPHICS_OPM_DRIVER_INTERNAL_ERROR = 0xC01E051E; +pub const GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS = 0xC01E051F; +pub const GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED = 0xC01E0520; +pub const GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST = 0xC01E0521; +pub const GRAPHICS_I2C_NOT_SUPPORTED = 0xC01E0580; +pub const GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST = 0xC01E0581; +pub const GRAPHICS_I2C_ERROR_TRANSMITTING_DATA = 0xC01E0582; +pub const GRAPHICS_I2C_ERROR_RECEIVING_DATA = 0xC01E0583; +pub const GRAPHICS_DDCCI_VCP_NOT_SUPPORTED = 0xC01E0584; +pub const GRAPHICS_DDCCI_INVALID_DATA = 0xC01E0585; +pub const GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE = 0xC01E0586; +pub const GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING = 0xC01E0587; +pub const GRAPHICS_MCA_INTERNAL_ERROR = 0xC01E0588; +pub const GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND = 0xC01E0589; +pub const GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH = 0xC01E058A; +pub const GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM = 0xC01E058B; +pub const GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE = 0xC01E058C; +pub const GRAPHICS_MONITOR_NO_LONGER_EXISTS = 0xC01E058D; +pub const GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED = 0xC01E05E0; +pub const GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME = 0xC01E05E1; +pub const GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP = 0xC01E05E2; +pub const GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED = 0xC01E05E3; +pub const GRAPHICS_INVALID_POINTER = 0xC01E05E4; +pub const GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE = 0xC01E05E5; +pub const GRAPHICS_PARAMETER_ARRAY_TOO_SMALL = 0xC01E05E6; +pub const GRAPHICS_INTERNAL_ERROR = 0xC01E05E7; +pub const GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS = 0xC01E05E8; +pub const FVE_LOCKED_VOLUME = 0xC0210000; +pub const FVE_NOT_ENCRYPTED = 0xC0210001; +pub const FVE_BAD_INFORMATION = 0xC0210002; +pub const FVE_TOO_SMALL = 0xC0210003; +pub const FVE_FAILED_WRONG_FS = 0xC0210004; +pub const FVE_FAILED_BAD_FS = 0xC0210005; +pub const FVE_FS_NOT_EXTENDED = 0xC0210006; +pub const FVE_FS_MOUNTED = 0xC0210007; +pub const FVE_NO_LICENSE = 0xC0210008; +pub const FVE_ACTION_NOT_ALLOWED = 0xC0210009; +pub const FVE_BAD_DATA = 0xC021000A; +pub const FVE_VOLUME_NOT_BOUND = 0xC021000B; +pub const FVE_NOT_DATA_VOLUME = 0xC021000C; +pub const FVE_CONV_READ_ERROR = 0xC021000D; +pub const FVE_CONV_WRITE_ERROR = 0xC021000E; +pub const FVE_OVERLAPPED_UPDATE = 0xC021000F; +pub const FVE_FAILED_SECTOR_SIZE = 0xC0210010; +pub const FVE_FAILED_AUTHENTICATION = 0xC0210011; +pub const FVE_NOT_OS_VOLUME = 0xC0210012; +pub const FVE_KEYFILE_NOT_FOUND = 0xC0210013; +pub const FVE_KEYFILE_INVALID = 0xC0210014; +pub const FVE_KEYFILE_NO_VMK = 0xC0210015; +pub const FVE_TPM_DISABLED = 0xC0210016; +pub const FVE_TPM_SRK_AUTH_NOT_ZERO = 0xC0210017; +pub const FVE_TPM_INVALID_PCR = 0xC0210018; +pub const FVE_TPM_NO_VMK = 0xC0210019; +pub const FVE_PIN_INVALID = 0xC021001A; +pub const FVE_AUTH_INVALID_APPLICATION = 0xC021001B; +pub const FVE_AUTH_INVALID_CONFIG = 0xC021001C; +pub const FVE_DEBUGGER_ENABLED = 0xC021001D; +pub const FVE_DRY_RUN_FAILED = 0xC021001E; +pub const FVE_BAD_METADATA_POINTER = 0xC021001F; +pub const FVE_OLD_METADATA_COPY = 0xC0210020; +pub const FVE_REBOOT_REQUIRED = 0xC0210021; +pub const FVE_RAW_ACCESS = 0xC0210022; +pub const FVE_RAW_BLOCKED = 0xC0210023; +pub const FVE_NO_FEATURE_LICENSE = 0xC0210026; +pub const FVE_POLICY_USER_DISABLE_RDV_NOT_ALLOWED = 0xC0210027; +pub const FVE_CONV_RECOVERY_FAILED = 0xC0210028; +pub const FVE_VIRTUALIZED_SPACE_TOO_BIG = 0xC0210029; +pub const FVE_VOLUME_TOO_SMALL = 0xC0210030; +pub const FWP_CALLOUT_NOT_FOUND = 0xC0220001; +pub const FWP_CONDITION_NOT_FOUND = 0xC0220002; +pub const FWP_FILTER_NOT_FOUND = 0xC0220003; +pub const FWP_LAYER_NOT_FOUND = 0xC0220004; +pub const FWP_PROVIDER_NOT_FOUND = 0xC0220005; +pub const FWP_PROVIDER_CONTEXT_NOT_FOUND = 0xC0220006; +pub const FWP_SUBLAYER_NOT_FOUND = 0xC0220007; +pub const FWP_NOT_FOUND = 0xC0220008; +pub const FWP_ALREADY_EXISTS = 0xC0220009; +pub const FWP_IN_USE = 0xC022000A; +pub const FWP_DYNAMIC_SESSION_IN_PROGRESS = 0xC022000B; +pub const FWP_WRONG_SESSION = 0xC022000C; +pub const FWP_NO_TXN_IN_PROGRESS = 0xC022000D; +pub const FWP_TXN_IN_PROGRESS = 0xC022000E; +pub const FWP_TXN_ABORTED = 0xC022000F; +pub const FWP_SESSION_ABORTED = 0xC0220010; +pub const FWP_INCOMPATIBLE_TXN = 0xC0220011; +pub const FWP_TIMEOUT = 0xC0220012; +pub const FWP_NET_EVENTS_DISABLED = 0xC0220013; +pub const FWP_INCOMPATIBLE_LAYER = 0xC0220014; +pub const FWP_KM_CLIENTS_ONLY = 0xC0220015; +pub const FWP_LIFETIME_MISMATCH = 0xC0220016; +pub const FWP_BUILTIN_OBJECT = 0xC0220017; +pub const FWP_TOO_MANY_BOOTTIME_FILTERS = 0xC0220018; +pub const FWP_TOO_MANY_CALLOUTS = 0xC0220018; +pub const FWP_NOTIFICATION_DROPPED = 0xC0220019; +pub const FWP_TRAFFIC_MISMATCH = 0xC022001A; +pub const FWP_INCOMPATIBLE_SA_STATE = 0xC022001B; +pub const FWP_NULL_POINTER = 0xC022001C; +pub const FWP_INVALID_ENUMERATOR = 0xC022001D; +pub const FWP_INVALID_FLAGS = 0xC022001E; +pub const FWP_INVALID_NET_MASK = 0xC022001F; +pub const FWP_INVALID_RANGE = 0xC0220020; +pub const FWP_INVALID_INTERVAL = 0xC0220021; +pub const FWP_ZERO_LENGTH_ARRAY = 0xC0220022; +pub const FWP_NULL_DISPLAY_NAME = 0xC0220023; +pub const FWP_INVALID_ACTION_TYPE = 0xC0220024; +pub const FWP_INVALID_WEIGHT = 0xC0220025; +pub const FWP_MATCH_TYPE_MISMATCH = 0xC0220026; +pub const FWP_TYPE_MISMATCH = 0xC0220027; +pub const FWP_OUT_OF_BOUNDS = 0xC0220028; +pub const FWP_RESERVED = 0xC0220029; +pub const FWP_DUPLICATE_CONDITION = 0xC022002A; +pub const FWP_DUPLICATE_KEYMOD = 0xC022002B; +pub const FWP_ACTION_INCOMPATIBLE_WITH_LAYER = 0xC022002C; +pub const FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER = 0xC022002D; +pub const FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER = 0xC022002E; +pub const FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT = 0xC022002F; +pub const FWP_INCOMPATIBLE_AUTH_METHOD = 0xC0220030; +pub const FWP_INCOMPATIBLE_DH_GROUP = 0xC0220031; +pub const FWP_EM_NOT_SUPPORTED = 0xC0220032; +pub const FWP_NEVER_MATCH = 0xC0220033; +pub const FWP_PROVIDER_CONTEXT_MISMATCH = 0xC0220034; +pub const FWP_INVALID_PARAMETER = 0xC0220035; +pub const FWP_TOO_MANY_SUBLAYERS = 0xC0220036; +pub const FWP_CALLOUT_NOTIFICATION_FAILED = 0xC0220037; +pub const FWP_INCOMPATIBLE_AUTH_CONFIG = 0xC0220038; +pub const FWP_INCOMPATIBLE_CIPHER_CONFIG = 0xC0220039; +pub const FWP_DUPLICATE_AUTH_METHOD = 0xC022003C; +pub const FWP_TCPIP_NOT_READY = 0xC0220100; +pub const FWP_INJECT_HANDLE_CLOSING = 0xC0220101; +pub const FWP_INJECT_HANDLE_STALE = 0xC0220102; +pub const FWP_CANNOT_PEND = 0xC0220103; +pub const NDIS_CLOSING = 0xC0230002; +pub const NDIS_BAD_VERSION = 0xC0230004; +pub const NDIS_BAD_CHARACTERISTICS = 0xC0230005; +pub const NDIS_ADAPTER_NOT_FOUND = 0xC0230006; +pub const NDIS_OPEN_FAILED = 0xC0230007; +pub const NDIS_DEVICE_FAILED = 0xC0230008; +pub const NDIS_MULTICAST_FULL = 0xC0230009; +pub const NDIS_MULTICAST_EXISTS = 0xC023000A; +pub const NDIS_MULTICAST_NOT_FOUND = 0xC023000B; +pub const NDIS_REQUEST_ABORTED = 0xC023000C; +pub const NDIS_RESET_IN_PROGRESS = 0xC023000D; +pub const NDIS_INVALID_PACKET = 0xC023000F; +pub const NDIS_INVALID_DEVICE_REQUEST = 0xC0230010; +pub const NDIS_ADAPTER_NOT_READY = 0xC0230011; +pub const NDIS_INVALID_LENGTH = 0xC0230014; +pub const NDIS_INVALID_DATA = 0xC0230015; +pub const NDIS_BUFFER_TOO_SHORT = 0xC0230016; +pub const NDIS_INVALID_OID = 0xC0230017; +pub const NDIS_ADAPTER_REMOVED = 0xC0230018; +pub const NDIS_UNSUPPORTED_MEDIA = 0xC0230019; +pub const NDIS_GROUP_ADDRESS_IN_USE = 0xC023001A; +pub const NDIS_FILE_NOT_FOUND = 0xC023001B; +pub const NDIS_ERROR_READING_FILE = 0xC023001C; +pub const NDIS_ALREADY_MAPPED = 0xC023001D; +pub const NDIS_RESOURCE_CONFLICT = 0xC023001E; +pub const NDIS_MEDIA_DISCONNECTED = 0xC023001F; +pub const NDIS_INVALID_ADDRESS = 0xC0230022; +pub const NDIS_PAUSED = 0xC023002A; +pub const NDIS_INTERFACE_NOT_FOUND = 0xC023002B; +pub const NDIS_UNSUPPORTED_REVISION = 0xC023002C; +pub const NDIS_INVALID_PORT = 0xC023002D; +pub const NDIS_INVALID_PORT_STATE = 0xC023002E; +pub const NDIS_LOW_POWER_STATE = 0xC023002F; +pub const NDIS_NOT_SUPPORTED = 0xC02300BB; +pub const NDIS_OFFLOAD_POLICY = 0xC023100F; +pub const NDIS_OFFLOAD_CONNECTION_REJECTED = 0xC0231012; +pub const NDIS_OFFLOAD_PATH_REJECTED = 0xC0231013; +pub const NDIS_DOT11_AUTO_CONFIG_ENABLED = 0xC0232000; +pub const NDIS_DOT11_MEDIA_IN_USE = 0xC0232001; +pub const NDIS_DOT11_POWER_STATE_INVALID = 0xC0232002; +pub const NDIS_PM_WOL_PATTERN_LIST_FULL = 0xC0232003; +pub const NDIS_PM_PROTOCOL_OFFLOAD_LIST_FULL = 0xC0232004; +pub const IPSEC_BAD_SPI = 0xC0360001; +pub const IPSEC_SA_LIFETIME_EXPIRED = 0xC0360002; +pub const IPSEC_WRONG_SA = 0xC0360003; +pub const IPSEC_REPLAY_CHECK_FAILED = 0xC0360004; +pub const IPSEC_INVALID_PACKET = 0xC0360005; +pub const IPSEC_INTEGRITY_CHECK_FAILED = 0xC0360006; +pub const IPSEC_CLEAR_TEXT_DROP = 0xC0360007; +pub const IPSEC_AUTH_FIREWALL_DROP = 0xC0360008; +pub const IPSEC_THROTTLE_DROP = 0xC0360009; +pub const IPSEC_DOSP_BLOCK = 0xC0368000; +pub const IPSEC_DOSP_RECEIVED_MULTICAST = 0xC0368001; +pub const IPSEC_DOSP_INVALID_PACKET = 0xC0368002; +pub const IPSEC_DOSP_STATE_LOOKUP_FAILED = 0xC0368003; +pub const IPSEC_DOSP_MAX_ENTRIES = 0xC0368004; +pub const IPSEC_DOSP_KEYMOD_NOT_ALLOWED = 0xC0368005; +pub const IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES = 0xC0368006; +pub const VOLMGR_MIRROR_NOT_SUPPORTED = 0xC038005B; +pub const VOLMGR_RAID5_NOT_SUPPORTED = 0xC038005C; +pub const VIRTDISK_PROVIDER_NOT_FOUND = 0xC03A0014; +pub const VIRTDISK_NOT_VIRTUAL_DISK = 0xC03A0015; +pub const VHD_PARENT_VHD_ACCESS_DENIED = 0xC03A0016; +pub const VHD_CHILD_PARENT_SIZE_MISMATCH = 0xC03A0017; +pub const VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED = 0xC03A0018; +pub const VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT = 0xC03A0019; diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig new file mode 100644 index 000000000..70cb62ac5 --- /dev/null +++ b/lib/std/os/windows/ws2_32.zig @@ -0,0 +1,401 @@ +usingnamespace @import("bits.zig"); + +pub const SOCKET = *@OpaqueType(); +pub const INVALID_SOCKET = @intToPtr(SOCKET, ~@as(usize, 0)); +pub const SOCKET_ERROR = -1; + +pub const WSADESCRIPTION_LEN = 256; +pub const WSASYS_STATUS_LEN = 128; + +pub const WSADATA = if (usize.bit_count == u64.bit_count) + extern struct { + wVersion: WORD, + wHighVersion: WORD, + iMaxSockets: u16, + iMaxUdpDg: u16, + lpVendorInfo: *u8, + szDescription: [WSADESCRIPTION_LEN + 1]u8, + szSystemStatus: [WSASYS_STATUS_LEN + 1]u8, + } +else + extern struct { + wVersion: WORD, + wHighVersion: WORD, + szDescription: [WSADESCRIPTION_LEN + 1]u8, + szSystemStatus: [WSASYS_STATUS_LEN + 1]u8, + iMaxSockets: u16, + iMaxUdpDg: u16, + lpVendorInfo: *u8, + }; + +pub const MAX_PROTOCOL_CHAIN = 7; + +pub const WSAPROTOCOLCHAIN = extern struct { + ChainLen: c_int, + ChainEntries: [MAX_PROTOCOL_CHAIN]DWORD, +}; + +pub const WSAPROTOCOL_LEN = 255; + +pub const WSAPROTOCOL_INFOA = extern struct { + dwServiceFlags1: DWORD, + dwServiceFlags2: DWORD, + dwServiceFlags3: DWORD, + dwServiceFlags4: DWORD, + dwProviderFlags: DWORD, + ProviderId: GUID, + dwCatalogEntryId: DWORD, + ProtocolChain: WSAPROTOCOLCHAIN, + iVersion: c_int, + iAddressFamily: c_int, + iMaxSockAddr: c_int, + iMinSockAddr: c_int, + iSocketType: c_int, + iProtocol: c_int, + iProtocolMaxOffset: c_int, + iNetworkByteOrder: c_int, + iSecurityScheme: c_int, + dwMessageSize: DWORD, + dwProviderReserved: DWORD, + szProtocol: [WSAPROTOCOL_LEN + 1]CHAR, +}; + +pub const WSAPROTOCOL_INFOW = extern struct { + dwServiceFlags1: DWORD, + dwServiceFlags2: DWORD, + dwServiceFlags3: DWORD, + dwServiceFlags4: DWORD, + dwProviderFlags: DWORD, + ProviderId: GUID, + dwCatalogEntryId: DWORD, + ProtocolChain: WSAPROTOCOLCHAIN, + iVersion: c_int, + iAddressFamily: c_int, + iMaxSockAddr: c_int, + iMinSockAddr: c_int, + iSocketType: c_int, + iProtocol: c_int, + iProtocolMaxOffset: c_int, + iNetworkByteOrder: c_int, + iSecurityScheme: c_int, + dwMessageSize: DWORD, + dwProviderReserved: DWORD, + szProtocol: [WSAPROTOCOL_LEN + 1]WCHAR, +}; + +pub const GROUP = u32; + +pub const SG_UNCONSTRAINED_GROUP = 0x1; +pub const SG_CONSTRAINED_GROUP = 0x2; + +pub const WSA_FLAG_OVERLAPPED = 0x01; +pub const WSA_FLAG_MULTIPOINT_C_ROOT = 0x02; +pub const WSA_FLAG_MULTIPOINT_C_LEAF = 0x04; +pub const WSA_FLAG_MULTIPOINT_D_ROOT = 0x08; +pub const WSA_FLAG_MULTIPOINT_D_LEAF = 0x10; +pub const WSA_FLAG_ACCESS_SYSTEM_SECURITY = 0x40; +pub const WSA_FLAG_NO_HANDLE_INHERIT = 0x80; + +pub const WSAEVENT = HANDLE; + +pub const WSAOVERLAPPED = extern struct { + Internal: DWORD, + InternalHigh: DWORD, + Offset: DWORD, + OffsetHigh: DWORD, + hEvent: ?WSAEVENT, +}; + +pub const WSAOVERLAPPED_COMPLETION_ROUTINE = extern fn (dwError: DWORD, cbTransferred: DWORD, lpOverlapped: *WSAOVERLAPPED, dwFlags: DWORD) void; + +pub const ADDRESS_FAMILY = u16; + +// Microsoft use the signed c_int for this, but it should never be negative +const socklen_t = u32; + +pub const AF_UNSPEC = 0; +pub const AF_UNIX = 1; +pub const AF_INET = 2; +pub const AF_IMPLINK = 3; +pub const AF_PUP = 4; +pub const AF_CHAOS = 5; +pub const AF_NS = 6; +pub const AF_IPX = AF_NS; +pub const AF_ISO = 7; +pub const AF_OSI = AF_ISO; +pub const AF_ECMA = 8; +pub const AF_DATAKIT = 9; +pub const AF_CCITT = 10; +pub const AF_SNA = 11; +pub const AF_DECnet = 12; +pub const AF_DLI = 13; +pub const AF_LAT = 14; +pub const AF_HYLINK = 15; +pub const AF_APPLETALK = 16; +pub const AF_NETBIOS = 17; +pub const AF_VOICEVIEW = 18; +pub const AF_FIREFOX = 19; +pub const AF_UNKNOWN1 = 20; +pub const AF_BAN = 21; +pub const AF_ATM = 22; +pub const AF_INET6 = 23; +pub const AF_CLUSTER = 24; +pub const AF_12844 = 25; +pub const AF_IRDA = 26; +pub const AF_NETDES = 28; +pub const AF_TCNPROCESS = 29; +pub const AF_TCNMESSAGE = 30; +pub const AF_ICLFXBM = 31; +pub const AF_BTH = 32; +pub const AF_MAX = 33; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const IPPROTO_ICMP = 1; +pub const IPPROTO_IGMP = 2; +pub const BTHPROTO_RFCOMM = 3; +pub const IPPROTO_TCP = 6; +pub const IPPROTO_UDP = 17; +pub const IPPROTO_ICMPV6 = 58; +pub const IPPROTO_RM = 113; + +pub const sockaddr = extern struct { + family: ADDRESS_FAMILY, + data: [14]u8, +}; + +/// IPv4 socket address +pub const sockaddr_in = extern struct { + family: ADDRESS_FAMILY = AF_INET, + port: USHORT, + addr: u32, + zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/// IPv6 socket address +pub const sockaddr_in6 = extern struct { + family: ADDRESS_FAMILY = AF_INET6, + port: USHORT, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +/// UNIX domain socket address +pub const sockaddr_un = extern struct { + family: ADDRESS_FAMILY = AF_UNIX, + path: [108]u8, +}; + +pub const WSABUF = extern struct { + len: ULONG, + buf: [*]u8, +}; + +pub const WSAMSG = extern struct { + name: *const sockaddr, + namelen: INT, + lpBuffers: [*]WSABUF, + dwBufferCount: DWORD, + Control: WSABUF, + dwFlags: DWORD, +}; + +pub const WSA_INVALID_HANDLE = 6; +pub const WSA_NOT_ENOUGH_MEMORY = 8; +pub const WSA_INVALID_PARAMETER = 87; +pub const WSA_OPERATION_ABORTED = 995; +pub const WSA_IO_INCOMPLETE = 996; +pub const WSA_IO_PENDING = 997; +pub const WSAEINTR = 10004; +pub const WSAEBADF = 10009; +pub const WSAEACCES = 10013; +pub const WSAEFAULT = 10014; +pub const WSAEINVAL = 10022; +pub const WSAEMFILE = 10024; +pub const WSAEWOULDBLOCK = 10035; +pub const WSAEINPROGRESS = 10036; +pub const WSAEALREADY = 10037; +pub const WSAENOTSOCK = 10038; +pub const WSAEDESTADDRREQ = 10039; +pub const WSAEMSGSIZE = 10040; +pub const WSAEPROTOTYPE = 10041; +pub const WSAENOPROTOOPT = 10042; +pub const WSAEPROTONOSUPPORT = 10043; +pub const WSAESOCKTNOSUPPORT = 10044; +pub const WSAEOPNOTSUPP = 10045; +pub const WSAEPFNOSUPPORT = 10046; +pub const WSAEAFNOSUPPORT = 10047; +pub const WSAEADDRINUSE = 10048; +pub const WSAEADDRNOTAVAIL = 10049; +pub const WSAENETDOWN = 10050; +pub const WSAENETUNREACH = 10051; +pub const WSAENETRESET = 10052; +pub const WSAECONNABORTED = 10053; +pub const WSAECONNRESET = 10054; +pub const WSAENOBUFS = 10055; +pub const WSAEISCONN = 10056; +pub const WSAENOTCONN = 10057; +pub const WSAESHUTDOWN = 10058; +pub const WSAETOOMANYREFS = 10059; +pub const WSAETIMEDOUT = 10060; +pub const WSAECONNREFUSED = 10061; +pub const WSAELOOP = 10062; +pub const WSAENAMETOOLONG = 10063; +pub const WSAEHOSTDOWN = 10064; +pub const WSAEHOSTUNREACH = 10065; +pub const WSAENOTEMPTY = 10066; +pub const WSAEPROCLIM = 10067; +pub const WSAEUSERS = 10068; +pub const WSAEDQUOT = 10069; +pub const WSAESTALE = 10070; +pub const WSAEREMOTE = 10071; +pub const WSASYSNOTREADY = 10091; +pub const WSAVERNOTSUPPORTED = 10092; +pub const WSANOTINITIALISED = 10093; +pub const WSAEDISCON = 10101; +pub const WSAENOMORE = 10102; +pub const WSAECANCELLED = 10103; +pub const WSAEINVALIDPROCTABLE = 10104; +pub const WSAEINVALIDPROVIDER = 10105; +pub const WSAEPROVIDERFAILEDINIT = 10106; +pub const WSASYSCALLFAILURE = 10107; +pub const WSASERVICE_NOT_FOUND = 10108; +pub const WSATYPE_NOT_FOUND = 10109; +pub const WSA_E_NO_MORE = 10110; +pub const WSA_E_CANCELLED = 10111; +pub const WSAEREFUSED = 10112; +pub const WSAHOST_NOT_FOUND = 11001; +pub const WSATRY_AGAIN = 11002; +pub const WSANO_RECOVERY = 11003; +pub const WSANO_DATA = 11004; +pub const WSA_QOS_RECEIVERS = 11005; +pub const WSA_QOS_SENDERS = 11006; +pub const WSA_QOS_NO_SENDERS = 11007; +pub const WSA_QOS_NO_RECEIVERS = 11008; +pub const WSA_QOS_REQUEST_CONFIRMED = 11009; +pub const WSA_QOS_ADMISSION_FAILURE = 11010; +pub const WSA_QOS_POLICY_FAILURE = 11011; +pub const WSA_QOS_BAD_STYLE = 11012; +pub const WSA_QOS_BAD_OBJECT = 11013; +pub const WSA_QOS_TRAFFIC_CTRL_ERROR = 11014; +pub const WSA_QOS_GENERIC_ERROR = 11015; +pub const WSA_QOS_ESERVICETYPE = 11016; +pub const WSA_QOS_EFLOWSPEC = 11017; +pub const WSA_QOS_EPROVSPECBUF = 11018; +pub const WSA_QOS_EFILTERSTYLE = 11019; +pub const WSA_QOS_EFILTERTYPE = 11020; +pub const WSA_QOS_EFILTERCOUNT = 11021; +pub const WSA_QOS_EOBJLENGTH = 11022; +pub const WSA_QOS_EFLOWCOUNT = 11023; +pub const WSA_QOS_EUNKOWNPSOBJ = 11024; +pub const WSA_QOS_EPOLICYOBJ = 11025; +pub const WSA_QOS_EFLOWDESC = 11026; +pub const WSA_QOS_EPSFLOWSPEC = 11027; +pub const WSA_QOS_EPSFILTERSPEC = 11028; +pub const WSA_QOS_ESDMODEOBJ = 11029; +pub const WSA_QOS_ESHAPERATEOBJ = 11030; +pub const WSA_QOS_RESERVED_PETYPE = 11031; + +/// no parameters +const IOC_VOID = 0x80000000; + +/// copy out parameters +const IOC_OUT = 0x40000000; + +/// copy in parameters +const IOC_IN = 0x80000000; + +/// The IOCTL is a generic Windows Sockets 2 IOCTL code. New IOCTL codes defined for Windows Sockets 2 will have T == 1. +const IOC_WS2 = 0x08000000; + +pub const SIO_BASE_HANDLE = IOC_OUT | IOC_WS2 | 34; + +pub extern "ws2_32" fn WSAStartup( + wVersionRequired: WORD, + lpWSAData: *WSADATA, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSACleanup() callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSAGetLastError() callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSASocketA( + af: c_int, + type: c_int, + protocol: c_int, + lpProtocolInfo: ?*WSAPROTOCOL_INFOA, + g: GROUP, + dwFlags: DWORD, +) callconv(.Stdcall) SOCKET; +pub extern "ws2_32" fn WSASocketW( + af: c_int, + type: c_int, + protocol: c_int, + lpProtocolInfo: ?*WSAPROTOCOL_INFOW, + g: GROUP, + dwFlags: DWORD, +) callconv(.Stdcall) SOCKET; +pub extern "ws2_32" fn closesocket(s: SOCKET) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSAIoctl( + s: SOCKET, + dwIoControlCode: DWORD, + lpvInBuffer: ?*const c_void, + cbInBuffer: DWORD, + lpvOutBuffer: ?LPVOID, + cbOutBuffer: DWORD, + lpcbBytesReturned: LPDWORD, + lpOverlapped: ?*WSAOVERLAPPED, + lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn accept( + s: SOCKET, + addr: ?*sockaddr, + addrlen: socklen_t, +) callconv(.Stdcall) SOCKET; +pub extern "ws2_32" fn connect( + s: SOCKET, + name: *const sockaddr, + namelen: socklen_t, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSARecv( + s: SOCKET, + lpBuffers: [*]const WSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesRecvd: ?*DWORD, + lpFlags: *DWORD, + lpOverlapped: ?*WSAOVERLAPPED, + lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSARecvFrom( + s: SOCKET, + lpBuffers: [*]const WSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesRecvd: ?*DWORD, + lpFlags: *DWORD, + lpFrom: ?*sockaddr, + lpFromlen: socklen_t, + lpOverlapped: ?*WSAOVERLAPPED, + lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSASend( + s: SOCKET, + lpBuffers: [*]WSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesSent: ?*DWORD, + dwFlags: DWORD, + lpOverlapped: ?*WSAOVERLAPPED, + lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, +) callconv(.Stdcall) c_int; +pub extern "ws2_32" fn WSASendTo( + s: SOCKET, + lpBuffers: [*]WSABUF, + dwBufferCount: DWORD, + lpNumberOfBytesSent: ?*DWORD, + dwFlags: DWORD, + lpTo: ?*const sockaddr, + iTolen: socklen_t, + lpOverlapped: ?*WSAOVERLAPPED, + lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE, +) callconv(.Stdcall) c_int; diff --git a/lib/std/os/zen.zig b/lib/std/os/zen.zig deleted file mode 100644 index 9d111d98a..000000000 --- a/lib/std/os/zen.zig +++ /dev/null @@ -1,260 +0,0 @@ -const std = @import("../std.zig"); -const assert = std.debug.assert; - -////////////////////////// -//// IPC structures //// -////////////////////////// - -pub const Message = struct { - sender: MailboxId, - receiver: MailboxId, - code: usize, - args: [5]usize, - payload: ?[]const u8, - - pub fn from(mailbox_id: MailboxId) Message { - return Message{ - .sender = MailboxId.Undefined, - .receiver = mailbox_id, - .code = undefined, - .args = undefined, - .payload = null, - }; - } - - pub fn to(mailbox_id: MailboxId, msg_code: usize, args: ...) Message { - var message = Message{ - .sender = MailboxId.This, - .receiver = mailbox_id, - .code = msg_code, - .args = undefined, - .payload = null, - }; - - assert(args.len <= message.args.len); - comptime var i = 0; - inline while (i < args.len) : (i += 1) { - message.args[i] = args[i]; - } - - return message; - } - - pub fn as(self: Message, sender: MailboxId) Message { - var message = self; - message.sender = sender; - return message; - } - - pub fn withPayload(self: Message, payload: []const u8) Message { - var message = self; - message.payload = payload; - return message; - } -}; - -pub const MailboxId = union(enum) { - Undefined, - This, - Kernel, - Port: u16, - Thread: u16, -}; - -////////////////////////////////////// -//// Ports reserved for servers //// -////////////////////////////////////// - -pub const Server = struct { - pub const Keyboard = MailboxId{ .Port = 0 }; - pub const Terminal = MailboxId{ .Port = 1 }; -}; - -//////////////////////// -//// POSIX things //// -//////////////////////// - -// Standard streams. -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -// FIXME: let's borrow Linux's error numbers for now. -usingnamespace @import("bits/linux/errno-generic.zig"); -// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; -} - -// TODO: implement this correctly. -pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - switch (fd) { - STDIN_FILENO => { - var i: usize = 0; - while (i < count) : (i += 1) { - send(&Message.to(Server.Keyboard, 0)); - - // FIXME: we should be certain that we are receiving from Keyboard. - var message = Message.from(MailboxId.This); - receive(&message); - - buf[i] = @intCast(u8, message.args[0]); - } - }, - else => unreachable, - } - return count; -} - -// TODO: implement this correctly. -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - switch (fd) { - STDOUT_FILENO, STDERR_FILENO => { - send(&Message.to(Server.Terminal, 1).withPayload(buf[0..count])); - }, - else => unreachable, - } - return count; -} - -/////////////////////////// -//// Syscall numbers //// -/////////////////////////// - -pub const Syscall = enum(usize) { - exit = 0, - send = 1, - receive = 2, - subscribeIRQ = 3, - inb = 4, - outb = 5, - map = 6, - createThread = 7, -}; - -//////////////////// -//// Syscalls //// -//////////////////// - -pub fn exit(status: i32) noreturn { - _ = syscall1(Syscall.exit, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn send(message: *const Message) void { - _ = syscall1(Syscall.send, @ptrToInt(message)); -} - -pub fn receive(destination: *Message) void { - _ = syscall1(Syscall.receive, @ptrToInt(destination)); -} - -pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void { - _ = syscall2(Syscall.subscribeIRQ, irq, @ptrToInt(mailbox_id)); -} - -pub fn inb(port: u16) u8 { - return @intCast(u8, syscall1(Syscall.inb, port)); -} - -pub fn outb(port: u16, value: u8) void { - _ = syscall2(Syscall.outb, port, value); -} - -pub fn map(v_addr: usize, p_addr: usize, size: usize, writable: bool) bool { - return syscall4(Syscall.map, v_addr, p_addr, size, @boolToInt(writable)) != 0; -} - -pub fn createThread(function: fn () void) u16 { - return u16(syscall1(Syscall.createThread, @ptrToInt(function))); -} - -///////////////////////// -//// Syscall stubs //// -///////////////////////// - -inline fn syscall0(number: Syscall) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number) - ); -} - -inline fn syscall1(number: Syscall, arg1: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1) - ); -} - -inline fn syscall2(number: Syscall, arg1: usize, arg2: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2) - ); -} - -inline fn syscall3(number: Syscall, arg1: usize, arg2: usize, arg3: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3) - ); -} - -inline fn syscall4(number: Syscall, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4) - ); -} - -inline fn syscall5( - number: Syscall, - arg1: usize, - arg2: usize, - arg3: usize, - arg4: usize, - arg5: usize, -) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5) - ); -} - -inline fn syscall6( - number: Syscall, - arg1: usize, - arg2: usize, - arg3: usize, - arg4: usize, - arg5: usize, - arg6: usize, -) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5), - [arg6] "{ebp}" (arg6) - ); -} diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 5cbab2d33..bc29e985b 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -193,7 +193,7 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian, ///Initialize a packed array using an unpacked array /// or, more likely, an array literal. pub fn init(ints: [int_count]Int) Self { - var self = Self(undefined); + var self = @as(Self, undefined); for (ints) |int, i| self.set(i, int); return self; } @@ -201,7 +201,7 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian, ///Return the Int stored at index pub fn get(self: Self, index: usize) Int { debug.assert(index < int_count); - return Io.get(self.bytes, index, 0); + return Io.get(&self.bytes, index, 0); } ///Copy int into the array at index @@ -328,11 +328,11 @@ test "PackedIntArray" { const expected_bytes = ((bits * int_count) + 7) / 8; testing.expect(@sizeOf(PackedArray) == expected_bytes); - var data = PackedArray(undefined); + var data = @as(PackedArray, undefined); //write values, counting up - var i = usize(0); - var count = I(0); + var i = @as(usize, 0); + var count = @as(I, 0); while (i < data.len()) : (i += 1) { data.set(i, count); if (bits > 0) count +%= 1; @@ -352,7 +352,7 @@ test "PackedIntArray" { test "PackedIntArray init" { const PackedArray = PackedIntArray(u3, 8); var packed_array = PackedArray.init([_]u3{ 0, 1, 2, 3, 4, 5, 6, 7 }); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array.len()) : (i += 1) testing.expect(packed_array.get(i) == i); } @@ -375,8 +375,8 @@ test "PackedIntSlice" { var data = P.init(&buffer, int_count); //write values, counting up - var i = usize(0); - var count = I(0); + var i = @as(usize, 0); + var count = @as(I, 0); while (i < data.len()) : (i += 1) { data.set(i, count); if (bits > 0) count +%= 1; @@ -402,11 +402,11 @@ test "PackedIntSlice of PackedInt(Array/Slice)" { const Int = @IntType(false, bits); const PackedArray = PackedIntArray(Int, int_count); - var packed_array = PackedArray(undefined); + var packed_array = @as(PackedArray, undefined); const limit = (1 << bits); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array.len()) : (i += 1) { packed_array.set(i, @intCast(Int, i % limit)); } @@ -463,20 +463,20 @@ test "PackedIntSlice accumulating bit offsets" { // anything { const PackedArray = PackedIntArray(u3, 16); - var packed_array = PackedArray(undefined); + var packed_array = @as(PackedArray, undefined); var packed_slice = packed_array.slice(0, packed_array.len()); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array.len() - 1) : (i += 1) { packed_slice = packed_slice.slice(1, packed_slice.len()); } } { const PackedArray = PackedIntArray(u11, 88); - var packed_array = PackedArray(undefined); + var packed_array = @as(PackedArray, undefined); var packed_slice = packed_array.slice(0, packed_array.len()); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array.len() - 1) : (i += 1) { packed_slice = packed_slice.slice(1, packed_slice.len()); } @@ -493,7 +493,7 @@ test "PackedInt(Array/Slice) sliceCast" { var packed_slice_cast_9 = packed_array.slice(0, (packed_array.len() / 9) * 9).sliceCast(u9); const packed_slice_cast_3 = packed_slice_cast_9.sliceCast(u3); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_slice_cast_2.len()) : (i += 1) { const val = switch (builtin.endian) { .Big => 0b01, @@ -518,8 +518,8 @@ test "PackedInt(Array/Slice) sliceCast" { i = 0; while (i < packed_slice_cast_3.len()) : (i += 1) { const val = switch (builtin.endian) { - .Big => if (i % 2 == 0) u3(0b111) else u3(0b000), - .Little => if (i % 2 == 0) u3(0b111) else u3(0b000), + .Big => if (i % 2 == 0) @as(u3, 0b111) else @as(u3, 0b000), + .Little => if (i % 2 == 0) @as(u3, 0b111) else @as(u3, 0b000), }; testing.expect(packed_slice_cast_3.get(i) == val); } @@ -528,20 +528,11 @@ test "PackedInt(Array/Slice) sliceCast" { test "PackedInt(Array/Slice)Endian" { { const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8); - var packed_array_be = PackedArrayBe.init([_]u4{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - }); + var packed_array_be = PackedArrayBe.init([_]u4{ 0, 1, 2, 3, 4, 5, 6, 7 }); testing.expect(packed_array_be.bytes[0] == 0b00000001); testing.expect(packed_array_be.bytes[1] == 0b00100011); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array_be.len()) : (i += 1) { testing.expect(packed_array_be.get(i) == i); } @@ -563,23 +554,14 @@ test "PackedInt(Array/Slice)Endian" { { const PackedArrayBe = PackedIntArrayEndian(u11, .Big, 8); - var packed_array_be = PackedArrayBe.init([_]u11{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - }); + var packed_array_be = PackedArrayBe.init([_]u11{ 0, 1, 2, 3, 4, 5, 6, 7 }); testing.expect(packed_array_be.bytes[0] == 0b00000000); testing.expect(packed_array_be.bytes[1] == 0b00000000); testing.expect(packed_array_be.bytes[2] == 0b00000100); testing.expect(packed_array_be.bytes[3] == 0b00000001); testing.expect(packed_array_be.bytes[4] == 0b00000000); - var i = usize(0); + var i = @as(usize, 0); while (i < packed_array_be.len()) : (i += 1) { testing.expect(packed_array_be.get(i) == i); } @@ -622,7 +604,7 @@ test "PackedIntArray at end of available memory" { p: PackedArray, }; - const allocator = std.heap.direct_allocator; + const allocator = std.heap.page_allocator; var pad = try allocator.create(Padded); defer allocator.destroy(pad); @@ -636,7 +618,7 @@ test "PackedIntSlice at end of available memory" { } const PackedSlice = PackedIntSlice(u11); - const allocator = std.heap.direct_allocator; + const allocator = std.heap.page_allocator; var page = try allocator.alloc(u8, std.mem.page_size); defer allocator.free(page); diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 7a2b2c6b6..db60e6049 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -6,6 +6,7 @@ const mem = std.mem; const os = std.os; const warn = std.debug.warn; const coff = std.coff; +const fs = std.fs; const File = std.fs.File; const ArrayList = std.ArrayList; @@ -469,7 +470,7 @@ pub const Pdb = struct { msf: Msf, pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { - self.in_file = try File.openRead(file_name); + self.in_file = try fs.cwd().openFile(file_name, .{}); self.allocator = coff_ptr.allocator; self.coff = coff_ptr; @@ -500,7 +501,7 @@ const Msf = struct { const superblock = try in.readStruct(SuperBlock); // Sanity checks - if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) + if (!mem.eql(u8, &superblock.FileMagic, SuperBlock.file_magic)) return error.InvalidDebugInfo; if (superblock.FreeBlockMapBlock != 1 and superblock.FreeBlockMapBlock != 2) return error.InvalidDebugInfo; @@ -532,7 +533,7 @@ const Msf = struct { const stream_sizes = try allocator.alloc(u32, stream_count); defer allocator.free(stream_sizes); - // Microsoft's implementation uses u32(-1) for inexistant streams. + // Microsoft's implementation uses @as(u32, -1) for inexistant streams. // These streams are not used, but still participate in the file // and must be taken into account when resolving stream indices. const Nil = 0xFFFFFFFF; @@ -546,7 +547,7 @@ const Msf = struct { const size = stream_sizes[i]; if (size == 0) { stream.* = MsfStream{ - .blocks = [_]u32{}, + .blocks = &[_]u32{}, }; } else { var blocks = try allocator.alloc(u32, size); @@ -634,7 +635,7 @@ const MsfStream = struct { /// Implementation of InStream trait for Pdb.MsfStream stream: Stream = undefined, - pub const Error = @typeOf(read).ReturnType.ErrorSet; + pub const Error = @TypeOf(read).ReturnType.ErrorSet; pub const Stream = io.InStream(Error); fn init(block_size: u32, file: File, blocks: []u32) MsfStream { diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index e6f398a55..6c56f469f 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -1,9 +1,12 @@ const std = @import("std.zig"); const Allocator = std.mem.Allocator; const debug = std.debug; +const assert = debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const expectError = std.testing.expectError; +/// Priority queue for storing generic data. Initialize with `init`. pub fn PriorityQueue(comptime T: type) type { return struct { const Self = @This(); @@ -13,19 +16,27 @@ pub fn PriorityQueue(comptime T: type) type { allocator: *Allocator, compareFn: fn (a: T, b: T) bool, + /// Initialize and return a priority queue. Provide + /// `compareFn` that returns `true` when its first argument + /// should get popped before its second argument. For example, + /// to make `pop` return the minimum value, provide + /// + /// `fn lessThan(a: T, b: T) bool { return a < b; }` pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) bool) Self { return Self{ - .items = [_]T{}, + .items = &[_]T{}, .len = 0, .allocator = allocator, .compareFn = compareFn, }; } + /// Free memory used by the queue. pub fn deinit(self: Self) void { self.allocator.free(self.items); } + /// Insert a new element, maintaining priority. pub fn add(self: *Self, elem: T) !void { try ensureCapacity(self, self.len + 1); addUnchecked(self, elem); @@ -48,6 +59,7 @@ pub fn PriorityQueue(comptime T: type) type { self.len += 1; } + /// Add each element in `items` to the queue. pub fn addSlice(self: *Self, items: []const T) !void { try self.ensureCapacity(self.len + items.len); for (items) |e| { @@ -55,27 +67,45 @@ pub fn PriorityQueue(comptime T: type) type { } } + /// Look at the highest priority element in the queue. Returns + /// `null` if empty. pub fn peek(self: *Self) ?T { return if (self.len > 0) self.items[0] else null; } + /// Pop the highest priority element from the queue. Returns + /// `null` if empty. pub fn removeOrNull(self: *Self) ?T { return if (self.len > 0) self.remove() else null; } + /// Remove and return the highest priority element from the + /// queue. pub fn remove(self: *Self) T { - const first = self.items[0]; - const last = self.items[self.len - 1]; - self.items[0] = last; - self.len -= 1; - siftDown(self, 0); - return first; + return self.removeIndex(0); } + /// Remove and return element at index. Indices are in the + /// same order as iterator, which is not necessarily priority + /// order. + pub fn removeIndex(self: *Self, index: usize) T { + assert(self.len > index); + const last = self.items[self.len - 1]; + const item = self.items[index]; + self.items[index] = last; + self.len -= 1; + siftDown(self, 0); + return item; + } + + /// Return the number of elements remaining in the priority + /// queue. pub fn count(self: Self) usize { return self.len; } + /// Return the number of elements that can be added to the + /// queue before more memory is allocated. pub fn capacity(self: Self) usize { return self.items.len; } @@ -171,6 +201,8 @@ pub fn PriorityQueue(comptime T: type) type { } }; + /// Return an iterator that walks the queue without consuming + /// it. Invalidated if the heap is modified. pub fn iterator(self: *Self) Iterator { return Iterator{ .queue = self, @@ -179,19 +211,19 @@ pub fn PriorityQueue(comptime T: type) type { } fn dump(self: *Self) void { - warn("{{ "); - warn("items: "); + warn("{{ ", .{}); + warn("items: ", .{}); for (self.items) |e, i| { if (i >= self.len) break; - warn("{}, ", e); + warn("{}, ", .{e}); } - warn("array: "); + warn("array: ", .{}); for (self.items) |e, i| { - warn("{}, ", e); + warn("{}, ", .{e}); } - warn("len: {} ", self.len); - warn("capacity: {}", self.capacity()); - warn(" }}\n"); + warn("len: {} ", .{self.len}); + warn("capacity: {}", .{self.capacity()}); + warn(" }}\n", .{}); } }; } @@ -216,12 +248,12 @@ test "std.PriorityQueue: add and remove min heap" { try queue.add(23); try queue.add(25); try queue.add(13); - expectEqual(u32(7), queue.remove()); - expectEqual(u32(12), queue.remove()); - expectEqual(u32(13), queue.remove()); - expectEqual(u32(23), queue.remove()); - expectEqual(u32(25), queue.remove()); - expectEqual(u32(54), queue.remove()); + expectEqual(@as(u32, 7), queue.remove()); + expectEqual(@as(u32, 12), queue.remove()); + expectEqual(@as(u32, 13), queue.remove()); + expectEqual(@as(u32, 23), queue.remove()); + expectEqual(@as(u32, 25), queue.remove()); + expectEqual(@as(u32, 54), queue.remove()); } test "std.PriorityQueue: add and remove same min heap" { @@ -234,12 +266,12 @@ test "std.PriorityQueue: add and remove same min heap" { try queue.add(2); try queue.add(1); try queue.add(1); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(2), queue.remove()); - expectEqual(u32(2), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 2), queue.remove()); + expectEqual(@as(u32, 2), queue.remove()); } test "std.PriorityQueue: removeOrNull on empty" { @@ -256,9 +288,9 @@ test "std.PriorityQueue: edge case 3 elements" { try queue.add(9); try queue.add(3); try queue.add(2); - expectEqual(u32(2), queue.remove()); - expectEqual(u32(3), queue.remove()); - expectEqual(u32(9), queue.remove()); + expectEqual(@as(u32, 2), queue.remove()); + expectEqual(@as(u32, 3), queue.remove()); + expectEqual(@as(u32, 9), queue.remove()); } test "std.PriorityQueue: peek" { @@ -269,8 +301,8 @@ test "std.PriorityQueue: peek" { try queue.add(9); try queue.add(3); try queue.add(2); - expectEqual(u32(2), queue.peek().?); - expectEqual(u32(2), queue.peek().?); + expectEqual(@as(u32, 2), queue.peek().?); + expectEqual(@as(u32, 2), queue.peek().?); } test "std.PriorityQueue: sift up with odd indices" { @@ -321,12 +353,12 @@ test "std.PriorityQueue: add and remove max heap" { try queue.add(23); try queue.add(25); try queue.add(13); - expectEqual(u32(54), queue.remove()); - expectEqual(u32(25), queue.remove()); - expectEqual(u32(23), queue.remove()); - expectEqual(u32(13), queue.remove()); - expectEqual(u32(12), queue.remove()); - expectEqual(u32(7), queue.remove()); + expectEqual(@as(u32, 54), queue.remove()); + expectEqual(@as(u32, 25), queue.remove()); + expectEqual(@as(u32, 23), queue.remove()); + expectEqual(@as(u32, 13), queue.remove()); + expectEqual(@as(u32, 12), queue.remove()); + expectEqual(@as(u32, 7), queue.remove()); } test "std.PriorityQueue: add and remove same max heap" { @@ -339,12 +371,12 @@ test "std.PriorityQueue: add and remove same max heap" { try queue.add(2); try queue.add(1); try queue.add(1); - expectEqual(u32(2), queue.remove()); - expectEqual(u32(2), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); - expectEqual(u32(1), queue.remove()); + expectEqual(@as(u32, 2), queue.remove()); + expectEqual(@as(u32, 2), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); + expectEqual(@as(u32, 1), queue.remove()); } test "std.PriorityQueue: iterator" { @@ -366,5 +398,28 @@ test "std.PriorityQueue: iterator" { _ = map.remove(e); } - expectEqual(usize(0), map.count()); + expectEqual(@as(usize, 0), map.count()); +} + +test "std.PriorityQueue: remove at index" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + try queue.add(3); + try queue.add(2); + try queue.add(1); + + var it = queue.iterator(); + var elem = it.next(); + var idx: usize = 0; + const two_idx = while (elem != null) : (elem = it.next()) { + if (elem.? == 2) + break idx; + idx += 1; + } else unreachable; + + expectEqual(queue.removeIndex(two_idx), 2); + expectEqual(queue.remove(), 1); + expectEqual(queue.remove(), 3); + expectEqual(queue.removeOrNull(), null); } diff --git a/lib/std/process.zig b/lib/std/process.zig index c74e8c43b..0ce3cabc1 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -39,7 +39,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); - if (os.windows.is_the_target) { + if (builtin.os == .windows) { const ptr = try os.windows.GetEnvironmentStringsW(); defer os.windows.FreeEnvironmentStringsW(ptr); @@ -77,9 +77,9 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { // TODO: Verify that the documentation is incorrect // https://github.com/WebAssembly/WASI/issues/27 - var environ = try allocator.alloc(?[*]u8, environ_count + 1); + var environ = try allocator.alloc(?[*:0]u8, environ_count + 1); defer allocator.free(environ); - var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size); + var environ_buf = try std.heap.page_allocator.alloc(u8, environ_buf_size); defer allocator.free(environ_buf); const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr); @@ -129,7 +129,7 @@ pub const GetEnvVarOwnedError = error{ /// Caller must free returned memory. /// TODO make this go through libc when we have it pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); defer allocator.free(key_with_null); @@ -397,7 +397,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { return os.unexpectedErrno(args_sizes_get_ret); } - var argv = try allocator.alloc([*]u8, count); + var argv = try allocator.alloc([*:0]u8, count); defer allocator.free(argv); var argv_buf = try allocator.alloc(u8, buf_size); @@ -473,14 +473,14 @@ pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [_][]const u8{ "a", "b", "c", "d" }); - testWindowsCmdLine(c"\"abc\" d e", [_][]const u8{ "abc", "d", "e" }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [_][]const u8{ "a\\\\\\b", "de fg", "h" }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [_][]const u8{ "a\\\"b", "c", "d" }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [_][]const u8{ "a\\\\b c", "d", "e" }); - testWindowsCmdLine(c"a b\tc \"d f", [_][]const u8{ "a", "b", "c", "\"d", "f" }); + testWindowsCmdLine("a b\tc d", &[_][]const u8{ "a", "b", "c", "d" }); + testWindowsCmdLine("\"abc\" d e", &[_][]const u8{ "abc", "d", "e" }); + testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" }); + testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" }); + testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" }); + testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "\"d", "f" }); - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [_][]const u8{ + testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{ ".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", diff --git a/lib/std/progress.zig b/lib/std/progress.zig new file mode 100644 index 000000000..1c5ecd261 --- /dev/null +++ b/lib/std/progress.zig @@ -0,0 +1,255 @@ +const std = @import("std"); +const testing = std.testing; +const assert = std.debug.assert; + +/// This API is non-allocating and non-fallible. The tradeoff is that users of +/// this API must provide the storage for each `Progress.Node`. +/// Initialize the struct directly, overriding these fields as desired: +/// * `refresh_rate_ms` +/// * `initial_delay_ms` +pub const Progress = struct { + /// `null` if the current node (and its children) should + /// not print on update() + terminal: ?std.fs.File = undefined, + + root: Node = undefined, + + /// Keeps track of how much time has passed since the beginning. + /// Used to compare with `initial_delay_ms` and `refresh_rate_ms`. + timer: std.time.Timer = undefined, + + /// When the previous refresh was written to the terminal. + /// Used to compare with `refresh_rate_ms`. + prev_refresh_timestamp: u64 = undefined, + + /// This buffer represents the maximum number of bytes written to the terminal + /// with each refresh. + output_buffer: [100]u8 = undefined, + + /// How many nanoseconds between writing updates to the terminal. + refresh_rate_ns: u64 = 50 * std.time.millisecond, + + /// How many nanoseconds to keep the output hidden + initial_delay_ns: u64 = 500 * std.time.millisecond, + + done: bool = true, + + /// Keeps track of how many columns in the terminal have been output, so that + /// we can move the cursor back later. + columns_written: usize = undefined, + + /// Represents one unit of progress. Each node can have children nodes, or + /// one can use integers with `update`. + pub const Node = struct { + context: *Progress, + parent: ?*Node, + completed_items: usize, + name: []const u8, + recently_updated_child: ?*Node = null, + + /// This field may be updated freely. + estimated_total_items: ?usize, + + /// Create a new child progress node. + /// Call `Node.end` when done. + /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this + /// API to set `self.parent.recently_updated_child` with the return value. + /// Until that is fixed you probably want to call `activate` on the return value. + pub fn start(self: *Node, name: []const u8, estimated_total_items: ?usize) Node { + return Node{ + .context = self.context, + .parent = self, + .completed_items = 0, + .name = name, + .estimated_total_items = estimated_total_items, + }; + } + + /// This is the same as calling `start` and then `end` on the returned `Node`. + pub fn completeOne(self: *Node) void { + if (self.parent) |parent| parent.recently_updated_child = self; + self.completed_items += 1; + self.context.maybeRefresh(); + } + + pub fn end(self: *Node) void { + self.context.maybeRefresh(); + if (self.parent) |parent| { + if (parent.recently_updated_child) |parent_child| { + if (parent_child == self) { + parent.recently_updated_child = null; + } + } + parent.completeOne(); + } else { + self.context.done = true; + self.context.refresh(); + } + } + + /// Tell the parent node that this node is actively being worked on. + pub fn activate(self: *Node) void { + if (self.parent) |parent| parent.recently_updated_child = self; + } + }; + + /// Create a new progress node. + /// Call `Node.end` when done. + /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this + /// API to return Progress rather than accept it as a parameter. + pub fn start(self: *Progress, name: []const u8, estimated_total_items: ?usize) !*Node { + const stderr = std.io.getStdErr(); + self.terminal = if (stderr.supportsAnsiEscapeCodes()) stderr else null; + self.root = Node{ + .context = self, + .parent = null, + .completed_items = 0, + .name = name, + .estimated_total_items = estimated_total_items, + }; + self.columns_written = 0; + self.prev_refresh_timestamp = 0; + self.timer = try std.time.Timer.start(); + self.done = false; + return &self.root; + } + + /// Updates the terminal if enough time has passed since last update. + pub fn maybeRefresh(self: *Progress) void { + const now = self.timer.read(); + if (now < self.initial_delay_ns) return; + if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; + self.refresh(); + } + + /// Updates the terminal and resets `self.next_refresh_timestamp`. + pub fn refresh(self: *Progress) void { + const file = self.terminal orelse return; + + const prev_columns_written = self.columns_written; + var end: usize = 0; + if (self.columns_written > 0) { + // restore cursor position + end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len; + self.columns_written = 0; + + // clear rest of line + end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; + } + + if (!self.done) { + var need_ellipse = false; + var maybe_node: ?*Node = &self.root; + while (maybe_node) |node| { + if (need_ellipse) { + self.bufWrite(&end, "...", .{}); + } + need_ellipse = false; + if (node.name.len != 0 or node.estimated_total_items != null) { + if (node.name.len != 0) { + self.bufWrite(&end, "{}", .{node.name}); + need_ellipse = true; + } + if (node.estimated_total_items) |total| { + if (need_ellipse) self.bufWrite(&end, " ", .{}); + self.bufWrite(&end, "[{}/{}] ", .{ node.completed_items + 1, total }); + need_ellipse = false; + } else if (node.completed_items != 0) { + if (need_ellipse) self.bufWrite(&end, " ", .{}); + self.bufWrite(&end, "[{}] ", .{node.completed_items + 1}); + need_ellipse = false; + } + } + maybe_node = node.recently_updated_child; + } + if (need_ellipse) { + self.bufWrite(&end, "...", .{}); + } + } + + _ = file.write(self.output_buffer[0..end]) catch |e| { + // Stop trying to write to this file once it errors. + self.terminal = null; + }; + self.prev_refresh_timestamp = self.timer.read(); + } + + pub fn log(self: *Progress, comptime format: []const u8, args: var) void { + const file = self.terminal orelse return; + self.refresh(); + file.outStream().stream.print(format, args) catch { + self.terminal = null; + return; + }; + self.columns_written = 0; + } + + fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: var) void { + if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { + const amt = written.len; + end.* += amt; + self.columns_written += amt; + } else |err| switch (err) { + error.BufferTooSmall => { + self.columns_written += self.output_buffer.len - end.*; + end.* = self.output_buffer.len; + }, + } + const bytes_needed_for_esc_codes_at_end = 11; + const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end; + if (end.* > max_end) { + const suffix = "..."; + self.columns_written = self.columns_written - (end.* - max_end) + suffix.len; + std.mem.copy(u8, self.output_buffer[max_end..], suffix); + end.* = max_end + suffix.len; + } + } +}; + +test "basic functionality" { + var disable = true; + if (disable) { + // This test is disabled because it uses time.sleep() and is therefore slow. It also + // prints bogus progress data to stderr. + return error.SkipZigTest; + } + var progress = Progress{}; + const root_node = try progress.start("", 100); + defer root_node.end(); + + const sub_task_names = [_][]const u8{ + "reticulating splines", + "adjusting shoes", + "climbing towers", + "pouring juice", + }; + var next_sub_task: usize = 0; + + var i: usize = 0; + while (i < 100) : (i += 1) { + var node = root_node.start(sub_task_names[next_sub_task], 5); + node.activate(); + next_sub_task = (next_sub_task + 1) % sub_task_names.len; + + node.completeOne(); + std.time.sleep(5 * std.time.millisecond); + node.completeOne(); + node.completeOne(); + std.time.sleep(5 * std.time.millisecond); + node.completeOne(); + node.completeOne(); + std.time.sleep(5 * std.time.millisecond); + + node.end(); + + std.time.sleep(5 * std.time.millisecond); + } + { + var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null); + node.activate(); + std.time.sleep(10 * std.time.millisecond); + progress.refresh(); + std.time.sleep(10 * std.time.millisecond); + node.end(); + } +} diff --git a/lib/std/rand.zig b/lib/std/rand.zig index e14a60e2a..f9fa4a2d6 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -54,7 +54,7 @@ pub const Random = struct { // use LE instead of native endian for better portability maybe? // TODO: endian portability is pointless if the underlying prng isn't endian portable. // TODO: document the endian portability of this library. - const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, rand_bytes); + const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, &rand_bytes); const unsigned_result = @truncate(UnsignedT, byte_aligned_result); return @bitCast(T, unsigned_result); } @@ -93,13 +93,13 @@ pub const Random = struct { // http://www.pcg-random.org/posts/bounded-rands.html // "Lemire's (with an extra tweak from me)" var x: Small = r.int(Small); - var m: Large = Large(x) * Large(less_than); + var m: Large = @as(Large, x) * @as(Large, less_than); var l: Small = @truncate(Small, m); if (l < less_than) { // TODO: workaround for https://github.com/ziglang/zig/issues/1770 // should be: // var t: Small = -%less_than; - var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), Small(less_than))); + var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), @as(Small, less_than))); if (t >= less_than) { t -= less_than; @@ -109,7 +109,7 @@ pub const Random = struct { } while (l < t) { x = r.int(Small); - m = Large(x) * Large(less_than); + m = @as(Large, x) * @as(Large, less_than); l = @truncate(Small, m); } } @@ -286,7 +286,7 @@ pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { // adapted from: // http://www.pcg-random.org/posts/bounded-rands.html // "Integer Multiplication (Biased)" - var m: T2 = T2(random_int) * T2(less_than); + var m: T2 = @as(T2, random_int) * @as(T2, less_than); return @intCast(T, m >> T.bit_count); } @@ -633,8 +633,8 @@ pub const Xoroshiro128 = struct { const r = s0 +% s1; s1 ^= s0; - self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14); - self.s[1] = math.rotl(u64, s1, u8(36)); + self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14); + self.s[1] = math.rotl(u64, s1, @as(u8, 36)); return r; } @@ -652,7 +652,7 @@ pub const Xoroshiro128 = struct { inline for (table) |entry| { var b: usize = 0; while (b < 64) : (b += 1) { - if ((entry & (u64(1) << @intCast(u6, b))) != 0) { + if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) { s0 ^= self.s[0]; s1 ^= self.s[1]; } @@ -1090,7 +1090,7 @@ fn testRange(r: *Random, start: i8, end: i8) void { testRangeBias(r, start, end, false); } fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { - const count = @intCast(usize, i32(end) - i32(start)); + const count = @intCast(usize, @as(i32, end) - @as(i32, start)); var values_buffer = [_]bool{false} ** 0x100; const values = values_buffer[0..count]; var i: usize = 0; diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 995248415..c29d3eeb2 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -17,7 +17,7 @@ pub fn next_f64(random: *Random, comptime tables: ZigTable) f64 { // We manually construct a float from parts as we can avoid an extra random lookup here by // using the unused exponent for the lookup table entry. const bits = random.scalar(u64); - const i = usize(bits & 0xff); + const i = @as(usize, bits & 0xff); const u = blk: { if (tables.is_symmetric) { diff --git a/lib/std/rb.zig b/lib/std/rb.zig index 4180c7459..c41a269a2 100644 --- a/lib/std/rb.zig +++ b/lib/std/rb.zig @@ -1,7 +1,7 @@ const std = @import("std.zig"); const assert = std.debug.assert; const testing = std.testing; -const mem = std.mem; // For mem.Compare +const Order = std.math.Order; const Color = enum(u1) { Black, @@ -132,7 +132,7 @@ pub const Node = struct { pub const Tree = struct { root: ?*Node, - compareFn: fn (*Node, *Node) mem.Compare, + compareFn: fn (*Node, *Node) Order, /// If you have a need for a version that caches this, please file a bug. pub fn first(tree: *Tree) ?*Node { @@ -389,7 +389,7 @@ pub const Tree = struct { var new = newconst; // I assume this can get optimized out if the caller already knows. - if (tree.compareFn(old, new) != mem.Compare.Equal) return ReplaceError.NotEqual; + if (tree.compareFn(old, new) != .eq) return ReplaceError.NotEqual; if (old.getParent()) |parent| { parent.setChild(new, parent.left == old); @@ -404,7 +404,7 @@ pub const Tree = struct { new.* = old.*; } - pub fn init(tree: *Tree, f: fn (*Node, *Node) mem.Compare) void { + pub fn init(tree: *Tree, f: fn (*Node, *Node) Order) void { tree.root = null; tree.compareFn = f; } @@ -469,19 +469,21 @@ fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node { is_left.* = false; while (maybe_node) |node| { - var res: mem.Compare = tree.compareFn(node, key); - if (res == mem.Compare.Equal) { + const res = tree.compareFn(node, key); + if (res == .eq) { return node; } pparent.* = node; - if (res == mem.Compare.GreaterThan) { - is_left.* = true; - maybe_node = node.left; - } else if (res == mem.Compare.LessThan) { - is_left.* = false; - maybe_node = node.right; - } else { - unreachable; + switch (res) { + .gt => { + is_left.* = true; + maybe_node = node.left; + }, + .lt => { + is_left.* = false; + maybe_node = node.right; + }, + .eq => unreachable, // handled above } } return null; @@ -496,16 +498,16 @@ fn testGetNumber(node: *Node) *testNumber { return @fieldParentPtr(testNumber, "node", node); } -fn testCompare(l: *Node, r: *Node) mem.Compare { +fn testCompare(l: *Node, r: *Node) Order { var left = testGetNumber(l); var right = testGetNumber(r); if (left.value < right.value) { - return mem.Compare.LessThan; + return .lt; } else if (left.value == right.value) { - return mem.Compare.Equal; + return .eq; } else if (left.value > right.value) { - return mem.Compare.GreaterThan; + return .gt; } unreachable; } diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig new file mode 100644 index 000000000..b9d9517c6 --- /dev/null +++ b/lib/std/reset_event.zig @@ -0,0 +1,436 @@ +const std = @import("std.zig"); +const builtin = @import("builtin"); +const testing = std.testing; +const SpinLock = std.SpinLock; +const assert = std.debug.assert; +const c = std.c; +const os = std.os; +const time = std.time; +const linux = os.linux; +const windows = os.windows; + +/// A resource object which supports blocking until signaled. +/// Once finished, the `deinit()` method should be called for correctness. +pub const ResetEvent = struct { + os_event: OsEvent, + + pub const OsEvent = if (builtin.single_threaded) + DebugEvent + else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux) + PosixEvent + else + AtomicEvent; + + pub fn init() ResetEvent { + return ResetEvent{ .os_event = OsEvent.init() }; + } + + pub fn deinit(self: *ResetEvent) void { + self.os_event.deinit(); + } + + /// Returns whether or not the event is currenetly set + pub fn isSet(self: *ResetEvent) bool { + return self.os_event.isSet(); + } + + /// Sets the event if not already set and + /// wakes up all the threads waiting on the event. + pub fn set(self: *ResetEvent) void { + return self.os_event.set(); + } + + /// Resets the event to its original, unset state. + pub fn reset(self: *ResetEvent) void { + return self.os_event.reset(); + } + + /// Wait for the event to be set by blocking the current thread. + pub fn wait(self: *ResetEvent) void { + return self.os_event.wait(null) catch unreachable; + } + + /// Wait for the event to be set by blocking the current thread. + /// A timeout in nanoseconds can be provided as a hint for how + /// long the thread should block on the unset event before throwind error.TimedOut. + pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { + return self.os_event.wait(timeout_ns); + } +}; + +const DebugEvent = struct { + is_set: bool, + + fn init() DebugEvent { + return DebugEvent{ .is_set = false }; + } + + fn deinit(self: *DebugEvent) void { + self.* = undefined; + } + + fn isSet(self: *DebugEvent) bool { + return self.is_set; + } + + fn reset(self: *DebugEvent) void { + self.is_set = false; + } + + fn set(self: *DebugEvent) void { + self.is_set = true; + } + + fn wait(self: *DebugEvent, timeout: ?u64) !void { + if (self.is_set) + return; + if (timeout != null) + return error.TimedOut; + @panic("deadlock detected"); + } +}; + +const PosixEvent = struct { + is_set: bool, + cond: c.pthread_cond_t, + mutex: c.pthread_mutex_t, + + fn init() PosixEvent { + return PosixEvent{ + .is_set = false, + .cond = c.PTHREAD_COND_INITIALIZER, + .mutex = c.PTHREAD_MUTEX_INITIALIZER, + }; + } + + fn deinit(self: *PosixEvent) void { + // on dragonfly, *destroy() functions can return EINVAL + // for statically initialized pthread structures + const err = if (builtin.os == .dragonfly) os.EINVAL else 0; + + const retm = c.pthread_mutex_destroy(&self.mutex); + assert(retm == 0 or retm == err); + const retc = c.pthread_cond_destroy(&self.cond); + assert(retc == 0 or retc == err); + } + + fn isSet(self: *PosixEvent) bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + return self.is_set; + } + + fn reset(self: *PosixEvent) void { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + self.is_set = false; + } + + fn set(self: *PosixEvent) void { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + if (!self.is_set) { + self.is_set = true; + assert(c.pthread_cond_broadcast(&self.cond) == 0); + } + } + + fn wait(self: *PosixEvent, timeout: ?u64) !void { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + // quick guard before possibly calling time syscalls below + if (self.is_set) + return; + + var ts: os.timespec = undefined; + if (timeout) |timeout_ns| { + var timeout_abs = timeout_ns; + if (comptime std.Target.current.isDarwin()) { + var tv: os.darwin.timeval = undefined; + assert(os.darwin.gettimeofday(&tv, null) == 0); + timeout_abs += @intCast(u64, tv.tv_sec) * time.second; + timeout_abs += @intCast(u64, tv.tv_usec) * time.microsecond; + } else { + os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable; + timeout_abs += @intCast(u64, ts.tv_sec) * time.second; + timeout_abs += @intCast(u64, ts.tv_nsec); + } + ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.second)); + ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.second)); + } + + while (!self.is_set) { + const rc = switch (timeout == null) { + true => c.pthread_cond_wait(&self.cond, &self.mutex), + else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts), + }; + switch (rc) { + 0 => {}, + os.ETIMEDOUT => return error.TimedOut, + os.EINVAL => unreachable, + os.EPERM => unreachable, + else => unreachable, + } + } + } +}; + +const AtomicEvent = struct { + waiters: u32, + + const WAKE = 1 << 0; + const WAIT = 1 << 1; + + fn init() AtomicEvent { + return AtomicEvent{ .waiters = 0 }; + } + + fn deinit(self: *AtomicEvent) void { + self.* = undefined; + } + + fn isSet(self: *const AtomicEvent) bool { + return @atomicLoad(u32, &self.waiters, .Acquire) == WAKE; + } + + fn reset(self: *AtomicEvent) void { + @atomicStore(u32, &self.waiters, 0, .Monotonic); + } + + fn set(self: *AtomicEvent) void { + const waiters = @atomicRmw(u32, &self.waiters, .Xchg, WAKE, .Release); + if (waiters >= WAIT) { + return Futex.wake(&self.waiters, waiters >> 1); + } + } + + fn wait(self: *AtomicEvent, timeout: ?u64) !void { + var waiters = @atomicLoad(u32, &self.waiters, .Acquire); + while (waiters != WAKE) { + waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout); + } + } + + pub const Futex = switch (builtin.os) { + .windows => WindowsFutex, + .linux => LinuxFutex, + else => SpinFutex, + }; + + const SpinFutex = struct { + fn wake(waiters: *u32, wake_count: u32) void {} + + fn wait(waiters: *u32, timeout: ?u64) !void { + // TODO: handle platforms where a monotonic timer isnt available + var timer: time.Timer = undefined; + if (timeout != null) + timer = time.Timer.start() catch unreachable; + + while (@atomicLoad(u32, waiters, .Acquire) != WAKE) { + SpinLock.yield(); + if (timeout) |timeout_ns| { + if (timer.read() >= timeout_ns) + return error.TimedOut; + } + } + } + }; + + const LinuxFutex = struct { + fn wake(waiters: *u32, wake_count: u32) void { + const waiting = std.math.maxInt(i32); // wake_count + const ptr = @ptrCast(*const i32, waiters); + const rc = linux.futex_wake(ptr, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, waiting); + assert(linux.getErrno(rc) == 0); + } + + fn wait(waiters: *u32, timeout: ?u64) !void { + var ts: linux.timespec = undefined; + var ts_ptr: ?*linux.timespec = null; + if (timeout) |timeout_ns| { + ts_ptr = &ts; + ts.tv_sec = @intCast(isize, timeout_ns / time.ns_per_s); + ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s); + } + + while (true) { + const waiting = @atomicLoad(u32, waiters, .Acquire); + if (waiting == WAKE) + return; + const expected = @intCast(i32, waiting); + const ptr = @ptrCast(*const i32, waiters); + const rc = linux.futex_wait(ptr, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, expected, ts_ptr); + switch (linux.getErrno(rc)) { + 0 => continue, + os.ETIMEDOUT => return error.TimedOut, + os.EINTR => continue, + os.EAGAIN => return, + else => unreachable, + } + } + } + }; + + const WindowsFutex = struct { + pub fn wake(waiters: *u32, wake_count: u32) void { + const handle = getEventHandle() orelse return SpinFutex.wake(waiters, wake_count); + const key = @ptrCast(*const c_void, waiters); + + var waiting = wake_count; + while (waiting != 0) : (waiting -= 1) { + const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == 0); + } + } + + pub fn wait(waiters: *u32, timeout: ?u64) !void { + const handle = getEventHandle() orelse return SpinFutex.wait(waiters, timeout); + const key = @ptrCast(*const c_void, waiters); + + // NT uses timeouts in units of 100ns with negative value being relative + var timeout_ptr: ?*windows.LARGE_INTEGER = null; + var timeout_value: windows.LARGE_INTEGER = undefined; + if (timeout) |timeout_ns| { + timeout_ptr = &timeout_value; + timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100); + } + + // NtWaitForKeyedEvent doesnt have spurious wake-ups + var rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr); + switch (rc) { + windows.WAIT_TIMEOUT => { + // update the wait count to signal that we're not waiting anymore. + // if the .set() thread already observed that we are, perform a + // matching NtWaitForKeyedEvent so that the .set() thread doesn't + // deadlock trying to run NtReleaseKeyedEvent above. + var waiting = @atomicLoad(u32, waiters, .Monotonic); + while (true) { + if (waiting == WAKE) { + rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == windows.WAIT_OBJECT_0); + break; + } else { + waiting = @cmpxchgWeak(u32, waiters, waiting, waiting - WAIT, .Acquire, .Monotonic) orelse break; + continue; + } + } + return error.TimedOut; + }, + windows.WAIT_OBJECT_0 => {}, + else => unreachable, + } + } + + var event_handle: usize = EMPTY; + const EMPTY = ~@as(usize, 0); + const LOADING = EMPTY - 1; + + pub fn getEventHandle() ?windows.HANDLE { + var handle = @atomicLoad(usize, &event_handle, .Monotonic); + while (true) { + switch (handle) { + EMPTY => handle = @cmpxchgWeak(usize, &event_handle, EMPTY, LOADING, .Acquire, .Monotonic) orelse { + const handle_ptr = @ptrCast(*windows.HANDLE, &handle); + const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; + if (windows.ntdll.NtCreateKeyedEvent(handle_ptr, access_mask, null, 0) != 0) + handle = 0; + @atomicStore(usize, &event_handle, handle, .Monotonic); + return @intToPtr(?windows.HANDLE, handle); + }, + LOADING => { + SpinLock.yield(); + handle = @atomicLoad(usize, &event_handle, .Monotonic); + }, + else => { + return @intToPtr(?windows.HANDLE, handle); + }, + } + } + } + }; +}; + +test "std.ResetEvent" { + var event = ResetEvent.init(); + defer event.deinit(); + + // test event setting + testing.expect(event.isSet() == false); + event.set(); + testing.expect(event.isSet() == true); + + // test event resetting + event.reset(); + testing.expect(event.isSet() == false); + + // test event waiting (non-blocking) + event.set(); + event.wait(); + try event.timedWait(1); + + // test cross-thread signaling + if (builtin.single_threaded) + return; + + const Context = struct { + const Self = @This(); + + value: u128, + in: ResetEvent, + out: ResetEvent, + + fn init() Self { + return Self{ + .value = 0, + .in = ResetEvent.init(), + .out = ResetEvent.init(), + }; + } + + fn deinit(self: *Self) void { + self.in.deinit(); + self.out.deinit(); + self.* = undefined; + } + + fn sender(self: *Self) void { + // update value and signal input + testing.expect(self.value == 0); + self.value = 1; + self.in.set(); + + // wait for receiver to update value and signal output + self.out.wait(); + testing.expect(self.value == 2); + + // update value and signal final input + self.value = 3; + self.in.set(); + } + + fn receiver(self: *Self) void { + // wait for sender to update value and signal input + self.in.wait(); + assert(self.value == 1); + + // update value and signal output + self.in.reset(); + self.value = 2; + self.out.set(); + + // wait for sender to update value and signal final input + self.in.wait(); + assert(self.value == 3); + } + }; + + var context = Context.init(); + defer context.deinit(); + const receiver = try std.Thread.spawn(&context, Context.receiver); + defer receiver.wait(); + context.sender(); +} diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig index 3bbbde782..80e4666b6 100644 --- a/lib/std/segmented_list.zig +++ b/lib/std/segmented_list.zig @@ -112,7 +112,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type .allocator = allocator, .len = 0, .prealloc_segment = undefined, - .dynamic_segments = [_][*]T{}, + .dynamic_segments = &[_][*]T{}, }; } @@ -122,7 +122,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type self.* = undefined; } - pub fn at(self: var, i: usize) AtType(@typeOf(self)) { + pub fn at(self: var, i: usize) AtType(@TypeOf(self)) { assert(i < self.len); return self.uncheckedAt(i); } @@ -162,7 +162,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Grows or shrinks capacity to match usage. pub fn setCapacity(self: *Self, new_capacity: usize) !void { if (prealloc_item_count != 0) { - if (new_capacity <= usize(1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { + if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { return self.shrinkCapacity(new_capacity); } } @@ -192,7 +192,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const len = @intCast(ShelfIndex, self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); - self.dynamic_segments = [_][*]T{}; + self.dynamic_segments = &[_][*]T{}; return; } @@ -213,7 +213,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type self.len = new_len; } - pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) { + pub fn uncheckedAt(self: var, index: usize) AtType(@TypeOf(self)) { if (index < prealloc_item_count) { return &self.prealloc_segment[index]; } @@ -231,9 +231,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type fn shelfSize(shelf_index: ShelfIndex) usize { if (prealloc_item_count == 0) { - return usize(1) << shelf_index; + return @as(usize, 1) << shelf_index; } - return usize(1) << (shelf_index + (prealloc_exp + 1)); + return @as(usize, 1) << (shelf_index + (prealloc_exp + 1)); } fn shelfIndex(list_index: usize) ShelfIndex { @@ -245,9 +245,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { if (prealloc_item_count == 0) { - return (list_index + 1) - (usize(1) << shelf_index); + return (list_index + 1) - (@as(usize, 1) << shelf_index); } - return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index)); + return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); } fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { @@ -339,7 +339,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } test "std.SegmentedList" { - var a = std.heap.direct_allocator; + var a = std.heap.page_allocator; try testSegmentedList(0, a); try testSegmentedList(1, a); @@ -385,18 +385,14 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { testing.expect(list.pop().? == 100); testing.expect(list.len == 99); - try list.pushMany([_]i32{ - 1, - 2, - 3, - }); + try list.pushMany(&[_]i32{ 1, 2, 3 }); testing.expect(list.len == 102); testing.expect(list.pop().? == 3); testing.expect(list.pop().? == 2); testing.expect(list.pop().? == 1); testing.expect(list.len == 99); - try list.pushMany([_]i32{}); + try list.pushMany(&[_]i32{}); testing.expect(list.len == 99); var i: i32 = 99; diff --git a/lib/std/sort.zig b/lib/std/sort.zig index 378a92992..9fa7803cb 100644 --- a/lib/std/sort.zig +++ b/lib/std/sort.zig @@ -813,7 +813,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // where have some idea as to how many unique values there are and where the next value might be fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length() / unique, usize(1)); + const skip = math.max(range.length() / unique, @as(usize, 1)); var index = range.start + skip; while (lessThan(items[index - 1], value)) : (index += skip) { @@ -827,7 +827,7 @@ fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessTh fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length() / unique, usize(1)); + const skip = math.max(range.length() / unique, @as(usize, 1)); var index = range.end - skip; while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) { @@ -841,7 +841,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessT fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length() / unique, usize(1)); + const skip = math.max(range.length() / unique, @as(usize, 1)); var index = range.start + skip; while (!lessThan(value, items[index - 1])) : (index += skip) { @@ -855,7 +855,7 @@ fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessTha fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length() / unique, usize(1)); + const skip = math.max(range.length() / unique, @as(usize, 1)); var index = range.end - skip; while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) { @@ -868,39 +868,35 @@ fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessTh } fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { - var start = range.start; - var end = range.end - 1; + var curr = range.start; + var size = range.length(); if (range.start >= range.end) return range.end; - while (start < end) { - const mid = start + (end - start) / 2; - if (lessThan(items[mid], value)) { - start = mid + 1; - } else { - end = mid; + while (size > 0) { + const offset = size % 2; + + size /= 2; + const mid = items[curr + size]; + if (lessThan(mid, value)) { + curr += size + offset; } } - if (start == range.end - 1 and lessThan(items[start], value)) { - start += 1; - } - return start; + return curr; } fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { - var start = range.start; - var end = range.end - 1; + var curr = range.start; + var size = range.length(); if (range.start >= range.end) return range.end; - while (start < end) { - const mid = start + (end - start) / 2; - if (!lessThan(value, items[mid])) { - start = mid + 1; - } else { - end = mid; + while (size > 0) { + const offset = size % 2; + + size /= 2; + const mid = items[curr + size]; + if (!lessThan(value, mid)) { + curr += size + offset; } } - if (start == range.end - 1 and !lessThan(value, items[start])) { - start += 1; - } - return start; + return curr; } fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void { @@ -1047,27 +1043,27 @@ fn cmpByValue(a: IdAndValue, b: IdAndValue) bool { test "std.sort" { const u8cases = [_][]const []const u8{ - [_][]const u8{ + &[_][]const u8{ "", "", }, - [_][]const u8{ + &[_][]const u8{ "a", "a", }, - [_][]const u8{ + &[_][]const u8{ "az", "az", }, - [_][]const u8{ + &[_][]const u8{ "za", "az", }, - [_][]const u8{ + &[_][]const u8{ "asdf", "adfs", }, - [_][]const u8{ + &[_][]const u8{ "one", "eno", }, @@ -1082,29 +1078,29 @@ test "std.sort" { } const i32cases = [_][]const []const i32{ - [_][]const i32{ - [_]i32{}, - [_]i32{}, + &[_][]const i32{ + &[_]i32{}, + &[_]i32{}, }, - [_][]const i32{ - [_]i32{1}, - [_]i32{1}, + &[_][]const i32{ + &[_]i32{1}, + &[_]i32{1}, }, - [_][]const i32{ - [_]i32{ 0, 1 }, - [_]i32{ 0, 1 }, + &[_][]const i32{ + &[_]i32{ 0, 1 }, + &[_]i32{ 0, 1 }, }, - [_][]const i32{ - [_]i32{ 1, 0 }, - [_]i32{ 0, 1 }, + &[_][]const i32{ + &[_]i32{ 1, 0 }, + &[_]i32{ 0, 1 }, }, - [_][]const i32{ - [_]i32{ 1, -1, 0 }, - [_]i32{ -1, 0, 1 }, + &[_][]const i32{ + &[_]i32{ 1, -1, 0 }, + &[_]i32{ -1, 0, 1 }, }, - [_][]const i32{ - [_]i32{ 2, 1, 3 }, - [_]i32{ 1, 2, 3 }, + &[_][]const i32{ + &[_]i32{ 2, 1, 3 }, + &[_]i32{ 1, 2, 3 }, }, }; @@ -1119,29 +1115,29 @@ test "std.sort" { test "std.sort descending" { const rev_cases = [_][]const []const i32{ - [_][]const i32{ - [_]i32{}, - [_]i32{}, + &[_][]const i32{ + &[_]i32{}, + &[_]i32{}, }, - [_][]const i32{ - [_]i32{1}, - [_]i32{1}, + &[_][]const i32{ + &[_]i32{1}, + &[_]i32{1}, }, - [_][]const i32{ - [_]i32{ 0, 1 }, - [_]i32{ 1, 0 }, + &[_][]const i32{ + &[_]i32{ 0, 1 }, + &[_]i32{ 1, 0 }, }, - [_][]const i32{ - [_]i32{ 1, 0 }, - [_]i32{ 1, 0 }, + &[_][]const i32{ + &[_]i32{ 1, 0 }, + &[_]i32{ 1, 0 }, }, - [_][]const i32{ - [_]i32{ 1, -1, 0 }, - [_]i32{ 1, 0, -1 }, + &[_][]const i32{ + &[_]i32{ 1, -1, 0 }, + &[_]i32{ 1, 0, -1 }, }, - [_][]const i32{ - [_]i32{ 2, 1, 3 }, - [_]i32{ 3, 2, 1 }, + &[_][]const i32{ + &[_]i32{ 2, 1, 3 }, + &[_]i32{ 3, 2, 1 }, }, }; @@ -1158,7 +1154,7 @@ test "another sort case" { var arr = [_]i32{ 5, 3, 1, 2, 4 }; sort(i32, arr[0..], asc(i32)); - testing.expect(mem.eql(i32, arr, [_]i32{ 1, 2, 3, 4, 5 })); + testing.expect(mem.eql(i32, &arr, &[_]i32{ 1, 2, 3, 4, 5 })); } test "sort fuzz testing" { @@ -1193,24 +1189,124 @@ fn fuzzTest(rng: *std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T { - var i: usize = 0; +pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize { + if (items.len == 0) { + return null; + } + var smallest = items[0]; - for (items[1..]) |item| { + var smallest_index: usize = 0; + for (items[1..]) |item, i| { if (lessThan(item, smallest)) { smallest = item; + smallest_index = i + 1; } } - return smallest; + + return smallest_index; } -pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T { - var i: usize = 0; +test "std.sort.argMin" { + testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); + testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); + testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); +} + +pub fn min(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T { + const i = argMin(T, items, lessThan) orelse return null; + return items[i]; +} + +test "std.sort.min" { + testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, asc(i32))); + testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, asc(i32))); + testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); + testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); + testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); + testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); + testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); +} + +pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize { + if (items.len == 0) { + return null; + } + var biggest = items[0]; - for (items[1..]) |item| { + var biggest_index: usize = 0; + for (items[1..]) |item, i| { if (lessThan(biggest, item)) { biggest = item; + biggest_index = i + 1; } } - return biggest; + + return biggest_index; +} + +test "std.sort.argMax" { + testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, asc(i32))); + testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); + testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); + testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); + testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); +} + +pub fn max(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T { + const i = argMax(T, items, lessThan) orelse return null; + return items[i]; +} + +test "std.sort.max" { + testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, asc(i32))); + testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, asc(i32))); + testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); + testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32))); + testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); + testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, asc(i32))); + testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32))); +} + +pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) bool { + var i: usize = 1; + while (i < items.len) : (i += 1) { + if (lessThan(items[i], items[i - 1])) { + return false; + } + } + + return true; +} + +test "std.sort.isSorted" { + testing.expect(isSorted(i32, &[_]i32{}, asc(i32))); + testing.expect(isSorted(i32, &[_]i32{10}, asc(i32))); + testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32))); + testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, asc(i32))); + + testing.expect(isSorted(i32, &[_]i32{}, desc(i32))); + testing.expect(isSorted(i32, &[_]i32{-20}, desc(i32))); + testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, desc(i32))); + testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, desc(i32))); + + testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32))); + testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, desc(i32))); + + testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, asc(i32))); + testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, desc(i32))); + + testing.expect(isSorted(u8, "abcd", asc(u8))); + testing.expect(isSorted(u8, "zyxw", desc(u8))); + + testing.expectEqual(false, isSorted(u8, "abcd", desc(u8))); + testing.expectEqual(false, isSorted(u8, "zyxw", asc(u8))); + + testing.expect(isSorted(u8, "ffff", asc(u8))); + testing.expect(isSorted(u8, "ffff", desc(u8))); } diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index efe3275fe..45dea7946 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -11,90 +11,77 @@ const warn = std.debug.warn; const File = std.fs.File; pub fn main() !void { - var arg_it = process.args(); - // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, // one shot program. We don't need to waste time freeing memory and finding places to squish // bytes into. So we free everything all at once at the very end. - - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = &arena.allocator; + var args = try process.argsAlloc(allocator); + defer process.argsFree(allocator, args); // skip my own exe name - _ = arg_it.skip(); + var arg_idx: usize = 1; - const zig_exe = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected first argument to be path to zig compiler\n"); + const zig_exe = nextArg(args, &arg_idx) orelse { + warn("Expected first argument to be path to zig compiler\n", .{}); return error.InvalidArgs; - }); - const build_root = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected second argument to be build root directory path\n"); + }; + const build_root = nextArg(args, &arg_idx) orelse { + warn("Expected second argument to be build root directory path\n", .{}); return error.InvalidArgs; - }); - const cache_root = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected third argument to be cache root directory path\n"); + }; + const cache_root = nextArg(args, &arg_idx) orelse { + warn("Expected third argument to be cache root directory path\n", .{}); return error.InvalidArgs; - }); + }; const builder = try Builder.create(allocator, zig_exe, build_root, cache_root); defer builder.destroy(); var targets = ArrayList([]const u8).init(allocator); - var stderr_file = io.getStdErr(); - var stderr_file_stream: File.OutStream = undefined; - var stderr_stream = if (stderr_file) |f| x: { - stderr_file_stream = f.outStream(); - break :x &stderr_file_stream.stream; - } else |err| err; + const stderr_stream = &io.getStdErr().outStream().stream; + const stdout_stream = &io.getStdOut().outStream().stream; - var stdout_file = io.getStdOut(); - var stdout_file_stream: File.OutStream = undefined; - var stdout_stream = if (stdout_file) |f| x: { - stdout_file_stream = f.outStream(); - break :x &stdout_file_stream.stream; - } else |err| err; - - while (arg_it.next(allocator)) |err_or_arg| { - const arg = try unwrapArg(err_or_arg); + while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-D")) { const option_contents = arg[2..]; if (option_contents.len == 0) { - warn("Expected option name after '-D'\n\n"); - return usageAndErr(builder, false, try stderr_stream); + warn("Expected option name after '-D'\n\n", .{}); + return usageAndErr(builder, false, stderr_stream); } if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { const option_name = option_contents[0..name_end]; const option_value = option_contents[name_end + 1 ..]; if (try builder.addUserInputOption(option_name, option_value)) - return usageAndErr(builder, false, try stderr_stream); + return usageAndErr(builder, false, stderr_stream); } else { if (try builder.addUserInputFlag(option_contents)) - return usageAndErr(builder, false, try stderr_stream); + return usageAndErr(builder, false, stderr_stream); } } else if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--verbose")) { builder.verbose = true; } else if (mem.eql(u8, arg, "--help")) { - return usage(builder, false, try stdout_stream); + return usage(builder, false, stdout_stream); } else if (mem.eql(u8, arg, "--prefix")) { - builder.install_prefix = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected argument after --prefix\n\n"); - return usageAndErr(builder, false, try stderr_stream); - }); + builder.install_prefix = nextArg(args, &arg_idx) orelse { + warn("Expected argument after --prefix\n\n", .{}); + return usageAndErr(builder, false, stderr_stream); + }; } else if (mem.eql(u8, arg, "--search-prefix")) { - const search_prefix = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected argument after --search-prefix\n\n"); - return usageAndErr(builder, false, try stderr_stream); - }); + const search_prefix = nextArg(args, &arg_idx) orelse { + warn("Expected argument after --search-prefix\n\n", .{}); + return usageAndErr(builder, false, stderr_stream); + }; builder.addSearchPrefix(search_prefix); } else if (mem.eql(u8, arg, "--override-lib-dir")) { - builder.override_lib_dir = try unwrapArg(arg_it.next(allocator) orelse { - warn("Expected argument after --override-lib-dir\n\n"); - return usageAndErr(builder, false, try stderr_stream); - }); + builder.override_lib_dir = nextArg(args, &arg_idx) orelse { + warn("Expected argument after --override-lib-dir\n\n", .{}); + return usageAndErr(builder, false, stderr_stream); + }; } else if (mem.eql(u8, arg, "--verbose-tokenize")) { builder.verbose_tokenize = true; } else if (mem.eql(u8, arg, "--verbose-ast")) { @@ -109,9 +96,12 @@ pub fn main() !void { builder.verbose_cimport = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { builder.verbose_cc = true; + } else if (mem.eql(u8, arg, "--")) { + builder.args = argsRest(args, arg_idx); + break; } else { - warn("Unrecognized argument: {}\n\n", arg); - return usageAndErr(builder, false, try stderr_stream); + warn("Unrecognized argument: {}\n\n", .{arg}); + return usageAndErr(builder, false, stderr_stream); } } else { try targets.append(arg); @@ -122,12 +112,12 @@ pub fn main() !void { try runBuild(builder); if (builder.validateUserInputDidItFail()) - return usageAndErr(builder, true, try stderr_stream); + return usageAndErr(builder, true, stderr_stream); builder.make(targets.toSliceConst()) catch |err| { switch (err) { error.InvalidStepName => { - return usageAndErr(builder, true, try stderr_stream); + return usageAndErr(builder, true, stderr_stream); }, error.UncleanExit => process.exit(1), else => return err, @@ -136,7 +126,7 @@ pub fn main() !void { } fn runBuild(builder: *Builder) anyerror!void { - switch (@typeId(@typeOf(root.build).ReturnType)) { + switch (@typeId(@TypeOf(root.build).ReturnType)) { .Void => root.build(builder), .ErrorUnion => try root.build(builder), else => @compileError("expected return type of build to be 'void' or '!void'"), @@ -156,15 +146,15 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { \\ \\Steps: \\ - , builder.zig_exe); + , .{builder.zig_exe}); const allocator = builder.allocator; for (builder.top_level_steps.toSliceConst()) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) - try fmt.allocPrint(allocator, "{} (default)", top_level_step.step.name) + try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) else top_level_step.step.name; - try out_stream.print(" {s:22} {}\n", name, top_level_step.description); + try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description }); } try out_stream.write( @@ -180,12 +170,15 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { ); if (builder.available_options_list.len == 0) { - try out_stream.print(" (none)\n"); + try out_stream.print(" (none)\n", .{}); } else { for (builder.available_options_list.toSliceConst()) |option| { - const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); + const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{ + option.name, + Builder.typeIdName(option.type_id), + }); defer allocator.free(name); - try out_stream.print("{s:24} {}\n", name, option.description); + try out_stream.print("{s:24} {}\n", .{ name, option.description }); } } @@ -211,11 +204,13 @@ fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void process.exit(1); } -const UnwrapArgError = error{OutOfMemory}; - -fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { - return arg catch |err| { - warn("Unable to parse command line: {}\n", err); - return err; - }; +fn nextArg(args: [][]const u8, idx: *usize) ?[]const u8 { + if (idx.* >= args.len) return null; + defer idx.* += 1; + return args[idx.*]; +} + +fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { + if (idx >= args.len) return null; + return args[idx..]; } diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index b2fca86db..0a911ba52 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -7,6 +7,7 @@ const std = @import("std"); const builtin = @import("builtin"); const maxInt = std.math.maxInt; +const isNan = std.math.isNan; const is_wasm = switch (builtin.arch) { .wasm32, .wasm64 => true, @@ -22,36 +23,34 @@ const is_freestanding = switch (builtin.os) { }; comptime { if (is_freestanding and is_wasm and builtin.link_libc) { - @export("_start", wasm_start, .Strong); + @export(wasm_start, .{ .name = "_start", .linkage = .Strong }); } if (builtin.link_libc) { - @export("strcmp", strcmp, .Strong); - @export("strncmp", strncmp, .Strong); - @export("strerror", strerror, .Strong); - @export("strlen", strlen, .Strong); + @export(strcmp, .{ .name = "strcmp", .linkage = .Strong }); + @export(strncmp, .{ .name = "strncmp", .linkage = .Strong }); + @export(strerror, .{ .name = "strerror", .linkage = .Strong }); + @export(strlen, .{ .name = "strlen", .linkage = .Strong }); } else if (is_msvc) { - @export("_fltused", _fltused, .Strong); - } else if (builtin.arch == builtin.Arch.arm and builtin.os == .linux) { - @export("__aeabi_read_tp", std.os.linux.getThreadPointer, .Strong); + @export(_fltused, .{ .name = "_fltused", .linkage = .Strong }); } } extern var _fltused: c_int = 1; -extern fn main(argc: c_int, argv: [*][*]u8) c_int; -extern fn wasm_start() void { +extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; +fn wasm_start() callconv(.C) void { _ = main(0, undefined); } -extern fn strcmp(s1: [*]const u8, s2: [*]const u8) c_int { +fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { return std.cstr.cmp(s1, s2); } -extern fn strlen(s: [*]const u8) usize { +fn strlen(s: [*:0]const u8) callconv(.C) usize { return std.mem.len(u8, s); } -extern fn strncmp(_l: [*]const u8, _r: [*]const u8, _n: usize) c_int { +fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int { if (_n == 0) return 0; var l = _l; var r = _r; @@ -61,18 +60,18 @@ extern fn strncmp(_l: [*]const u8, _r: [*]const u8, _n: usize) c_int { r += 1; n -= 1; } - return c_int(l[0]) - c_int(r[0]); + return @as(c_int, l[0]) - @as(c_int, r[0]); } -extern fn strerror(errnum: c_int) [*]const u8 { - return c"TODO strerror implementation"; +fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 { + return "TODO strerror implementation"; } test "strncmp" { - std.testing.expect(strncmp(c"a", c"b", 1) == -1); - std.testing.expect(strncmp(c"a", c"c", 1) == -2); - std.testing.expect(strncmp(c"b", c"a", 1) == 1); - std.testing.expect(strncmp(c"\xff", c"\x02", 1) == 253); + std.testing.expect(strncmp("a", "b", 1) == -1); + std.testing.expect(strncmp("a", "c", 1) == -2); + std.testing.expect(strncmp("b", "a", 1) == 1); + std.testing.expect(strncmp("\xff", "\x02", 1) == 253); } // Avoid dragging in the runtime safety mechanisms into this .o file, @@ -80,10 +79,12 @@ test "strncmp" { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { if (builtin.is_test) { @setCold(true); - std.debug.panic("{}", msg); - } else { - unreachable; + std.debug.panic("{}", .{msg}); } + if (builtin.os != .freestanding and builtin.os != .other) { + std.os.abort(); + } + while (true) {} } export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { @@ -179,21 +180,64 @@ comptime { builtin.mode != builtin.Mode.ReleaseSmall and builtin.os != builtin.Os.windows) { - @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong); + @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" }); } if (builtin.os == builtin.Os.linux) { - @export("clone", clone, builtin.GlobalLinkage.Strong); + @export(clone, .{ .name = "clone" }); } } -extern fn __stack_chk_fail() noreturn { +fn __stack_chk_fail() callconv(.C) noreturn { @panic("stack smashing detected"); } // TODO we should be able to put this directly in std/linux/x86_64.zig but // it causes a segfault in release mode. this is a workaround of calling it // across .o file boundaries. fix comptime @ptrCast of nakedcc functions. -nakedcc fn clone() void { +fn clone() callconv(.Naked) void { switch (builtin.arch) { + .i386 => { + // __clone(func, stack, flags, arg, ptid, tls, ctid) + // +8, +12, +16, +20, +24, +28, +32 + // syscall(SYS_clone, flags, stack, ptid, tls, ctid) + // eax, ebx, ecx, edx, esi, edi + asm volatile ( + \\ push %%ebp + \\ mov %%esp,%%ebp + \\ push %%ebx + \\ push %%esi + \\ push %%edi + \\ // Setup the arguments + \\ mov 16(%%ebp),%%ebx + \\ mov 12(%%ebp),%%ecx + \\ and $-16,%%ecx + \\ sub $20,%%ecx + \\ mov 20(%%ebp),%%eax + \\ mov %%eax,4(%%ecx) + \\ mov 8(%%ebp),%%eax + \\ mov %%eax,0(%%ecx) + \\ mov 24(%%ebp),%%edx + \\ mov 28(%%ebp),%%esi + \\ mov 32(%%ebp),%%edi + \\ mov $120,%%eax + \\ int $128 + \\ test %%eax,%%eax + \\ jnz 1f + \\ pop %%eax + \\ xor %%ebp,%%ebp + \\ call *%%eax + \\ mov %%eax,%%ebx + \\ xor %%eax,%%eax + \\ inc %%eax + \\ int $128 + \\ hlt + \\1: + \\ pop %%edi + \\ pop %%esi + \\ pop %%ebx + \\ pop %%ebp + \\ ret + ); + }, .x86_64 => { asm volatile ( \\ xor %%eax,%%eax @@ -480,7 +524,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); var i: uint = undefined; - if (uy << 1 == 0 or isNan(uint, uy) or ex == mask) + if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask) return (x * y) / (x * y); if (ux << 1 <= uy << 1) { @@ -537,7 +581,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { // scale result up if (ex > 0) { ux -%= 1 << digits; - ux |= uint(@bitCast(u32, ex)) << digits; + ux |= @as(uint, @bitCast(u32, ex)) << digits; } else { ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); } @@ -549,18 +593,6 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { return @bitCast(T, ux); } -fn isNan(comptime T: type, bits: T) bool { - if (T == u16) { - return (bits & 0x7fff) > 0x7c00; - } else if (T == u32) { - return (bits & 0x7fffffff) > 0x7f800000; - } else if (T == u64) { - return (bits & (maxInt(u64) >> 1)) > (u64(0x7ff) << 52); - } else { - unreachable; - } -} - // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. @@ -694,9 +726,31 @@ export fn sqrt(x: f64) f64 { return @bitCast(f64, uz); } +test "sqrt" { + const epsilon = 0.000001; + + std.testing.expect(sqrt(0.0) == 0.0); + std.testing.expect(std.math.approxEq(f64, sqrt(2.0), 1.414214, epsilon)); + std.testing.expect(std.math.approxEq(f64, sqrt(3.6), 1.897367, epsilon)); + std.testing.expect(sqrt(4.0) == 2.0); + std.testing.expect(std.math.approxEq(f64, sqrt(7.539840), 2.745877, epsilon)); + std.testing.expect(std.math.approxEq(f64, sqrt(19.230934), 4.385309, epsilon)); + std.testing.expect(sqrt(64.0) == 8.0); + std.testing.expect(std.math.approxEq(f64, sqrt(64.1), 8.006248, epsilon)); + std.testing.expect(std.math.approxEq(f64, sqrt(8942.230469), 94.563367, epsilon)); +} + +test "sqrt special" { + std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); + std.testing.expect(sqrt(0.0) == 0.0); + std.testing.expect(sqrt(-0.0) == -0.0); + std.testing.expect(std.math.isNan(sqrt(-1.0))); + std.testing.expect(std.math.isNan(sqrt(std.math.nan(f64)))); +} + export fn sqrtf(x: f32) f32 { const tiny: f32 = 1.0e-30; - const sign: i32 = @bitCast(i32, u32(0x80000000)); + const sign: i32 = @bitCast(i32, @as(u32, 0x80000000)); var ix: i32 = @bitCast(i32, x); if ((ix & 0x7F800000) == 0x7F800000) { @@ -769,3 +823,25 @@ export fn sqrtf(x: f32) f32 { ix += m << 23; return @bitCast(f32, ix); } + +test "sqrtf" { + const epsilon = 0.000001; + + std.testing.expect(sqrtf(0.0) == 0.0); + std.testing.expect(std.math.approxEq(f32, sqrtf(2.0), 1.414214, epsilon)); + std.testing.expect(std.math.approxEq(f32, sqrtf(3.6), 1.897367, epsilon)); + std.testing.expect(sqrtf(4.0) == 2.0); + std.testing.expect(std.math.approxEq(f32, sqrtf(7.539840), 2.745877, epsilon)); + std.testing.expect(std.math.approxEq(f32, sqrtf(19.230934), 4.385309, epsilon)); + std.testing.expect(sqrtf(64.0) == 8.0); + std.testing.expect(std.math.approxEq(f32, sqrtf(64.1), 8.006248, epsilon)); + std.testing.expect(std.math.approxEq(f32, sqrtf(8942.230469), 94.563370, epsilon)); +} + +test "sqrtf special" { + std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); + std.testing.expect(sqrtf(0.0) == 0.0); + std.testing.expect(sqrtf(-0.0) == -0.0); + std.testing.expect(std.math.isNan(sqrtf(-1.0))); + std.testing.expect(std.math.isNan(sqrtf(std.math.nan(f32)))); +} diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 638a9bb60..212888354 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -12,290 +12,305 @@ comptime { const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong; switch (builtin.arch) { - .i386, .x86_64 => @export("__zig_probe_stack", @import("compiler_rt/stack_probe.zig").zig_probe_stack, linkage), + .i386, .x86_64 => @export(@import("compiler_rt/stack_probe.zig").zig_probe_stack, .{ .name = "__zig_probe_stack", .linkage = linkage }), else => {}, } - @export("__lesf2", @import("compiler_rt/comparesf2.zig").__lesf2, linkage); - @export("__ledf2", @import("compiler_rt/comparedf2.zig").__ledf2, linkage); - @export("__letf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__lesf2, .{ .name = "__lesf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__ledf2, .{ .name = "__ledf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__letf2", .linkage = linkage }); - @export("__gesf2", @import("compiler_rt/comparesf2.zig").__gesf2, linkage); - @export("__gedf2", @import("compiler_rt/comparedf2.zig").__gedf2, linkage); - @export("__getf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__gesf2, .{ .name = "__gesf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__gedf2, .{ .name = "__gedf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__getf2, .{ .name = "__getf2", .linkage = linkage }); if (!is_test) { - @export("__cmpsf2", @import("compiler_rt/comparesf2.zig").__lesf2, linkage); - @export("__cmpdf2", @import("compiler_rt/comparedf2.zig").__ledf2, linkage); - @export("__cmptf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__cmptf2", .linkage = linkage }); - @export("__eqsf2", @import("compiler_rt/comparesf2.zig").__eqsf2, linkage); - @export("__eqdf2", @import("compiler_rt/comparedf2.zig").__eqdf2, linkage); - @export("__eqtf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__eqtf2", .linkage = linkage }); - @export("__ltsf2", @import("compiler_rt/comparesf2.zig").__ltsf2, linkage); - @export("__ltdf2", @import("compiler_rt/comparedf2.zig").__ltdf2, linkage); - @export("__lttf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__lttf2", .linkage = linkage }); - @export("__nesf2", @import("compiler_rt/comparesf2.zig").__nesf2, linkage); - @export("__nedf2", @import("compiler_rt/comparedf2.zig").__nedf2, linkage); - @export("__netf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__nesf2, .{ .name = "__nesf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__nedf2, .{ .name = "__nedf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__letf2, .{ .name = "__netf2", .linkage = linkage }); - @export("__gtsf2", @import("compiler_rt/comparesf2.zig").__gtsf2, linkage); - @export("__gtdf2", @import("compiler_rt/comparedf2.zig").__gtdf2, linkage); - @export("__gttf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__getf2, .{ .name = "__gttf2", .linkage = linkage }); - @export("__gnu_h2f_ieee", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); - @export("__gnu_f2h_ieee", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); + @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); } - @export("__unordsf2", @import("compiler_rt/comparesf2.zig").__unordsf2, linkage); - @export("__unorddf2", @import("compiler_rt/comparedf2.zig").__unorddf2, linkage); - @export("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage); + @export(@import("compiler_rt/comparesf2.zig").__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); + @export(@import("compiler_rt/comparetf2.zig").__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); - @export("__addsf3", @import("compiler_rt/addXf3.zig").__addsf3, linkage); - @export("__adddf3", @import("compiler_rt/addXf3.zig").__adddf3, linkage); - @export("__addtf3", @import("compiler_rt/addXf3.zig").__addtf3, linkage); - @export("__subsf3", @import("compiler_rt/addXf3.zig").__subsf3, linkage); - @export("__subdf3", @import("compiler_rt/addXf3.zig").__subdf3, linkage); - @export("__subtf3", @import("compiler_rt/addXf3.zig").__subtf3, linkage); + @export(@import("compiler_rt/addXf3.zig").__addsf3, .{ .name = "__addsf3", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__adddf3, .{ .name = "__adddf3", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__addtf3, .{ .name = "__addtf3", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__subsf3, .{ .name = "__subsf3", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__subdf3, .{ .name = "__subdf3", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__subtf3, .{ .name = "__subtf3", .linkage = linkage }); - @export("__mulsf3", @import("compiler_rt/mulXf3.zig").__mulsf3, linkage); - @export("__muldf3", @import("compiler_rt/mulXf3.zig").__muldf3, linkage); - @export("__multf3", @import("compiler_rt/mulXf3.zig").__multf3, linkage); + @export(@import("compiler_rt/mulXf3.zig").__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); + @export(@import("compiler_rt/mulXf3.zig").__muldf3, .{ .name = "__muldf3", .linkage = linkage }); + @export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__multf3", .linkage = linkage }); - @export("__divsf3", @import("compiler_rt/divsf3.zig").__divsf3, linkage); - @export("__divdf3", @import("compiler_rt/divdf3.zig").__divdf3, linkage); + @export(@import("compiler_rt/divsf3.zig").__divsf3, .{ .name = "__divsf3", .linkage = linkage }); + @export(@import("compiler_rt/divdf3.zig").__divdf3, .{ .name = "__divdf3", .linkage = linkage }); - @export("__ashlti3", @import("compiler_rt/ashlti3.zig").__ashlti3, linkage); - @export("__lshrti3", @import("compiler_rt/lshrti3.zig").__lshrti3, linkage); - @export("__ashrti3", @import("compiler_rt/ashrti3.zig").__ashrti3, linkage); + @export(@import("compiler_rt/ashlti3.zig").__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); + @export(@import("compiler_rt/lshrti3.zig").__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); + @export(@import("compiler_rt/ashrti3.zig").__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); - @export("__floatsidf", @import("compiler_rt/floatsiXf.zig").__floatsidf, linkage); - @export("__floatsisf", @import("compiler_rt/floatsiXf.zig").__floatsisf, linkage); - @export("__floatdidf", @import("compiler_rt/floatdidf.zig").__floatdidf, linkage); - @export("__floatsitf", @import("compiler_rt/floatsiXf.zig").__floatsitf, linkage); - @export("__floatunsidf", @import("compiler_rt/floatunsidf.zig").__floatunsidf, linkage); - @export("__floatundidf", @import("compiler_rt/floatundidf.zig").__floatundidf, linkage); + @export(@import("compiler_rt/floatsiXf.zig").__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); + @export(@import("compiler_rt/floatsiXf.zig").__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); + @export(@import("compiler_rt/floatdidf.zig").__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); + @export(@import("compiler_rt/floatsiXf.zig").__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); - @export("__floattitf", @import("compiler_rt/floattitf.zig").__floattitf, linkage); - @export("__floattidf", @import("compiler_rt/floattidf.zig").__floattidf, linkage); - @export("__floattisf", @import("compiler_rt/floattisf.zig").__floattisf, linkage); + @export(@import("compiler_rt/floatunsisf.zig").__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); + @export(@import("compiler_rt/floatundisf.zig").__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); + @export(@import("compiler_rt/floatunsidf.zig").__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); + @export(@import("compiler_rt/floatundidf.zig").__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); - @export("__floatunditf", @import("compiler_rt/floatunditf.zig").__floatunditf, linkage); - @export("__floatunsitf", @import("compiler_rt/floatunsitf.zig").__floatunsitf, linkage); + @export(@import("compiler_rt/floattitf.zig").__floattitf, .{ .name = "__floattitf", .linkage = linkage }); + @export(@import("compiler_rt/floattidf.zig").__floattidf, .{ .name = "__floattidf", .linkage = linkage }); + @export(@import("compiler_rt/floattisf.zig").__floattisf, .{ .name = "__floattisf", .linkage = linkage }); - @export("__floatuntitf", @import("compiler_rt/floatuntitf.zig").__floatuntitf, linkage); - @export("__floatuntidf", @import("compiler_rt/floatuntidf.zig").__floatuntidf, linkage); - @export("__floatuntisf", @import("compiler_rt/floatuntisf.zig").__floatuntisf, linkage); + @export(@import("compiler_rt/floatunditf.zig").__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); + @export(@import("compiler_rt/floatunsitf.zig").__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - @export("__extenddftf2", @import("compiler_rt/extendXfYf2.zig").__extenddftf2, linkage); - @export("__extendsftf2", @import("compiler_rt/extendXfYf2.zig").__extendsftf2, linkage); - @export("__extendhfsf2", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); + @export(@import("compiler_rt/floatuntitf.zig").__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); + @export(@import("compiler_rt/floatuntidf.zig").__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); + @export(@import("compiler_rt/floatuntisf.zig").__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); - @export("__truncsfhf2", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); - @export("__truncdfhf2", @import("compiler_rt/truncXfYf2.zig").__truncdfhf2, linkage); - @export("__trunctfdf2", @import("compiler_rt/truncXfYf2.zig").__trunctfdf2, linkage); - @export("__trunctfsf2", @import("compiler_rt/truncXfYf2.zig").__trunctfsf2, linkage); + @export(@import("compiler_rt/extendXfYf2.zig").__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); + @export(@import("compiler_rt/extendXfYf2.zig").__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); + @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); - @export("__truncdfsf2", @import("compiler_rt/truncXfYf2.zig").__truncdfsf2, linkage); + @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); - @export("__extendsfdf2", @import("compiler_rt/extendXfYf2.zig").__extendsfdf2, linkage); + @export(@import("compiler_rt/truncXfYf2.zig").__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - @export("__fixunssfsi", @import("compiler_rt/fixunssfsi.zig").__fixunssfsi, linkage); - @export("__fixunssfdi", @import("compiler_rt/fixunssfdi.zig").__fixunssfdi, linkage); - @export("__fixunssfti", @import("compiler_rt/fixunssfti.zig").__fixunssfti, linkage); + @export(@import("compiler_rt/extendXfYf2.zig").__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); - @export("__fixunsdfsi", @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, linkage); - @export("__fixunsdfdi", @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, linkage); - @export("__fixunsdfti", @import("compiler_rt/fixunsdfti.zig").__fixunsdfti, linkage); + @export(@import("compiler_rt/fixunssfsi.zig").__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixunssfdi.zig").__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixunssfti.zig").__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - @export("__fixunstfsi", @import("compiler_rt/fixunstfsi.zig").__fixunstfsi, linkage); - @export("__fixunstfdi", @import("compiler_rt/fixunstfdi.zig").__fixunstfdi, linkage); - @export("__fixunstfti", @import("compiler_rt/fixunstfti.zig").__fixunstfti, linkage); + @export(@import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixunsdfti.zig").__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - @export("__fixdfdi", @import("compiler_rt/fixdfdi.zig").__fixdfdi, linkage); - @export("__fixdfsi", @import("compiler_rt/fixdfsi.zig").__fixdfsi, linkage); - @export("__fixdfti", @import("compiler_rt/fixdfti.zig").__fixdfti, linkage); - @export("__fixsfdi", @import("compiler_rt/fixsfdi.zig").__fixsfdi, linkage); - @export("__fixsfsi", @import("compiler_rt/fixsfsi.zig").__fixsfsi, linkage); - @export("__fixsfti", @import("compiler_rt/fixsfti.zig").__fixsfti, linkage); - @export("__fixtfdi", @import("compiler_rt/fixtfdi.zig").__fixtfdi, linkage); - @export("__fixtfsi", @import("compiler_rt/fixtfsi.zig").__fixtfsi, linkage); - @export("__fixtfti", @import("compiler_rt/fixtfti.zig").__fixtfti, linkage); + @export(@import("compiler_rt/fixunstfsi.zig").__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixunstfdi.zig").__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixunstfti.zig").__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); - @export("__udivmoddi4", @import("compiler_rt/udivmoddi4.zig").__udivmoddi4, linkage); - @export("__popcountdi2", @import("compiler_rt/popcountdi2.zig").__popcountdi2, linkage); + @export(@import("compiler_rt/fixdfdi.zig").__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixdfsi.zig").__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixdfti.zig").__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); + @export(@import("compiler_rt/fixsfdi.zig").__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixsfsi.zig").__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixsfti.zig").__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); + @export(@import("compiler_rt/fixtfdi.zig").__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); + @export(@import("compiler_rt/fixtfsi.zig").__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); + @export(@import("compiler_rt/fixtfti.zig").__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); - @export("__muldi3", @import("compiler_rt/muldi3.zig").__muldi3, linkage); - @export("__divmoddi4", __divmoddi4, linkage); - @export("__divsi3", __divsi3, linkage); - @export("__divdi3", __divdi3, linkage); - @export("__udivsi3", __udivsi3, linkage); - @export("__udivdi3", __udivdi3, linkage); - @export("__modsi3", __modsi3, linkage); - @export("__moddi3", __moddi3, linkage); - @export("__umodsi3", __umodsi3, linkage); - @export("__umoddi3", __umoddi3, linkage); - @export("__divmodsi4", __divmodsi4, linkage); - @export("__udivmodsi4", __udivmodsi4, linkage); + @export(@import("compiler_rt/int.zig").__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); + @export(@import("compiler_rt/popcountdi2.zig").__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); - @export("__negsf2", @import("compiler_rt/negXf2.zig").__negsf2, linkage); - @export("__negdf2", @import("compiler_rt/negXf2.zig").__negdf2, linkage); + @export(@import("compiler_rt/muldi3.zig").__muldi3, .{ .name = "__muldi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__divsi3, .{ .name = "__divsi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__divdi3, .{ .name = "__divdi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__modsi3, .{ .name = "__modsi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__moddi3, .{ .name = "__moddi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); + + @export(@import("compiler_rt/negXf2.zig").__negsf2, .{ .name = "__negsf2", .linkage = linkage }); + @export(@import("compiler_rt/negXf2.zig").__negdf2, .{ .name = "__negdf2", .linkage = linkage }); if (is_arm_arch and !is_arm_64 and !is_test) { - @export("__aeabi_unwind_cpp_pr0", __aeabi_unwind_cpp_pr0, strong_linkage); - @export("__aeabi_unwind_cpp_pr1", __aeabi_unwind_cpp_pr1, linkage); - @export("__aeabi_unwind_cpp_pr2", __aeabi_unwind_cpp_pr2, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = strong_linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); - @export("__aeabi_lmul", @import("compiler_rt/muldi3.zig").__muldi3, linkage); + @export(@import("compiler_rt/muldi3.zig").__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); - @export("__aeabi_ldivmod", __aeabi_ldivmod, linkage); - @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); - @export("__aeabi_idiv", __divsi3, linkage); - @export("__aeabi_idivmod", __aeabi_idivmod, linkage); - @export("__aeabi_uidiv", __udivsi3, linkage); - @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); + @export(@import("compiler_rt/int.zig").__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); + @export(@import("compiler_rt/int.zig").__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); - @export("__aeabi_memcpy", __aeabi_memcpy, linkage); - @export("__aeabi_memcpy4", __aeabi_memcpy, linkage); - @export("__aeabi_memcpy8", __aeabi_memcpy, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); - @export("__aeabi_memmove", __aeabi_memmove, linkage); - @export("__aeabi_memmove4", __aeabi_memmove, linkage); - @export("__aeabi_memmove8", __aeabi_memmove, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); - @export("__aeabi_memset", __aeabi_memset, linkage); - @export("__aeabi_memset4", __aeabi_memset, linkage); - @export("__aeabi_memset8", __aeabi_memset, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); - @export("__aeabi_memclr", __aeabi_memclr, linkage); - @export("__aeabi_memclr4", __aeabi_memclr, linkage); - @export("__aeabi_memclr8", __aeabi_memclr, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); + @export(@import("compiler_rt/arm.zig").__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); - @export("__aeabi_memcmp", __aeabi_memcmp, linkage); - @export("__aeabi_memcmp4", __aeabi_memcmp, linkage); - @export("__aeabi_memcmp8", __aeabi_memcmp, linkage); + @export(@import("compiler_rt/arm.zig").__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); - @export("__aeabi_f2d", @import("compiler_rt/extendXfYf2.zig").__extendsfdf2, linkage); - @export("__aeabi_i2d", @import("compiler_rt/floatsiXf.zig").__floatsidf, linkage); - @export("__aeabi_l2d", @import("compiler_rt/floatdidf.zig").__floatdidf, linkage); - @export("__aeabi_ui2d", @import("compiler_rt/floatunsidf.zig").__floatunsidf, linkage); - @export("__aeabi_ul2d", @import("compiler_rt/floatundidf.zig").__floatundidf, linkage); + @export(@import("compiler_rt/extendXfYf2.zig").__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); + @export(@import("compiler_rt/floatsiXf.zig").__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); + @export(@import("compiler_rt/floatdidf.zig").__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); + @export(@import("compiler_rt/floatunsidf.zig").__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); + @export(@import("compiler_rt/floatundidf.zig").__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); + @export(@import("compiler_rt/floatunsisf.zig").__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); + @export(@import("compiler_rt/floatundisf.zig").__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); - @export("__aeabi_fneg", @import("compiler_rt/negXf2.zig").__negsf2, linkage); - @export("__aeabi_dneg", @import("compiler_rt/negXf2.zig").__negdf2, linkage); + @export(@import("compiler_rt/negXf2.zig").__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); + @export(@import("compiler_rt/negXf2.zig").__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); - @export("__aeabi_fmul", @import("compiler_rt/mulXf3.zig").__mulsf3, linkage); - @export("__aeabi_dmul", @import("compiler_rt/mulXf3.zig").__muldf3, linkage); + @export(@import("compiler_rt/mulXf3.zig").__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); + @export(@import("compiler_rt/mulXf3.zig").__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); - @export("__aeabi_d2h", @import("compiler_rt/truncXfYf2.zig").__truncdfhf2, linkage); + @export(@import("compiler_rt/truncXfYf2.zig").__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); - @export("__aeabi_f2ulz", @import("compiler_rt/fixunssfdi.zig").__fixunssfdi, linkage); - @export("__aeabi_d2ulz", @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, linkage); + @export(@import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); + @export(@import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - @export("__aeabi_f2lz", @import("compiler_rt/fixsfdi.zig").__fixsfdi, linkage); - @export("__aeabi_d2lz", @import("compiler_rt/fixdfdi.zig").__fixdfdi, linkage); + @export(@import("compiler_rt/fixsfdi.zig").__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); + @export(@import("compiler_rt/fixdfdi.zig").__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - @export("__aeabi_d2uiz", @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, linkage); + @export(@import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); - @export("__aeabi_h2f", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); - @export("__aeabi_f2h", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); + @export(@import("compiler_rt/extendXfYf2.zig").__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); - @export("__aeabi_i2f", @import("compiler_rt/floatsiXf.zig").__floatsisf, linkage); - @export("__aeabi_d2f", @import("compiler_rt/truncXfYf2.zig").__truncdfsf2, linkage); + @export(@import("compiler_rt/floatsiXf.zig").__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); - @export("__aeabi_fadd", @import("compiler_rt/addXf3.zig").__addsf3, linkage); - @export("__aeabi_dadd", @import("compiler_rt/addXf3.zig").__adddf3, linkage); - @export("__aeabi_fsub", @import("compiler_rt/addXf3.zig").__subsf3, linkage); - @export("__aeabi_dsub", @import("compiler_rt/addXf3.zig").__subdf3, linkage); + @export(@import("compiler_rt/addXf3.zig").__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); + @export(@import("compiler_rt/addXf3.zig").__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); - @export("__aeabi_f2uiz", @import("compiler_rt/fixunssfsi.zig").__fixunssfsi, linkage); + @export(@import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - @export("__aeabi_f2iz", @import("compiler_rt/fixsfsi.zig").__fixsfsi, linkage); - @export("__aeabi_d2iz", @import("compiler_rt/fixdfsi.zig").__fixdfsi, linkage); + @export(@import("compiler_rt/fixsfsi.zig").__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); + @export(@import("compiler_rt/fixdfsi.zig").__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); - @export("__aeabi_fdiv", @import("compiler_rt/divsf3.zig").__divsf3, linkage); - @export("__aeabi_ddiv", @import("compiler_rt/divdf3.zig").__divdf3, linkage); + @export(@import("compiler_rt/divsf3.zig").__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); + @export(@import("compiler_rt/divdf3.zig").__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); - @export("__aeabi_fcmpeq", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpeq, linkage); - @export("__aeabi_fcmplt", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmplt, linkage); - @export("__aeabi_fcmple", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmple, linkage); - @export("__aeabi_fcmpge", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpge, linkage); - @export("__aeabi_fcmpgt", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpgt, linkage); - @export("__aeabi_fcmpun", @import("compiler_rt/comparesf2.zig").__unordsf2, linkage); + @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); + @export(@import("compiler_rt/comparesf2.zig").__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); - @export("__aeabi_dcmpeq", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpeq, linkage); - @export("__aeabi_dcmplt", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmplt, linkage); - @export("__aeabi_dcmple", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmple, linkage); - @export("__aeabi_dcmpge", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpge, linkage); - @export("__aeabi_dcmpgt", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpgt, linkage); - @export("__aeabi_dcmpun", @import("compiler_rt/comparedf2.zig").__unorddf2, linkage); + @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); + @export(@import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); + @export(@import("compiler_rt/comparedf2.zig").__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); } if (builtin.os == .windows) { - if (!builtin.link_libc) { - @export("_chkstk", @import("compiler_rt/stack_probe.zig")._chkstk, strong_linkage); - @export("__chkstk", @import("compiler_rt/stack_probe.zig").__chkstk, strong_linkage); - @export("___chkstk", @import("compiler_rt/stack_probe.zig").___chkstk, strong_linkage); - @export("__chkstk_ms", @import("compiler_rt/stack_probe.zig").__chkstk_ms, strong_linkage); - @export("___chkstk_ms", @import("compiler_rt/stack_probe.zig").___chkstk_ms, strong_linkage); - } else if (is_mingw) { - @export("___chkstk_ms", @import("compiler_rt/stack_probe.zig").___chkstk_ms, strong_linkage); - @export("__stack_chk_fail", __stack_chk_fail, strong_linkage); - @export("__stack_chk_guard", __stack_chk_guard, strong_linkage); + // Default stack-probe functions emitted by LLVM + if (is_mingw) { + @export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); + @export(@import("compiler_rt/stack_probe.zig").___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage }); + } else if (!builtin.link_libc) { + // This symbols are otherwise exported by MSVCRT.lib + @export(@import("compiler_rt/stack_probe.zig")._chkstk, .{ .name = "_chkstk", .linkage = strong_linkage }); + @export(@import("compiler_rt/stack_probe.zig").__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); + } + + if (is_mingw) { + @export(__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = strong_linkage }); + @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = strong_linkage }); } switch (builtin.arch) { .i386 => { - @export("_aulldiv", @import("compiler_rt/aulldiv.zig")._aulldiv, strong_linkage); - @export("_aullrem", @import("compiler_rt/aullrem.zig")._aullrem, strong_linkage); + // Don't let LLVM apply the stdcall name mangling on those MSVC + // builtin functions + @export(@import("compiler_rt/aulldiv.zig")._alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); + @export(@import("compiler_rt/aulldiv.zig")._aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); + @export(@import("compiler_rt/aullrem.zig")._allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); + @export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); + + @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); + @export(@import("compiler_rt/modti3.zig").__modti3, .{ .name = "__modti3", .linkage = linkage }); + @export(@import("compiler_rt/multi3.zig").__multi3, .{ .name = "__multi3", .linkage = linkage }); + @export(@import("compiler_rt/udivti3.zig").__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + @export(@import("compiler_rt/udivmodti4.zig").__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(@import("compiler_rt/umodti3.zig").__umodti3, .{ .name = "__umodti3", .linkage = linkage }); }, .x86_64 => { // The "ti" functions must use @Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage); - @export("__modti3", @import("compiler_rt/modti3.zig").__modti3_windows_x86_64, linkage); - @export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage); - @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage); - @export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); - @export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64, linkage); + @export(@import("compiler_rt/divti3.zig").__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); + @export(@import("compiler_rt/modti3.zig").__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); + @export(@import("compiler_rt/multi3.zig").__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); + @export(@import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); + @export(@import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(@import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); }, else => {}, } } else { if (builtin.glibc_version != null) { - @export("__stack_chk_guard", __stack_chk_guard, linkage); + @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); } - @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage); - @export("__modti3", @import("compiler_rt/modti3.zig").__modti3, linkage); - @export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage); - @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3, linkage); - @export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4, linkage); - @export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3, linkage); + @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); + @export(@import("compiler_rt/modti3.zig").__modti3, .{ .name = "__modti3", .linkage = linkage }); + @export(@import("compiler_rt/multi3.zig").__multi3, .{ .name = "__multi3", .linkage = linkage }); + @export(@import("compiler_rt/udivti3.zig").__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + @export(@import("compiler_rt/udivmodti4.zig").__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(@import("compiler_rt/umodti3.zig").__umodti3, .{ .name = "__umodti3", .linkage = linkage }); } - @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage); - @export("__mulodi4", @import("compiler_rt/mulodi4.zig").__mulodi4, linkage); + @export(@import("compiler_rt/muloti4.zig").__muloti4, .{ .name = "__muloti4", .linkage = linkage }); + @export(@import("compiler_rt/mulodi4.zig").__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); } const std = @import("std"); const assert = std.debug.assert; const testing = std.testing; -const __udivmoddi4 = @import("compiler_rt/udivmoddi4.zig").__udivmoddi4; - // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - std.debug.panic("{}", msg); + std.debug.panic("{}", .{msg}); } else { unreachable; } } -extern fn __stack_chk_fail() noreturn { +fn __stack_chk_fail() callconv(.C) noreturn { @panic("stack smashing detected"); } @@ -306,108 +321,6 @@ extern var __stack_chk_guard: usize = blk: { break :blk @bitCast(usize, buf); }; -extern fn __aeabi_unwind_cpp_pr0() void { - unreachable; -} -extern fn __aeabi_unwind_cpp_pr1() void { - unreachable; -} -extern fn __aeabi_unwind_cpp_pr2() void { - unreachable; -} - -extern fn __divmoddi4(a: i64, b: i64, rem: *i64) i64 { - @setRuntimeSafety(is_test); - - const d = __divdi3(a, b); - rem.* = a -% (d *% b); - return d; -} - -extern fn __divdi3(a: i64, b: i64) i64 { - @setRuntimeSafety(is_test); - - // Set aside the sign of the quotient. - const sign = @bitCast(u64, (a ^ b) >> 63); - // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). - const abs_a = (a ^ (a >> 63)) -% (a >> 63); - const abs_b = (b ^ (b >> 63)) -% (b >> 63); - // Unsigned division - const res = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), null); - // Apply sign of quotient to result and return. - return @bitCast(i64, (res ^ sign) -% sign); -} - -extern fn __moddi3(a: i64, b: i64) i64 { - @setRuntimeSafety(is_test); - - // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). - const abs_a = (a ^ (a >> 63)) -% (a >> 63); - const abs_b = (b ^ (b >> 63)) -% (b >> 63); - // Unsigned division - var r: u64 = undefined; - _ = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), &r); - // Apply the sign of the dividend and return. - return (@bitCast(i64, r) ^ (a >> 63)) -% (a >> 63); -} - -extern fn __udivdi3(a: u64, b: u64) u64 { - @setRuntimeSafety(is_test); - return __udivmoddi4(a, b, null); -} - -extern fn __umoddi3(a: u64, b: u64) u64 { - @setRuntimeSafety(is_test); - - var r: u64 = undefined; - _ = __udivmoddi4(a, b, &r); - return r; -} - -extern fn __aeabi_uidivmod(n: u32, d: u32) extern struct { - q: u32, - r: u32, -} { - @setRuntimeSafety(is_test); - - var result: @typeOf(__aeabi_uidivmod).ReturnType = undefined; - result.q = __udivmodsi4(n, d, &result.r); - return result; -} - -extern fn __aeabi_uldivmod(n: u64, d: u64) extern struct { - q: u64, - r: u64, -} { - @setRuntimeSafety(is_test); - - var result: @typeOf(__aeabi_uldivmod).ReturnType = undefined; - result.q = __udivmoddi4(n, d, &result.r); - return result; -} - -extern fn __aeabi_idivmod(n: i32, d: i32) extern struct { - q: i32, - r: i32, -} { - @setRuntimeSafety(is_test); - - var result: @typeOf(__aeabi_idivmod).ReturnType = undefined; - result.q = __divmodsi4(n, d, &result.r); - return result; -} - -extern fn __aeabi_ldivmod(n: i64, d: i64) extern struct { - q: i64, - r: i64, -} { - @setRuntimeSafety(is_test); - - var result: @typeOf(__aeabi_ldivmod).ReturnType = undefined; - result.q = __divmoddi4(n, d, &result.r); - return result; -} - const is_arm_64 = switch (builtin.arch) { builtin.Arch.aarch64, builtin.Arch.aarch64_be, @@ -427,1242 +340,3 @@ const is_arm_arch = switch (builtin.arch) { }; const is_arm_32 = is_arm_arch and !is_arm_64; - -const use_thumb_1 = usesThumb1(builtin.arch); - -fn usesThumb1(arch: builtin.Arch) bool { - return switch (arch) { - .arm => |sub_arch| switch (sub_arch) { - .v6m => true, - else => false, - }, - .armeb => |sub_arch| switch (sub_arch) { - .v6m => true, - else => false, - }, - .thumb => |sub_arch| switch (sub_arch) { - .v5, - .v5te, - .v4t, - .v6, - .v6m, - .v6k, - => true, - else => false, - }, - .thumbeb => |sub_arch| switch (sub_arch) { - .v5, - .v5te, - .v4t, - .v6, - .v6m, - .v6k, - => true, - else => false, - }, - else => false, - }; -} - -test "usesThumb1" { - testing.expect(usesThumb1(builtin.Arch{ .arm = .v6m })); - testing.expect(!usesThumb1(builtin.Arch{ .arm = .v5 })); - //etc. - - testing.expect(usesThumb1(builtin.Arch{ .armeb = .v6m })); - testing.expect(!usesThumb1(builtin.Arch{ .armeb = .v5 })); - //etc. - - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v5 })); - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v5te })); - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v4t })); - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6 })); - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6k })); - testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6m })); - testing.expect(!usesThumb1(builtin.Arch{ .thumb = .v6t2 })); - //etc. - - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v5 })); - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v5te })); - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v4t })); - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6 })); - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6k })); - testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6m })); - testing.expect(!usesThumb1(builtin.Arch{ .thumbeb = .v6t2 })); - //etc. - - testing.expect(!usesThumb1(builtin.Arch{ .aarch64 = .v8 })); - testing.expect(!usesThumb1(builtin.Arch{ .aarch64_be = .v8 })); - testing.expect(!usesThumb1(builtin.Arch.x86_64)); - testing.expect(!usesThumb1(builtin.Arch.riscv32)); - //etc. -} - -const use_thumb_1_pre_armv6 = usesThumb1PreArmv6(builtin.arch); - -fn usesThumb1PreArmv6(arch: builtin.Arch) bool { - return switch (arch) { - .thumb => |sub_arch| switch (sub_arch) { - .v5, .v5te, .v4t => true, - else => false, - }, - .thumbeb => |sub_arch| switch (sub_arch) { - .v5, .v5te, .v4t => true, - else => false, - }, - else => false, - }; -} - -nakedcc fn __aeabi_memcpy() noreturn { - @setRuntimeSafety(false); - if (use_thumb_1) { - asm volatile ( - \\ push {r7, lr} - \\ bl memcpy - \\ pop {r7, pc} - ); - } else { - asm volatile ( - \\ b memcpy - ); - } - unreachable; -} - -nakedcc fn __aeabi_memmove() noreturn { - @setRuntimeSafety(false); - if (use_thumb_1) { - asm volatile ( - \\ push {r7, lr} - \\ bl memmove - \\ pop {r7, pc} - ); - } else { - asm volatile ( - \\ b memmove - ); - } - unreachable; -} - -nakedcc fn __aeabi_memset() noreturn { - @setRuntimeSafety(false); - if (use_thumb_1_pre_armv6) { - asm volatile ( - \\ eors r1, r2 - \\ eors r2, r1 - \\ eors r1, r2 - \\ push {r7, lr} - \\ b memset - \\ pop {r7, pc} - ); - } else if (use_thumb_1) { - asm volatile ( - \\ mov r3, r1 - \\ mov r1, r2 - \\ mov r2, r3 - \\ push {r7, lr} - \\ b memset - \\ pop {r7, pc} - ); - } else { - asm volatile ( - \\ mov r3, r1 - \\ mov r1, r2 - \\ mov r2, r3 - \\ b memset - ); - } - unreachable; -} - -nakedcc fn __aeabi_memclr() noreturn { - @setRuntimeSafety(false); - if (use_thumb_1_pre_armv6) { - asm volatile ( - \\ adds r2, r1, #0 - \\ movs r1, #0 - \\ push {r7, lr} - \\ bl memset - \\ pop {r7, pc} - ); - } else if (use_thumb_1) { - asm volatile ( - \\ mov r2, r1 - \\ movs r1, #0 - \\ push {r7, lr} - \\ bl memset - \\ pop {r7, pc} - ); - } else { - asm volatile ( - \\ mov r2, r1 - \\ movs r1, #0 - \\ b memset - ); - } - unreachable; -} - -nakedcc fn __aeabi_memcmp() noreturn { - @setRuntimeSafety(false); - if (use_thumb_1) { - asm volatile ( - \\ push {r7, lr} - \\ bl memcmp - \\ pop {r7, pc} - ); - } else { - asm volatile ( - \\ b memcmp - ); - } - unreachable; -} - -extern fn __divmodsi4(a: i32, b: i32, rem: *i32) i32 { - @setRuntimeSafety(is_test); - - const d = __divsi3(a, b); - rem.* = a -% (d * b); - return d; -} - -extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { - @setRuntimeSafety(is_test); - - const d = __udivsi3(a, b); - rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); - return d; -} - -extern fn __divsi3(n: i32, d: i32) i32 { - @setRuntimeSafety(is_test); - - // Set aside the sign of the quotient. - const sign = @bitCast(u32, (n ^ d) >> 31); - // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). - const abs_n = (n ^ (n >> 31)) -% (n >> 31); - const abs_d = (d ^ (d >> 31)) -% (d >> 31); - // abs(a) / abs(b) - const res = @bitCast(u32, abs_n) / @bitCast(u32, abs_d); - // Apply sign of quotient to result and return. - return @bitCast(i32, (res ^ sign) -% sign); -} - -extern fn __udivsi3(n: u32, d: u32) u32 { - @setRuntimeSafety(is_test); - - const n_uword_bits: c_uint = u32.bit_count; - // special cases - if (d == 0) return 0; // ?! - if (n == 0) return 0; - var sr = @bitCast(c_uint, c_int(@clz(u32, d)) - c_int(@clz(u32, n))); - // 0 <= sr <= n_uword_bits - 1 or sr large - if (sr > n_uword_bits - 1) { - // d > r - return 0; - } - if (sr == n_uword_bits - 1) { - // d == 1 - return n; - } - sr += 1; - // 1 <= sr <= n_uword_bits - 1 - // Not a special case - var q: u32 = n << @intCast(u5, n_uword_bits - sr); - var r: u32 = n >> @intCast(u5, sr); - var carry: u32 = 0; - while (sr > 0) : (sr -= 1) { - // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); - q = (q << 1) | carry; - // carry = 0; - // if (r.all >= d.all) - // { - // r.all -= d.all; - // carry = 1; - // } - const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); - carry = @intCast(u32, s & 1); - r -= d & @bitCast(u32, s); - } - q = (q << 1) | carry; - return q; -} - -extern fn __modsi3(n: i32, d: i32) i32 { - @setRuntimeSafety(is_test); - - return n -% __divsi3(n, d) *% d; -} - -extern fn __umodsi3(n: u32, d: u32) u32 { - @setRuntimeSafety(is_test); - - return n -% __udivsi3(n, d) *% d; -} - -test "test_umoddi3" { - test_one_umoddi3(0, 1, 0); - test_one_umoddi3(2, 1, 0); - test_one_umoddi3(0x8000000000000000, 1, 0x0); - test_one_umoddi3(0x8000000000000000, 2, 0x0); - test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); -} - -fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { - const r = __umoddi3(a, b); - testing.expect(r == expected_r); -} - -test "test_udivsi3" { - const cases = [_][3]u32{ - [_]u32{ - 0x00000000, - 0x00000001, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x00000002, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x00000003, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x00000010, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x0747AE14, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x00000000, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x00000001, - 0x00000001, - }, - [_]u32{ - 0x00000001, - 0x00000002, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x00000003, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x00000010, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x0747AE14, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x00000001, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x00000001, - 0x00000002, - }, - [_]u32{ - 0x00000002, - 0x00000002, - 0x00000001, - }, - [_]u32{ - 0x00000002, - 0x00000003, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x00000010, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x0747AE14, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x00000002, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0x00000001, - 0x00000003, - }, - [_]u32{ - 0x00000003, - 0x00000002, - 0x00000001, - }, - [_]u32{ - 0x00000003, - 0x00000003, - 0x00000001, - }, - [_]u32{ - 0x00000003, - 0x00000010, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0x0747AE14, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x00000003, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0x00000001, - 0x00000010, - }, - [_]u32{ - 0x00000010, - 0x00000002, - 0x00000008, - }, - [_]u32{ - 0x00000010, - 0x00000003, - 0x00000005, - }, - [_]u32{ - 0x00000010, - 0x00000010, - 0x00000001, - }, - [_]u32{ - 0x00000010, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0x0747AE14, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x00000010, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x078644FA, - 0x00000001, - 0x078644FA, - }, - [_]u32{ - 0x078644FA, - 0x00000002, - 0x03C3227D, - }, - [_]u32{ - 0x078644FA, - 0x00000003, - 0x028216FE, - }, - [_]u32{ - 0x078644FA, - 0x00000010, - 0x0078644F, - }, - [_]u32{ - 0x078644FA, - 0x078644FA, - 0x00000001, - }, - [_]u32{ - 0x078644FA, - 0x0747AE14, - 0x00000001, - }, - [_]u32{ - 0x078644FA, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x078644FA, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x078644FA, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x078644FA, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x078644FA, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0x00000001, - 0x0747AE14, - }, - [_]u32{ - 0x0747AE14, - 0x00000002, - 0x03A3D70A, - }, - [_]u32{ - 0x0747AE14, - 0x00000003, - 0x026D3A06, - }, - [_]u32{ - 0x0747AE14, - 0x00000010, - 0x00747AE1, - }, - [_]u32{ - 0x0747AE14, - 0x078644FA, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0x0747AE14, - 0x00000001, - }, - [_]u32{ - 0x0747AE14, - 0x7FFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x0747AE14, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x7FFFFFFF, - 0x00000001, - 0x7FFFFFFF, - }, - [_]u32{ - 0x7FFFFFFF, - 0x00000002, - 0x3FFFFFFF, - }, - [_]u32{ - 0x7FFFFFFF, - 0x00000003, - 0x2AAAAAAA, - }, - [_]u32{ - 0x7FFFFFFF, - 0x00000010, - 0x07FFFFFF, - }, - [_]u32{ - 0x7FFFFFFF, - 0x078644FA, - 0x00000011, - }, - [_]u32{ - 0x7FFFFFFF, - 0x0747AE14, - 0x00000011, - }, - [_]u32{ - 0x7FFFFFFF, - 0x7FFFFFFF, - 0x00000001, - }, - [_]u32{ - 0x7FFFFFFF, - 0x80000000, - 0x00000000, - }, - [_]u32{ - 0x7FFFFFFF, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x7FFFFFFF, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x7FFFFFFF, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0x80000000, - 0x00000001, - 0x80000000, - }, - [_]u32{ - 0x80000000, - 0x00000002, - 0x40000000, - }, - [_]u32{ - 0x80000000, - 0x00000003, - 0x2AAAAAAA, - }, - [_]u32{ - 0x80000000, - 0x00000010, - 0x08000000, - }, - [_]u32{ - 0x80000000, - 0x078644FA, - 0x00000011, - }, - [_]u32{ - 0x80000000, - 0x0747AE14, - 0x00000011, - }, - [_]u32{ - 0x80000000, - 0x7FFFFFFF, - 0x00000001, - }, - [_]u32{ - 0x80000000, - 0x80000000, - 0x00000001, - }, - [_]u32{ - 0x80000000, - 0xFFFFFFFD, - 0x00000000, - }, - [_]u32{ - 0x80000000, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0x80000000, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0xFFFFFFFD, - 0x00000001, - 0xFFFFFFFD, - }, - [_]u32{ - 0xFFFFFFFD, - 0x00000002, - 0x7FFFFFFE, - }, - [_]u32{ - 0xFFFFFFFD, - 0x00000003, - 0x55555554, - }, - [_]u32{ - 0xFFFFFFFD, - 0x00000010, - 0x0FFFFFFF, - }, - [_]u32{ - 0xFFFFFFFD, - 0x078644FA, - 0x00000022, - }, - [_]u32{ - 0xFFFFFFFD, - 0x0747AE14, - 0x00000023, - }, - [_]u32{ - 0xFFFFFFFD, - 0x7FFFFFFF, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFD, - 0x80000000, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFD, - 0xFFFFFFFD, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFD, - 0xFFFFFFFE, - 0x00000000, - }, - [_]u32{ - 0xFFFFFFFD, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0xFFFFFFFE, - 0x00000001, - 0xFFFFFFFE, - }, - [_]u32{ - 0xFFFFFFFE, - 0x00000002, - 0x7FFFFFFF, - }, - [_]u32{ - 0xFFFFFFFE, - 0x00000003, - 0x55555554, - }, - [_]u32{ - 0xFFFFFFFE, - 0x00000010, - 0x0FFFFFFF, - }, - [_]u32{ - 0xFFFFFFFE, - 0x078644FA, - 0x00000022, - }, - [_]u32{ - 0xFFFFFFFE, - 0x0747AE14, - 0x00000023, - }, - [_]u32{ - 0xFFFFFFFE, - 0x7FFFFFFF, - 0x00000002, - }, - [_]u32{ - 0xFFFFFFFE, - 0x80000000, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFE, - 0xFFFFFFFD, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFE, - 0xFFFFFFFE, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFE, - 0xFFFFFFFF, - 0x00000000, - }, - [_]u32{ - 0xFFFFFFFF, - 0x00000001, - 0xFFFFFFFF, - }, - [_]u32{ - 0xFFFFFFFF, - 0x00000002, - 0x7FFFFFFF, - }, - [_]u32{ - 0xFFFFFFFF, - 0x00000003, - 0x55555555, - }, - [_]u32{ - 0xFFFFFFFF, - 0x00000010, - 0x0FFFFFFF, - }, - [_]u32{ - 0xFFFFFFFF, - 0x078644FA, - 0x00000022, - }, - [_]u32{ - 0xFFFFFFFF, - 0x0747AE14, - 0x00000023, - }, - [_]u32{ - 0xFFFFFFFF, - 0x7FFFFFFF, - 0x00000002, - }, - [_]u32{ - 0xFFFFFFFF, - 0x80000000, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFF, - 0xFFFFFFFD, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFF, - 0xFFFFFFFE, - 0x00000001, - }, - [_]u32{ - 0xFFFFFFFF, - 0xFFFFFFFF, - 0x00000001, - }, - }; - - for (cases) |case| { - test_one_udivsi3(case[0], case[1], case[2]); - } -} - -fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { - const q: u32 = __udivsi3(a, b); - testing.expect(q == expected_q); -} - -test "test_divsi3" { - const cases = [_][3]i32{ - [_]i32{ 0, 1, 0 }, - [_]i32{ 0, -1, 0 }, - [_]i32{ 2, 1, 2 }, - [_]i32{ 2, -1, -2 }, - [_]i32{ -2, 1, -2 }, - [_]i32{ -2, -1, 2 }, - - [_]i32{ @bitCast(i32, u32(0x80000000)), 1, @bitCast(i32, u32(0x80000000)) }, - [_]i32{ @bitCast(i32, u32(0x80000000)), -1, @bitCast(i32, u32(0x80000000)) }, - [_]i32{ @bitCast(i32, u32(0x80000000)), -2, 0x40000000 }, - [_]i32{ @bitCast(i32, u32(0x80000000)), 2, @bitCast(i32, u32(0xC0000000)) }, - }; - - for (cases) |case| { - test_one_divsi3(case[0], case[1], case[2]); - } -} - -fn test_one_divsi3(a: i32, b: i32, expected_q: i32) void { - const q: i32 = __divsi3(a, b); - testing.expect(q == expected_q); -} - -test "test_divmodsi4" { - const cases = [_][4]i32{ - [_]i32{ 0, 1, 0, 0 }, - [_]i32{ 0, -1, 0, 0 }, - [_]i32{ 2, 1, 2, 0 }, - [_]i32{ 2, -1, -2, 0 }, - [_]i32{ -2, 1, -2, 0 }, - [_]i32{ -2, -1, 2, 0 }, - [_]i32{ 7, 5, 1, 2 }, - [_]i32{ -7, 5, -1, -2 }, - [_]i32{ 19, 5, 3, 4 }, - [_]i32{ 19, -5, -3, 4 }, - - [_]i32{ @bitCast(i32, u32(0x80000000)), 8, @bitCast(i32, u32(0xf0000000)), 0 }, - [_]i32{ @bitCast(i32, u32(0x80000007)), 8, @bitCast(i32, u32(0xf0000001)), -1 }, - }; - - for (cases) |case| { - test_one_divmodsi4(case[0], case[1], case[2], case[3]); - } -} - -fn test_one_divmodsi4(a: i32, b: i32, expected_q: i32, expected_r: i32) void { - var r: i32 = undefined; - const q: i32 = __divmodsi4(a, b, &r); - testing.expect(q == expected_q and r == expected_r); -} - -test "test_divdi3" { - const cases = [_][3]i64{ - [_]i64{ 0, 1, 0 }, - [_]i64{ 0, -1, 0 }, - [_]i64{ 2, 1, 2 }, - [_]i64{ 2, -1, -2 }, - [_]i64{ -2, 1, -2 }, - [_]i64{ -2, -1, 2 }, - - [_]i64{ @bitCast(i64, u64(0x8000000000000000)), 1, @bitCast(i64, u64(0x8000000000000000)) }, - [_]i64{ @bitCast(i64, u64(0x8000000000000000)), -1, @bitCast(i64, u64(0x8000000000000000)) }, - [_]i64{ @bitCast(i64, u64(0x8000000000000000)), -2, 0x4000000000000000 }, - [_]i64{ @bitCast(i64, u64(0x8000000000000000)), 2, @bitCast(i64, u64(0xC000000000000000)) }, - }; - - for (cases) |case| { - test_one_divdi3(case[0], case[1], case[2]); - } -} - -fn test_one_divdi3(a: i64, b: i64, expected_q: i64) void { - const q: i64 = __divdi3(a, b); - testing.expect(q == expected_q); -} - -test "test_moddi3" { - const cases = [_][3]i64{ - [_]i64{ 0, 1, 0 }, - [_]i64{ 0, -1, 0 }, - [_]i64{ 5, 3, 2 }, - [_]i64{ 5, -3, 2 }, - [_]i64{ -5, 3, -2 }, - [_]i64{ -5, -3, -2 }, - - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 1, 0 }, - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -1, 0 }, - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 2, 0 }, - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -2, 0 }, - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 3, -2 }, - [_]i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -3, -2 }, - }; - - for (cases) |case| { - test_one_moddi3(case[0], case[1], case[2]); - } -} - -fn test_one_moddi3(a: i64, b: i64, expected_r: i64) void { - const r: i64 = __moddi3(a, b); - testing.expect(r == expected_r); -} - -test "test_modsi3" { - const cases = [_][3]i32{ - [_]i32{ 0, 1, 0 }, - [_]i32{ 0, -1, 0 }, - [_]i32{ 5, 3, 2 }, - [_]i32{ 5, -3, 2 }, - [_]i32{ -5, 3, -2 }, - [_]i32{ -5, -3, -2 }, - [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 1, 0x0 }, - [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 2, 0x0 }, - [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -2, 0x0 }, - [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 3, -2 }, - [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -3, -2 }, - }; - - for (cases) |case| { - test_one_modsi3(case[0], case[1], case[2]); - } -} - -fn test_one_modsi3(a: i32, b: i32, expected_r: i32) void { - const r: i32 = __modsi3(a, b); - testing.expect(r == expected_r); -} - -test "test_umodsi3" { - const cases = [_][3]u32{ - [_]u32{ 0x00000000, 0x00000001, 0x00000000 }, - [_]u32{ 0x00000000, 0x00000002, 0x00000000 }, - [_]u32{ 0x00000000, 0x00000003, 0x00000000 }, - [_]u32{ 0x00000000, 0x00000010, 0x00000000 }, - [_]u32{ 0x00000000, 0x078644FA, 0x00000000 }, - [_]u32{ 0x00000000, 0x0747AE14, 0x00000000 }, - [_]u32{ 0x00000000, 0x7FFFFFFF, 0x00000000 }, - [_]u32{ 0x00000000, 0x80000000, 0x00000000 }, - [_]u32{ 0x00000000, 0xFFFFFFFD, 0x00000000 }, - [_]u32{ 0x00000000, 0xFFFFFFFE, 0x00000000 }, - [_]u32{ 0x00000000, 0xFFFFFFFF, 0x00000000 }, - [_]u32{ 0x00000001, 0x00000001, 0x00000000 }, - [_]u32{ 0x00000001, 0x00000002, 0x00000001 }, - [_]u32{ 0x00000001, 0x00000003, 0x00000001 }, - [_]u32{ 0x00000001, 0x00000010, 0x00000001 }, - [_]u32{ 0x00000001, 0x078644FA, 0x00000001 }, - [_]u32{ 0x00000001, 0x0747AE14, 0x00000001 }, - [_]u32{ 0x00000001, 0x7FFFFFFF, 0x00000001 }, - [_]u32{ 0x00000001, 0x80000000, 0x00000001 }, - [_]u32{ 0x00000001, 0xFFFFFFFD, 0x00000001 }, - [_]u32{ 0x00000001, 0xFFFFFFFE, 0x00000001 }, - [_]u32{ 0x00000001, 0xFFFFFFFF, 0x00000001 }, - [_]u32{ 0x00000002, 0x00000001, 0x00000000 }, - [_]u32{ 0x00000002, 0x00000002, 0x00000000 }, - [_]u32{ 0x00000002, 0x00000003, 0x00000002 }, - [_]u32{ 0x00000002, 0x00000010, 0x00000002 }, - [_]u32{ 0x00000002, 0x078644FA, 0x00000002 }, - [_]u32{ 0x00000002, 0x0747AE14, 0x00000002 }, - [_]u32{ 0x00000002, 0x7FFFFFFF, 0x00000002 }, - [_]u32{ 0x00000002, 0x80000000, 0x00000002 }, - [_]u32{ 0x00000002, 0xFFFFFFFD, 0x00000002 }, - [_]u32{ 0x00000002, 0xFFFFFFFE, 0x00000002 }, - [_]u32{ 0x00000002, 0xFFFFFFFF, 0x00000002 }, - [_]u32{ 0x00000003, 0x00000001, 0x00000000 }, - [_]u32{ 0x00000003, 0x00000002, 0x00000001 }, - [_]u32{ 0x00000003, 0x00000003, 0x00000000 }, - [_]u32{ 0x00000003, 0x00000010, 0x00000003 }, - [_]u32{ 0x00000003, 0x078644FA, 0x00000003 }, - [_]u32{ 0x00000003, 0x0747AE14, 0x00000003 }, - [_]u32{ 0x00000003, 0x7FFFFFFF, 0x00000003 }, - [_]u32{ 0x00000003, 0x80000000, 0x00000003 }, - [_]u32{ 0x00000003, 0xFFFFFFFD, 0x00000003 }, - [_]u32{ 0x00000003, 0xFFFFFFFE, 0x00000003 }, - [_]u32{ 0x00000003, 0xFFFFFFFF, 0x00000003 }, - [_]u32{ 0x00000010, 0x00000001, 0x00000000 }, - [_]u32{ 0x00000010, 0x00000002, 0x00000000 }, - [_]u32{ 0x00000010, 0x00000003, 0x00000001 }, - [_]u32{ 0x00000010, 0x00000010, 0x00000000 }, - [_]u32{ 0x00000010, 0x078644FA, 0x00000010 }, - [_]u32{ 0x00000010, 0x0747AE14, 0x00000010 }, - [_]u32{ 0x00000010, 0x7FFFFFFF, 0x00000010 }, - [_]u32{ 0x00000010, 0x80000000, 0x00000010 }, - [_]u32{ 0x00000010, 0xFFFFFFFD, 0x00000010 }, - [_]u32{ 0x00000010, 0xFFFFFFFE, 0x00000010 }, - [_]u32{ 0x00000010, 0xFFFFFFFF, 0x00000010 }, - [_]u32{ 0x078644FA, 0x00000001, 0x00000000 }, - [_]u32{ 0x078644FA, 0x00000002, 0x00000000 }, - [_]u32{ 0x078644FA, 0x00000003, 0x00000000 }, - [_]u32{ 0x078644FA, 0x00000010, 0x0000000A }, - [_]u32{ 0x078644FA, 0x078644FA, 0x00000000 }, - [_]u32{ 0x078644FA, 0x0747AE14, 0x003E96E6 }, - [_]u32{ 0x078644FA, 0x7FFFFFFF, 0x078644FA }, - [_]u32{ 0x078644FA, 0x80000000, 0x078644FA }, - [_]u32{ 0x078644FA, 0xFFFFFFFD, 0x078644FA }, - [_]u32{ 0x078644FA, 0xFFFFFFFE, 0x078644FA }, - [_]u32{ 0x078644FA, 0xFFFFFFFF, 0x078644FA }, - [_]u32{ 0x0747AE14, 0x00000001, 0x00000000 }, - [_]u32{ 0x0747AE14, 0x00000002, 0x00000000 }, - [_]u32{ 0x0747AE14, 0x00000003, 0x00000002 }, - [_]u32{ 0x0747AE14, 0x00000010, 0x00000004 }, - [_]u32{ 0x0747AE14, 0x078644FA, 0x0747AE14 }, - [_]u32{ 0x0747AE14, 0x0747AE14, 0x00000000 }, - [_]u32{ 0x0747AE14, 0x7FFFFFFF, 0x0747AE14 }, - [_]u32{ 0x0747AE14, 0x80000000, 0x0747AE14 }, - [_]u32{ 0x0747AE14, 0xFFFFFFFD, 0x0747AE14 }, - [_]u32{ 0x0747AE14, 0xFFFFFFFE, 0x0747AE14 }, - [_]u32{ 0x0747AE14, 0xFFFFFFFF, 0x0747AE14 }, - [_]u32{ 0x7FFFFFFF, 0x00000001, 0x00000000 }, - [_]u32{ 0x7FFFFFFF, 0x00000002, 0x00000001 }, - [_]u32{ 0x7FFFFFFF, 0x00000003, 0x00000001 }, - [_]u32{ 0x7FFFFFFF, 0x00000010, 0x0000000F }, - [_]u32{ 0x7FFFFFFF, 0x078644FA, 0x00156B65 }, - [_]u32{ 0x7FFFFFFF, 0x0747AE14, 0x043D70AB }, - [_]u32{ 0x7FFFFFFF, 0x7FFFFFFF, 0x00000000 }, - [_]u32{ 0x7FFFFFFF, 0x80000000, 0x7FFFFFFF }, - [_]u32{ 0x7FFFFFFF, 0xFFFFFFFD, 0x7FFFFFFF }, - [_]u32{ 0x7FFFFFFF, 0xFFFFFFFE, 0x7FFFFFFF }, - [_]u32{ 0x7FFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF }, - [_]u32{ 0x80000000, 0x00000001, 0x00000000 }, - [_]u32{ 0x80000000, 0x00000002, 0x00000000 }, - [_]u32{ 0x80000000, 0x00000003, 0x00000002 }, - [_]u32{ 0x80000000, 0x00000010, 0x00000000 }, - [_]u32{ 0x80000000, 0x078644FA, 0x00156B66 }, - [_]u32{ 0x80000000, 0x0747AE14, 0x043D70AC }, - [_]u32{ 0x80000000, 0x7FFFFFFF, 0x00000001 }, - [_]u32{ 0x80000000, 0x80000000, 0x00000000 }, - [_]u32{ 0x80000000, 0xFFFFFFFD, 0x80000000 }, - [_]u32{ 0x80000000, 0xFFFFFFFE, 0x80000000 }, - [_]u32{ 0x80000000, 0xFFFFFFFF, 0x80000000 }, - [_]u32{ 0xFFFFFFFD, 0x00000001, 0x00000000 }, - [_]u32{ 0xFFFFFFFD, 0x00000002, 0x00000001 }, - [_]u32{ 0xFFFFFFFD, 0x00000003, 0x00000001 }, - [_]u32{ 0xFFFFFFFD, 0x00000010, 0x0000000D }, - [_]u32{ 0xFFFFFFFD, 0x078644FA, 0x002AD6C9 }, - [_]u32{ 0xFFFFFFFD, 0x0747AE14, 0x01333341 }, - [_]u32{ 0xFFFFFFFD, 0x7FFFFFFF, 0x7FFFFFFE }, - [_]u32{ 0xFFFFFFFD, 0x80000000, 0x7FFFFFFD }, - [_]u32{ 0xFFFFFFFD, 0xFFFFFFFD, 0x00000000 }, - [_]u32{ 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFD }, - [_]u32{ 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFD }, - [_]u32{ 0xFFFFFFFE, 0x00000001, 0x00000000 }, - [_]u32{ 0xFFFFFFFE, 0x00000002, 0x00000000 }, - [_]u32{ 0xFFFFFFFE, 0x00000003, 0x00000002 }, - [_]u32{ 0xFFFFFFFE, 0x00000010, 0x0000000E }, - [_]u32{ 0xFFFFFFFE, 0x078644FA, 0x002AD6CA }, - [_]u32{ 0xFFFFFFFE, 0x0747AE14, 0x01333342 }, - [_]u32{ 0xFFFFFFFE, 0x7FFFFFFF, 0x00000000 }, - [_]u32{ 0xFFFFFFFE, 0x80000000, 0x7FFFFFFE }, - [_]u32{ 0xFFFFFFFE, 0xFFFFFFFD, 0x00000001 }, - [_]u32{ 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, - [_]u32{ 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE }, - [_]u32{ 0xFFFFFFFF, 0x00000001, 0x00000000 }, - [_]u32{ 0xFFFFFFFF, 0x00000002, 0x00000001 }, - [_]u32{ 0xFFFFFFFF, 0x00000003, 0x00000000 }, - [_]u32{ 0xFFFFFFFF, 0x00000010, 0x0000000F }, - [_]u32{ 0xFFFFFFFF, 0x078644FA, 0x002AD6CB }, - [_]u32{ 0xFFFFFFFF, 0x0747AE14, 0x01333343 }, - [_]u32{ 0xFFFFFFFF, 0x7FFFFFFF, 0x00000001 }, - [_]u32{ 0xFFFFFFFF, 0x80000000, 0x7FFFFFFF }, - [_]u32{ 0xFFFFFFFF, 0xFFFFFFFD, 0x00000002 }, - [_]u32{ 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001 }, - [_]u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, - }; - - for (cases) |case| { - test_one_umodsi3(case[0], case[1], case[2]); - } -} - -fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) void { - const r: u32 = __umodsi3(a, b); - testing.expect(r == expected_r); -} diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 1654c1f08..4294752ba 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -6,39 +6,59 @@ const std = @import("std"); const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __addsf3(a: f32, b: f32) f32 { +pub fn __addsf3(a: f32, b: f32) callconv(.C) f32 { return addXf3(f32, a, b); } -pub extern fn __adddf3(a: f64, b: f64) f64 { +pub fn __adddf3(a: f64, b: f64) callconv(.C) f64 { return addXf3(f64, a, b); } -pub extern fn __addtf3(a: f128, b: f128) f128 { +pub fn __addtf3(a: f128, b: f128) callconv(.C) f128 { return addXf3(f128, a, b); } -pub extern fn __subsf3(a: f32, b: f32) f32 { - const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (u32(1) << 31)); +pub fn __subsf3(a: f32, b: f32) callconv(.C) f32 { + const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (@as(u32, 1) << 31)); return addXf3(f32, a, neg_b); } -pub extern fn __subdf3(a: f64, b: f64) f64 { - const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (u64(1) << 63)); +pub fn __subdf3(a: f64, b: f64) callconv(.C) f64 { + const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (@as(u64, 1) << 63)); return addXf3(f64, a, neg_b); } -pub extern fn __subtf3(a: f128, b: f128) f128 { - const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); +pub fn __subtf3(a: f128, b: f128) callconv(.C) f128 { + const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); return addXf3(f128, a, neg_b); } +pub fn __aeabi_fadd(a: f32, b: f32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __addsf3, .{ a, b }); +} + +pub fn __aeabi_dadd(a: f64, b: f64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __adddf3, .{ a, b }); +} + +pub fn __aeabi_fsub(a: f32, b: f32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __subsf3, .{ a, b }); +} + +pub fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __subdf3, .{ a, b }); +} + // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { const Z = @IntType(false, T.bit_count); - const S = @IntType(false, T.bit_count - @clz(Z, Z(T.bit_count) - 1)); + const S = @IntType(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1)); const significandBits = std.math.floatMantissaBits(T); - const implicitBit = Z(1) << significandBits; + const implicitBit = @as(Z, 1) << significandBits; const shift = @clz(@IntType(false, T.bit_count), significand.*) - @clz(Z, implicitBit); significand.* <<= @intCast(S, shift); @@ -48,17 +68,17 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 fn addXf3(comptime T: type, a: T, b: T) T { const Z = @IntType(false, T.bit_count); - const S = @IntType(false, T.bit_count - @clz(Z, Z(T.bit_count) - 1)); + const S = @IntType(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1)); const typeWidth = T.bit_count; const significandBits = std.math.floatMantissaBits(T); const exponentBits = std.math.floatExponentBits(T); - const signBit = (Z(1) << (significandBits + exponentBits)); + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (Z(1) << significandBits); + const implicitBit = (@as(Z, 1) << significandBits); const quietBit = implicitBit >> 1; const significandMask = implicitBit - 1; @@ -78,8 +98,8 @@ fn addXf3(comptime T: type, a: T, b: T) T { const infRep = @bitCast(Z, std.math.inf(T)); // Detect if a or b is zero, infinity, or NaN. - if (aAbs -% Z(1) >= infRep - Z(1) or - bAbs -% Z(1) >= infRep - Z(1)) + if (aAbs -% @as(Z, 1) >= infRep - @as(Z, 1) or + bAbs -% @as(Z, 1) >= infRep - @as(Z, 1)) { // NaN + anything = qNaN if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit); @@ -148,7 +168,7 @@ fn addXf3(comptime T: type, a: T, b: T) T { const @"align" = @intCast(Z, aExponent - bExponent); if (@"align" != 0) { if (@"align" < typeWidth) { - const sticky = if (bSignificand << @intCast(S, typeWidth - @"align") != 0) Z(1) else 0; + const sticky = if (bSignificand << @intCast(S, typeWidth - @"align") != 0) @as(Z, 1) else 0; bSignificand = (bSignificand >> @truncate(S, @"align")) | sticky; } else { bSignificand = 1; // sticky; b is known to be non-zero. @@ -157,7 +177,7 @@ fn addXf3(comptime T: type, a: T, b: T) T { if (subtraction) { aSignificand -= bSignificand; // If a == -b, return +zero. - if (aSignificand == 0) return @bitCast(T, Z(0)); + if (aSignificand == 0) return @bitCast(T, @as(Z, 0)); // If partial cancellation occured, we need to left-shift the result // and adjust the exponent: @@ -185,7 +205,7 @@ fn addXf3(comptime T: type, a: T, b: T) T { // Result is denormal before rounding; the exponent is zero and we // need to shift the significand. const shift = @intCast(Z, 1 - aExponent); - const sticky = if (aSignificand << @intCast(S, typeWidth - shift) != 0) Z(1) else 0; + const sticky = if (aSignificand << @intCast(S, typeWidth - shift) != 0) @as(Z, 1) else 0; aSignificand = aSignificand >> @intCast(S, shift | sticky); aExponent = 0; } diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig index 099b73797..af991b37e 100644 --- a/lib/std/special/compiler_rt/addXf3_test.zig +++ b/lib/std/special/compiler_rt/addXf3_test.zig @@ -3,8 +3,8 @@ // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/addtf3_test.c // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/subtf3_test.c -const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64); -const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64); +const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); +const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); const __addtf3 = @import("addXf3.zig").__addtf3; @@ -34,7 +34,7 @@ test "addtf3" { test__addtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // NaN + any = NaN - test__addtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + test__addtf3(@bitCast(f128, (@as(u128, 0x7fff000000000000) << 64) | @as(u128, 0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // inf + inf = inf test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0); @@ -75,7 +75,7 @@ test "subtf3" { test__subtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // NaN + any = NaN - test__subtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + test__subtf3(@bitCast(f128, (@as(u128, 0x7fff000000000000) << 64) | @as(u128, 0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // inf - any = inf test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); diff --git a/lib/std/special/compiler_rt/arm.zig b/lib/std/special/compiler_rt/arm.zig new file mode 100644 index 000000000..9ba423931 --- /dev/null +++ b/lib/std/special/compiler_rt/arm.zig @@ -0,0 +1,128 @@ +// ARM specific builtins +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const __divmodsi4 = @import("int.zig").__divmodsi4; +const __udivmodsi4 = @import("int.zig").__udivmodsi4; +const __divmoddi4 = @import("int.zig").__divmoddi4; +const __udivmoddi4 = @import("int.zig").__udivmoddi4; + +extern fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8; +extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8; +extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8; + +pub fn __aeabi_memcpy(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memcpy(dest, src, n); +} + +pub fn __aeabi_memmove(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memmove(dest, src, n); +} + +pub fn __aeabi_memset(dest: [*]u8, n: usize, c: u8) callconv(.AAPCS) void { + @setRuntimeSafety(false); + // This is dentical to the standard `memset` definition but with the last + // two arguments swapped + _ = memset(dest, c, n); +} + +pub fn __aeabi_memclr(dest: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memset(dest, 0, n); +} + +pub fn __aeabi_unwind_cpp_pr0() callconv(.C) void { + unreachable; +} +pub fn __aeabi_unwind_cpp_pr1() callconv(.C) void { + unreachable; +} +pub fn __aeabi_unwind_cpp_pr2() callconv(.C) void { + unreachable; +} + +// This function can only clobber r0 according to the ABI +pub fn __aeabi_read_tp() callconv(.Naked) void { + asm volatile ( + \\ mrc p15, 0, r0, c13, c0, 3 + \\ bx lr + ); + unreachable; +} + +// The following functions are wrapped in an asm block to ensure the required +// calling convention is always respected + +pub fn __aeabi_uidivmod() callconv(.Naked) void { + // Divide r0 by r1; the quotient goes in r0, the remainder in r1 + asm volatile ( + \\ push {lr} + \\ sub sp, #4 + \\ mov r2, sp + \\ bl __udivmodsi4 + \\ ldr r1, [sp] + \\ add sp, #4 + \\ pop {pc} + : + : + : "memory" + ); + unreachable; +} + +pub fn __aeabi_uldivmod() callconv(.Naked) void { + // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2 + asm volatile ( + \\ push {r4, lr} + \\ sub sp, #16 + \\ add r4, sp, #8 + \\ str r4, [sp] + \\ bl __udivmoddi4 + \\ ldr r2, [sp, #8] + \\ ldr r3, [sp, #12] + \\ add sp, #16 + \\ pop {r4, pc} + : + : + : "memory" + ); + unreachable; +} + +pub fn __aeabi_idivmod() callconv(.Naked) void { + // Divide r0 by r1; the quotient goes in r0, the remainder in r1 + asm volatile ( + \\ push {lr} + \\ sub sp, #4 + \\ mov r2, sp + \\ bl __divmodsi4 + \\ ldr r1, [sp] + \\ add sp, #4 + \\ pop {pc} + : + : + : "memory" + ); + unreachable; +} + +pub fn __aeabi_ldivmod() callconv(.Naked) void { + // Divide r1:r0 by r3:r2; the quotient goes in r1:r0, the remainder in r3:r2 + asm volatile ( + \\ push {r4, lr} + \\ sub sp, #16 + \\ add r4, sp, #8 + \\ str r4, [sp] + \\ bl __divmoddi4 + \\ ldr r2, [sp, #8] + \\ ldr r3, [sp, #12] + \\ add sp, #16 + \\ pop {r4, pc} + : + : + : "memory" + ); + unreachable; +} diff --git a/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig b/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig index 33bfdabcf..a8ed18290 100644 --- a/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig +++ b/lib/std/special/compiler_rt/arm/aeabi_dcmp.zig @@ -2,8 +2,6 @@ // // https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_dcmp.S -const compiler_rt_armhf_target = false; // TODO - const ConditionalOperator = enum { Eq, Lt, @@ -12,53 +10,42 @@ const ConditionalOperator = enum { Gt, }; -pub nakedcc fn __aeabi_dcmpeq() noreturn { +pub fn __aeabi_dcmpeq() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_dcmp, .Eq); + @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Eq}); unreachable; } -pub nakedcc fn __aeabi_dcmplt() noreturn { +pub fn __aeabi_dcmplt() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_dcmp, .Lt); + @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Lt}); unreachable; } -pub nakedcc fn __aeabi_dcmple() noreturn { +pub fn __aeabi_dcmple() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_dcmp, .Le); + @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Le}); unreachable; } -pub nakedcc fn __aeabi_dcmpge() noreturn { +pub fn __aeabi_dcmpge() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_dcmp, .Ge); + @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Ge}); unreachable; } -pub nakedcc fn __aeabi_dcmpgt() noreturn { +pub fn __aeabi_dcmpgt() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_dcmp, .Gt); + @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Gt}); unreachable; } -inline fn convert_dcmp_args_to_df2_args() void { - asm volatile ( - \\ vmov d0, r0, r1 - \\ vmov d1, r2, r3 - ); -} - fn aeabi_dcmp(comptime cond: ConditionalOperator) void { @setRuntimeSafety(false); asm volatile ( \\ push { r4, lr } ); - if (compiler_rt_armhf_target) { - convert_dcmp_args_to_df2_args(); - } - switch (cond) { .Eq => asm volatile ( \\ bl __eqdf2 diff --git a/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig b/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig index cc5efc64f..0b4c0f0d4 100644 --- a/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig +++ b/lib/std/special/compiler_rt/arm/aeabi_fcmp.zig @@ -2,8 +2,6 @@ // // https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_fcmp.S -const compiler_rt_armhf_target = false; // TODO - const ConditionalOperator = enum { Eq, Lt, @@ -12,53 +10,42 @@ const ConditionalOperator = enum { Gt, }; -pub nakedcc fn __aeabi_fcmpeq() noreturn { +pub fn __aeabi_fcmpeq() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_fcmp, .Eq); + @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Eq}); unreachable; } -pub nakedcc fn __aeabi_fcmplt() noreturn { +pub fn __aeabi_fcmplt() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_fcmp, .Lt); + @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Lt}); unreachable; } -pub nakedcc fn __aeabi_fcmple() noreturn { +pub fn __aeabi_fcmple() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_fcmp, .Le); + @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Le}); unreachable; } -pub nakedcc fn __aeabi_fcmpge() noreturn { +pub fn __aeabi_fcmpge() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_fcmp, .Ge); + @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Ge}); unreachable; } -pub nakedcc fn __aeabi_fcmpgt() noreturn { +pub fn __aeabi_fcmpgt() callconv(.Naked) noreturn { @setRuntimeSafety(false); - @inlineCall(aeabi_fcmp, .Gt); + @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Gt}); unreachable; } -inline fn convert_fcmp_args_to_sf2_args() void { - asm volatile ( - \\ vmov s0, r0 - \\ vmov s1, r1 - ); -} - fn aeabi_fcmp(comptime cond: ConditionalOperator) void { @setRuntimeSafety(false); asm volatile ( \\ push { r4, lr } ); - if (compiler_rt_armhf_target) { - convert_fcmp_args_to_sf2_args(); - } - switch (cond) { .Eq => asm volatile ( \\ bl __eqsf2 diff --git a/lib/std/special/compiler_rt/ashlti3.zig b/lib/std/special/compiler_rt/ashlti3.zig index 65b23f22e..211515f9d 100644 --- a/lib/std/special/compiler_rt/ashlti3.zig +++ b/lib/std/special/compiler_rt/ashlti3.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __ashlti3(a: i128, b: i32) i128 { +pub fn __ashlti3(a: i128, b: i32) callconv(.C) i128 { var input = twords{ .all = a }; var result: twords = undefined; diff --git a/lib/std/special/compiler_rt/ashrti3.zig b/lib/std/special/compiler_rt/ashrti3.zig index 40ee89c3c..1bcd40d2e 100644 --- a/lib/std/special/compiler_rt/ashrti3.zig +++ b/lib/std/special/compiler_rt/ashrti3.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __ashrti3(a: i128, b: i32) i128 { +pub fn __ashrti3(a: i128, b: i32) callconv(.C) i128 { var input = twords{ .all = a }; var result: twords = undefined; diff --git a/lib/std/special/compiler_rt/ashrti3_test.zig b/lib/std/special/compiler_rt/ashrti3_test.zig index ad3164be8..6a7565e55 100644 --- a/lib/std/special/compiler_rt/ashrti3_test.zig +++ b/lib/std/special/compiler_rt/ashrti3_test.zig @@ -3,8 +3,8 @@ const testing = @import("std").testing; fn test__ashrti3(a: i128, b: i32, expected: i128) void { const x = __ashrti3(a, b); - // @import("std").debug.warn("got 0x{x}\nexp 0x{x}\n", @truncate(u64, - // @bitCast(u128, x) >> 64), @truncate(u64, @bitCast(u128, expected)) >> 64); + // @import("std").debug.warn("got 0x{x}\nexp 0x{x}\n", .{@truncate(u64, + // @bitCast(u128, x) >> 64), @truncate(u64, @bitCast(u128, expected)) >> 64}); testing.expect(x == expected); } diff --git a/lib/std/special/compiler_rt/aulldiv.zig b/lib/std/special/compiler_rt/aulldiv.zig index d99bc94ff..2e2dd364d 100644 --- a/lib/std/special/compiler_rt/aulldiv.zig +++ b/lib/std/special/compiler_rt/aulldiv.zig @@ -1,55 +1,76 @@ -pub nakedcc fn _aulldiv() void { +const builtin = @import("builtin"); + +pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 { + @setRuntimeSafety(builtin.is_test); + const s_a = a >> (i64.bit_count - 1); + const s_b = b >> (i64.bit_count - 1); + + const an = (a ^ s_a) -% s_a; + const bn = (b ^ s_b) -% s_b; + + const r = @bitCast(u64, an) / @bitCast(u64, bn); + const s = s_a ^ s_b; + return (@bitCast(i64, r) ^ s) -% s; +} + +pub fn _aulldiv() callconv(.Naked) void { @setRuntimeSafety(false); + + // The stack layout is: + // ESP+16 divisor (hi) + // ESP+12 divisor (low) + // ESP+8 dividend (hi) + // ESP+4 dividend (low) + // ESP return address + asm volatile ( - \\.intel_syntax noprefix - \\ - \\ push ebx - \\ push esi - \\ mov eax,dword ptr [esp+18h] - \\ or eax,eax - \\ jne L1 - \\ mov ecx,dword ptr [esp+14h] - \\ mov eax,dword ptr [esp+10h] - \\ xor edx,edx - \\ div ecx - \\ mov ebx,eax - \\ mov eax,dword ptr [esp+0Ch] - \\ div ecx - \\ mov edx,ebx - \\ jmp L2 - \\ L1: - \\ mov ecx,eax - \\ mov ebx,dword ptr [esp+14h] - \\ mov edx,dword ptr [esp+10h] - \\ mov eax,dword ptr [esp+0Ch] - \\ L3: - \\ shr ecx,1 - \\ rcr ebx,1 - \\ shr edx,1 - \\ rcr eax,1 - \\ or ecx,ecx - \\ jne L3 - \\ div ebx - \\ mov esi,eax - \\ mul dword ptr [esp+18h] - \\ mov ecx,eax - \\ mov eax,dword ptr [esp+14h] - \\ mul esi - \\ add edx,ecx - \\ jb L4 - \\ cmp edx,dword ptr [esp+10h] - \\ ja L4 - \\ jb L5 - \\ cmp eax,dword ptr [esp+0Ch] - \\ jbe L5 - \\ L4: - \\ dec esi - \\ L5: - \\ xor edx,edx - \\ mov eax,esi - \\ L2: - \\ pop esi - \\ pop ebx - \\ ret 10h + \\ push %%ebx + \\ push %%esi + \\ mov 0x18(%%esp),%%eax + \\ or %%eax,%%eax + \\ jne 1f + \\ mov 0x14(%%esp),%%ecx + \\ mov 0x10(%%esp),%%eax + \\ xor %%edx,%%edx + \\ div %%ecx + \\ mov %%eax,%%ebx + \\ mov 0xc(%%esp),%%eax + \\ div %%ecx + \\ mov %%ebx,%%edx + \\ jmp 5f + \\ 1: + \\ mov %%eax,%%ecx + \\ mov 0x14(%%esp),%%ebx + \\ mov 0x10(%%esp),%%edx + \\ mov 0xc(%%esp),%%eax + \\ 2: + \\ shr %%ecx + \\ rcr %%ebx + \\ shr %%edx + \\ rcr %%eax + \\ or %%ecx,%%ecx + \\ jne 2b + \\ div %%ebx + \\ mov %%eax,%%esi + \\ mull 0x18(%%esp) + \\ mov %%eax,%%ecx + \\ mov 0x14(%%esp),%%eax + \\ mul %%esi + \\ add %%ecx,%%edx + \\ jb 3f + \\ cmp 0x10(%%esp),%%edx + \\ ja 3f + \\ jb 4f + \\ cmp 0xc(%%esp),%%eax + \\ jbe 4f + \\ 3: + \\ dec %%esi + \\ 4: + \\ xor %%edx,%%edx + \\ mov %%esi,%%eax + \\ 5: + \\ pop %%esi + \\ pop %%ebx + \\ ret $0x10 ); } diff --git a/lib/std/special/compiler_rt/aullrem.zig b/lib/std/special/compiler_rt/aullrem.zig index 51c4eebe3..4244f1bb8 100644 --- a/lib/std/special/compiler_rt/aullrem.zig +++ b/lib/std/special/compiler_rt/aullrem.zig @@ -1,56 +1,77 @@ -pub nakedcc fn _aullrem() void { +const builtin = @import("builtin"); + +pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 { + @setRuntimeSafety(builtin.is_test); + const s_a = a >> (i64.bit_count - 1); + const s_b = b >> (i64.bit_count - 1); + + const an = (a ^ s_a) -% s_a; + const bn = (b ^ s_b) -% s_b; + + const r = @bitCast(u64, an) % @bitCast(u64, bn); + const s = s_a ^ s_b; + return (@bitCast(i64, r) ^ s) -% s; +} + +pub fn _aullrem() callconv(.Naked) void { @setRuntimeSafety(false); + + // The stack layout is: + // ESP+16 divisor (hi) + // ESP+12 divisor (low) + // ESP+8 dividend (hi) + // ESP+4 dividend (low) + // ESP return address + asm volatile ( - \\.intel_syntax noprefix - \\ - \\ push ebx - \\ mov eax,dword ptr [esp+14h] - \\ or eax,eax - \\ jne L1a - \\ mov ecx,dword ptr [esp+10h] - \\ mov eax,dword ptr [esp+0Ch] - \\ xor edx,edx - \\ div ecx - \\ mov eax,dword ptr [esp+8] - \\ div ecx - \\ mov eax,edx - \\ xor edx,edx - \\ jmp L2a - \\ L1a: - \\ mov ecx,eax - \\ mov ebx,dword ptr [esp+10h] - \\ mov edx,dword ptr [esp+0Ch] - \\ mov eax,dword ptr [esp+8] - \\ L3a: - \\ shr ecx,1 - \\ rcr ebx,1 - \\ shr edx,1 - \\ rcr eax,1 - \\ or ecx,ecx - \\ jne L3a - \\ div ebx - \\ mov ecx,eax - \\ mul dword ptr [esp+14h] - \\ xchg eax,ecx - \\ mul dword ptr [esp+10h] - \\ add edx,ecx - \\ jb L4a - \\ cmp edx,dword ptr [esp+0Ch] - \\ ja L4a - \\ jb L5a - \\ cmp eax,dword ptr [esp+8] - \\ jbe L5a - \\ L4a: - \\ sub eax,dword ptr [esp+10h] - \\ sbb edx,dword ptr [esp+14h] - \\ L5a: - \\ sub eax,dword ptr [esp+8] - \\ sbb edx,dword ptr [esp+0Ch] - \\ neg edx - \\ neg eax - \\ sbb edx,0 - \\ L2a: - \\ pop ebx - \\ ret 10h + \\ push %%ebx + \\ mov 0x14(%%esp),%%eax + \\ or %%eax,%%eax + \\ jne 1f + \\ mov 0x10(%%esp),%%ecx + \\ mov 0xc(%%esp),%%eax + \\ xor %%edx,%%edx + \\ div %%ecx + \\ mov 0x8(%%esp),%%eax + \\ div %%ecx + \\ mov %%edx,%%eax + \\ xor %%edx,%%edx + \\ jmp 6f + \\ 1: + \\ mov %%eax,%%ecx + \\ mov 0x10(%%esp),%%ebx + \\ mov 0xc(%%esp),%%edx + \\ mov 0x8(%%esp),%%eax + \\ 2: + \\ shr %%ecx + \\ rcr %%ebx + \\ shr %%edx + \\ rcr %%eax + \\ or %%ecx,%%ecx + \\ jne 2b + \\ div %%ebx + \\ mov %%eax,%%ecx + \\ mull 0x14(%%esp) + \\ xchg %%eax,%%ecx + \\ mull 0x10(%%esp) + \\ add %%ecx,%%edx + \\ jb 3f + \\ cmp 0xc(%%esp),%%edx + \\ ja 3f + \\ jb 4f + \\ cmp 0x8(%%esp),%%eax + \\ jbe 4f + \\ 3: + \\ sub 0x10(%%esp),%%eax + \\ sbb 0x14(%%esp),%%edx + \\ 4: + \\ sub 0x8(%%esp),%%eax + \\ sbb 0xc(%%esp),%%edx + \\ neg %%edx + \\ neg %%eax + \\ sbb $0x0,%%edx + \\ 6: + \\ pop %%ebx + \\ ret $0x10 ); } diff --git a/lib/std/special/compiler_rt/comparedf2.zig b/lib/std/special/compiler_rt/comparedf2.zig index f97e2474b..98cca106f 100644 --- a/lib/std/special/compiler_rt/comparedf2.zig +++ b/lib/std/special/compiler_rt/comparedf2.zig @@ -13,21 +13,21 @@ const srep_t = i64; const typeWidth = rep_t.bit_count; const significandBits = std.math.floatMantissaBits(fp_t); const exponentBits = std.math.floatExponentBits(fp_t); -const signBit = (rep_t(1) << (significandBits + exponentBits)); +const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); const absMask = signBit - 1; -const implicitBit = rep_t(1) << significandBits; +const implicitBit = @as(rep_t, 1) << significandBits; const significandMask = implicitBit - 1; const exponentMask = absMask ^ significandMask; const infRep = @bitCast(rep_t, std.math.inf(fp_t)); // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = c_int(-1); -const LE_EQUAL = c_int(0); -const LE_GREATER = c_int(1); -const LE_UNORDERED = c_int(1); +const LE_LESS = @as(c_int, -1); +const LE_EQUAL = @as(c_int, 0); +const LE_GREATER = @as(c_int, 1); +const LE_UNORDERED = @as(c_int, 1); -pub extern fn __ledf2(a: fp_t, b: fp_t) c_int { +pub fn __ledf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt: srep_t = @bitCast(srep_t, a); const bInt: srep_t = @bitCast(srep_t, b); @@ -65,12 +65,12 @@ pub extern fn __ledf2(a: fp_t, b: fp_t) c_int { // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = c_int(-1); -const GE_EQUAL = c_int(0); -const GE_GREATER = c_int(1); -const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED +const GE_LESS = @as(c_int, -1); +const GE_EQUAL = @as(c_int, 0); +const GE_GREATER = @as(c_int, 1); +const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED -pub extern fn __gedf2(a: fp_t, b: fp_t) c_int { +pub fn __gedf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt: srep_t = @bitCast(srep_t, a); const bInt: srep_t = @bitCast(srep_t, b); @@ -94,29 +94,34 @@ pub extern fn __gedf2(a: fp_t, b: fp_t) c_int { } } -pub extern fn __unorddf2(a: fp_t, b: fp_t) c_int { +pub fn __unorddf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aAbs: rep_t = @bitCast(rep_t, a) & absMask; const bAbs: rep_t = @bitCast(rep_t, b) & absMask; return @boolToInt(aAbs > infRep or bAbs > infRep); } -pub extern fn __eqdf2(a: fp_t, b: fp_t) c_int { +pub fn __eqdf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __ledf2(a, b); } -pub extern fn __ltdf2(a: fp_t, b: fp_t) c_int { +pub fn __ltdf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __ledf2(a, b); } -pub extern fn __nedf2(a: fp_t, b: fp_t) c_int { +pub fn __nedf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __ledf2(a, b); } -pub extern fn __gtdf2(a: fp_t, b: fp_t) c_int { +pub fn __gtdf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __gedf2(a, b); } +pub fn __aeabi_dcmpun(a: fp_t, b: fp_t) callconv(.AAPCS) c_int { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __unorddf2, .{ a, b }); +} + test "import comparedf2" { _ = @import("comparedf2_test.zig"); } diff --git a/lib/std/special/compiler_rt/comparesf2.zig b/lib/std/special/compiler_rt/comparesf2.zig index e99e0bb3d..bd881af2a 100644 --- a/lib/std/special/compiler_rt/comparesf2.zig +++ b/lib/std/special/compiler_rt/comparesf2.zig @@ -13,21 +13,21 @@ const srep_t = i32; const typeWidth = rep_t.bit_count; const significandBits = std.math.floatMantissaBits(fp_t); const exponentBits = std.math.floatExponentBits(fp_t); -const signBit = (rep_t(1) << (significandBits + exponentBits)); +const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); const absMask = signBit - 1; -const implicitBit = rep_t(1) << significandBits; +const implicitBit = @as(rep_t, 1) << significandBits; const significandMask = implicitBit - 1; const exponentMask = absMask ^ significandMask; const infRep = @bitCast(rep_t, std.math.inf(fp_t)); // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = c_int(-1); -const LE_EQUAL = c_int(0); -const LE_GREATER = c_int(1); -const LE_UNORDERED = c_int(1); +const LE_LESS = @as(c_int, -1); +const LE_EQUAL = @as(c_int, 0); +const LE_GREATER = @as(c_int, 1); +const LE_UNORDERED = @as(c_int, 1); -pub extern fn __lesf2(a: fp_t, b: fp_t) c_int { +pub fn __lesf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt: srep_t = @bitCast(srep_t, a); const bInt: srep_t = @bitCast(srep_t, b); @@ -65,12 +65,12 @@ pub extern fn __lesf2(a: fp_t, b: fp_t) c_int { // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = c_int(-1); -const GE_EQUAL = c_int(0); -const GE_GREATER = c_int(1); -const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED +const GE_LESS = @as(c_int, -1); +const GE_EQUAL = @as(c_int, 0); +const GE_GREATER = @as(c_int, 1); +const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED -pub extern fn __gesf2(a: fp_t, b: fp_t) c_int { +pub fn __gesf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt: srep_t = @bitCast(srep_t, a); const bInt: srep_t = @bitCast(srep_t, b); @@ -94,29 +94,34 @@ pub extern fn __gesf2(a: fp_t, b: fp_t) c_int { } } -pub extern fn __unordsf2(a: fp_t, b: fp_t) c_int { +pub fn __unordsf2(a: fp_t, b: fp_t) callconv(.C) c_int { @setRuntimeSafety(is_test); const aAbs: rep_t = @bitCast(rep_t, a) & absMask; const bAbs: rep_t = @bitCast(rep_t, b) & absMask; return @boolToInt(aAbs > infRep or bAbs > infRep); } -pub extern fn __eqsf2(a: fp_t, b: fp_t) c_int { +pub fn __eqsf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __lesf2(a, b); } -pub extern fn __ltsf2(a: fp_t, b: fp_t) c_int { +pub fn __ltsf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __lesf2(a, b); } -pub extern fn __nesf2(a: fp_t, b: fp_t) c_int { +pub fn __nesf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __lesf2(a, b); } -pub extern fn __gtsf2(a: fp_t, b: fp_t) c_int { +pub fn __gtsf2(a: fp_t, b: fp_t) callconv(.C) c_int { return __gesf2(a, b); } +pub fn __aeabi_fcmpun(a: fp_t, b: fp_t) callconv(.AAPCS) c_int { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __unordsf2, .{ a, b }); +} + test "import comparesf2" { _ = @import("comparesf2_test.zig"); } diff --git a/lib/std/special/compiler_rt/comparetf2.zig b/lib/std/special/compiler_rt/comparetf2.zig index 05e597455..f2969f211 100644 --- a/lib/std/special/compiler_rt/comparetf2.zig +++ b/lib/std/special/compiler_rt/comparetf2.zig @@ -1,9 +1,9 @@ // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const LE_LESS = c_int(-1); -const LE_EQUAL = c_int(0); -const LE_GREATER = c_int(1); -const LE_UNORDERED = c_int(1); +const LE_LESS = @as(c_int, -1); +const LE_EQUAL = @as(c_int, 0); +const LE_GREATER = @as(c_int, 1); +const LE_UNORDERED = @as(c_int, 1); const rep_t = u128; const srep_t = i128; @@ -11,9 +11,9 @@ const srep_t = i128; const typeWidth = rep_t.bit_count; const significandBits = 112; const exponentBits = (typeWidth - significandBits - 1); -const signBit = (rep_t(1) << (significandBits + exponentBits)); +const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); const absMask = signBit - 1; -const implicitBit = rep_t(1) << significandBits; +const implicitBit = @as(rep_t, 1) << significandBits; const significandMask = implicitBit - 1; const exponentMask = absMask ^ significandMask; const infRep = exponentMask; @@ -21,7 +21,7 @@ const infRep = exponentMask; const builtin = @import("builtin"); const is_test = builtin.is_test; -pub extern fn __letf2(a: f128, b: f128) c_int { +pub fn __letf2(a: f128, b: f128) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt = @bitCast(rep_t, a); @@ -60,12 +60,12 @@ pub extern fn __letf2(a: f128, b: f128) c_int { // TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int -const GE_LESS = c_int(-1); -const GE_EQUAL = c_int(0); -const GE_GREATER = c_int(1); -const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED +const GE_LESS = @as(c_int, -1); +const GE_EQUAL = @as(c_int, 0); +const GE_GREATER = @as(c_int, 1); +const GE_UNORDERED = @as(c_int, -1); // Note: different from LE_UNORDERED -pub extern fn __getf2(a: f128, b: f128) c_int { +pub fn __getf2(a: f128, b: f128) callconv(.C) c_int { @setRuntimeSafety(is_test); const aInt = @bitCast(srep_t, a); @@ -90,7 +90,7 @@ pub extern fn __getf2(a: f128, b: f128) c_int { GE_GREATER; } -pub extern fn __unordtf2(a: f128, b: f128) c_int { +pub fn __unordtf2(a: f128, b: f128) callconv(.C) c_int { @setRuntimeSafety(is_test); const aAbs = @bitCast(rep_t, a) & absMask; diff --git a/lib/std/special/compiler_rt/divdf3.zig b/lib/std/special/compiler_rt/divdf3.zig index 072feaec6..f0c0d1247 100644 --- a/lib/std/special/compiler_rt/divdf3.zig +++ b/lib/std/special/compiler_rt/divdf3.zig @@ -5,7 +5,7 @@ const std = @import("std"); const builtin = @import("builtin"); -pub extern fn __divdf3(a: f64, b: f64) f64 { +pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, f64.bit_count); const SignedZ = @IntType(true, f64.bit_count); @@ -14,11 +14,11 @@ pub extern fn __divdf3(a: f64, b: f64) f64 { const significandBits = std.math.floatMantissaBits(f64); const exponentBits = std.math.floatExponentBits(f64); - const signBit = (Z(1) << (significandBits + exponentBits)); + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (Z(1) << significandBits); + const implicitBit = (@as(Z, 1) << significandBits); const quietBit = implicitBit >> 1; const significandMask = implicitBit - 1; @@ -91,7 +91,7 @@ pub extern fn __divdf3(a: f64, b: f64) f64 { // polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This // is accurate to about 3.5 binary digits. const q31b: u32 = @truncate(u32, bSignificand >> 21); - var recip32 = u32(0x7504f333) -% q31b; + var recip32 = @as(u32, 0x7504f333) -% q31b; // Now refine the reciprocal estimate using a Newton-Raphson iteration: // @@ -101,12 +101,12 @@ pub extern fn __divdf3(a: f64, b: f64) f64 { // with each iteration, so after three iterations, we have about 28 binary // digits of accuracy. var correction32: u32 = undefined; - correction32 = @truncate(u32, ~(u64(recip32) *% q31b >> 32) +% 1); - recip32 = @truncate(u32, u64(recip32) *% correction32 >> 31); - correction32 = @truncate(u32, ~(u64(recip32) *% q31b >> 32) +% 1); - recip32 = @truncate(u32, u64(recip32) *% correction32 >> 31); - correction32 = @truncate(u32, ~(u64(recip32) *% q31b >> 32) +% 1); - recip32 = @truncate(u32, u64(recip32) *% correction32 >> 31); + correction32 = @truncate(u32, ~(@as(u64, recip32) *% q31b >> 32) +% 1); + recip32 = @truncate(u32, @as(u64, recip32) *% correction32 >> 31); + correction32 = @truncate(u32, ~(@as(u64, recip32) *% q31b >> 32) +% 1); + recip32 = @truncate(u32, @as(u64, recip32) *% correction32 >> 31); + correction32 = @truncate(u32, ~(@as(u64, recip32) *% q31b >> 32) +% 1); + recip32 = @truncate(u32, @as(u64, recip32) *% correction32 >> 31); // recip32 might have overflowed to exactly zero in the preceding // computation if the high word of b is exactly 1.0. This would sabotage @@ -119,10 +119,10 @@ pub extern fn __divdf3(a: f64, b: f64) f64 { const q63blo: u32 = @truncate(u32, bSignificand << 11); var correction: u64 = undefined; var reciprocal: u64 = undefined; - correction = ~(u64(recip32) *% q31b +% (u64(recip32) *% q63blo >> 32)) +% 1; + correction = ~(@as(u64, recip32) *% q31b +% (@as(u64, recip32) *% q63blo >> 32)) +% 1; const cHi = @truncate(u32, correction >> 32); const cLo = @truncate(u32, correction); - reciprocal = u64(recip32) *% cHi +% (u64(recip32) *% cLo >> 32); + reciprocal = @as(u64, recip32) *% cHi +% (@as(u64, recip32) *% cLo >> 32); // We already adjusted the 32-bit estimate, now we need to adjust the final // 64-bit reciprocal estimate downward to ensure that it is strictly smaller @@ -195,7 +195,7 @@ pub extern fn __divdf3(a: f64, b: f64) f64 { // Clear the implicit bit var absResult = quotient & significandMask; // Insert the exponent - absResult |= @bitCast(Z, SignedZ(writtenExponent)) << significandBits; + absResult |= @bitCast(Z, @as(SignedZ, writtenExponent)) << significandBits; // Round absResult +%= round; // Insert the sign and return @@ -208,7 +208,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { switch (Z) { u32 => { // 32x32 --> 64 bit multiply - const product = u64(a) * u64(b); + const product = @as(u64, a) * @as(u64, b); hi.* = @truncate(u32, product >> 32); lo.* = @truncate(u32, product); }, @@ -237,9 +237,9 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; }, u128 => { - const Word_LoMask = u64(0x00000000ffffffff); - const Word_HiMask = u64(0xffffffff00000000); - const Word_FullMask = u64(0xffffffffffffffff); + const Word_LoMask = @as(u64, 0x00000000ffffffff); + const Word_HiMask = @as(u64, 0xffffffff00000000); + const Word_FullMask = @as(u64, 0xffffffffffffffff); const S = struct { fn Word_1(x: u128) u64 { return @truncate(u32, x >> 96); @@ -275,22 +275,22 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { const product43: u64 = S.Word_4(a) * S.Word_3(b); const product44: u64 = S.Word_4(a) * S.Word_4(b); - const sum0: u128 = u128(product44); - const sum1: u128 = u128(product34) +% - u128(product43); - const sum2: u128 = u128(product24) +% - u128(product33) +% - u128(product42); - const sum3: u128 = u128(product14) +% - u128(product23) +% - u128(product32) +% - u128(product41); - const sum4: u128 = u128(product13) +% - u128(product22) +% - u128(product31); - const sum5: u128 = u128(product12) +% - u128(product21); - const sum6: u128 = u128(product11); + const sum0: u128 = @as(u128, product44); + const sum1: u128 = @as(u128, product34) +% + @as(u128, product43); + const sum2: u128 = @as(u128, product24) +% + @as(u128, product33) +% + @as(u128, product42); + const sum3: u128 = @as(u128, product14) +% + @as(u128, product23) +% + @as(u128, product32) +% + @as(u128, product41); + const sum4: u128 = @as(u128, product13) +% + @as(u128, product22) +% + @as(u128, product31); + const sum5: u128 = @as(u128, product12) +% + @as(u128, product21); + const sum6: u128 = @as(u128, product11); const r0: u128 = (sum0 & Word_FullMask) +% ((sum1 & Word_LoMask) << 32); @@ -316,13 +316,18 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, T.bit_count); const significandBits = std.math.floatMantissaBits(T); - const implicitBit = Z(1) << significandBits; + const implicitBit = @as(Z, 1) << significandBits; const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); significand.* <<= @intCast(std.math.Log2Int(Z), shift); return 1 - shift; } +pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __divdf3, .{ a, b }); +} + test "import divdf3" { _ = @import("divdf3_test.zig"); } diff --git a/lib/std/special/compiler_rt/divsf3.zig b/lib/std/special/compiler_rt/divsf3.zig index 447653fbe..e51f94e19 100644 --- a/lib/std/special/compiler_rt/divsf3.zig +++ b/lib/std/special/compiler_rt/divsf3.zig @@ -5,7 +5,7 @@ const std = @import("std"); const builtin = @import("builtin"); -pub extern fn __divsf3(a: f32, b: f32) f32 { +pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, f32.bit_count); @@ -13,11 +13,11 @@ pub extern fn __divsf3(a: f32, b: f32) f32 { const significandBits = std.math.floatMantissaBits(f32); const exponentBits = std.math.floatExponentBits(f32); - const signBit = (Z(1) << (significandBits + exponentBits)); + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (Z(1) << significandBits); + const implicitBit = (@as(Z, 1) << significandBits); const quietBit = implicitBit >> 1; const significandMask = implicitBit - 1; @@ -90,7 +90,7 @@ pub extern fn __divsf3(a: f32, b: f32) f32 { // polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This // is accurate to about 3.5 binary digits. const q31b = bSignificand << 8; - var reciprocal = u32(0x7504f333) -% q31b; + var reciprocal = @as(u32, 0x7504f333) -% q31b; // Now refine the reciprocal estimate using a Newton-Raphson iteration: // @@ -100,12 +100,12 @@ pub extern fn __divsf3(a: f32, b: f32) f32 { // with each iteration, so after three iterations, we have about 28 binary // digits of accuracy. var correction: u32 = undefined; - correction = @truncate(u32, ~(u64(reciprocal) *% q31b >> 32) +% 1); - reciprocal = @truncate(u32, u64(reciprocal) *% correction >> 31); - correction = @truncate(u32, ~(u64(reciprocal) *% q31b >> 32) +% 1); - reciprocal = @truncate(u32, u64(reciprocal) *% correction >> 31); - correction = @truncate(u32, ~(u64(reciprocal) *% q31b >> 32) +% 1); - reciprocal = @truncate(u32, u64(reciprocal) *% correction >> 31); + correction = @truncate(u32, ~(@as(u64, reciprocal) *% q31b >> 32) +% 1); + reciprocal = @truncate(u32, @as(u64, reciprocal) *% correction >> 31); + correction = @truncate(u32, ~(@as(u64, reciprocal) *% q31b >> 32) +% 1); + reciprocal = @truncate(u32, @as(u64, reciprocal) *% correction >> 31); + correction = @truncate(u32, ~(@as(u64, reciprocal) *% q31b >> 32) +% 1); + reciprocal = @truncate(u32, @as(u64, reciprocal) *% correction >> 31); // Exhaustive testing shows that the error in reciprocal after three steps // is in the interval [-0x1.f58108p-31, 0x1.d0e48cp-29], in line with our @@ -127,7 +127,7 @@ pub extern fn __divsf3(a: f32, b: f32) f32 { // is the error in the reciprocal of b scaled by the maximum // possible value of a. As a consequence of this error bound, // either q or nextafter(q) is the correctly rounded - var quotient: Z = @truncate(u32, u64(reciprocal) *% (aSignificand << 1) >> 32); + var quotient: Z = @truncate(u32, @as(u64, reciprocal) *% (aSignificand << 1) >> 32); // Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0). // In either case, we are going to compute a residual of the form @@ -189,13 +189,18 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, T.bit_count); const significandBits = std.math.floatMantissaBits(T); - const implicitBit = Z(1) << significandBits; + const implicitBit = @as(Z, 1) << significandBits; const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); significand.* <<= @intCast(std.math.Log2Int(Z), shift); return 1 - shift; } +pub fn __aeabi_fdiv(a: f32, b: f32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __divsf3, .{ a, b }); +} + test "import divsf3" { _ = @import("divsf3_test.zig"); } diff --git a/lib/std/special/compiler_rt/divti3.zig b/lib/std/special/compiler_rt/divti3.zig index 477ce2cb9..2b878f526 100644 --- a/lib/std/special/compiler_rt/divti3.zig +++ b/lib/std/special/compiler_rt/divti3.zig @@ -1,7 +1,7 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -pub extern fn __divti3(a: i128, b: i128) i128 { +pub fn __divti3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); const s_a = a >> (i128.bit_count - 1); @@ -16,8 +16,11 @@ pub extern fn __divti3(a: i128, b: i128) i128 { } const v128 = @Vector(2, u64); -pub extern fn __divti3_windows_x86_64(a: v128, b: v128) v128 { - return @bitCast(v128, @inlineCall(__divti3, @bitCast(i128, a), @bitCast(i128, b))); +pub fn __divti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, @call(.{ .modifier = .always_inline }, __divti3, .{ + @bitCast(i128, a), + @bitCast(i128, b), + })); } test "import divti3" { diff --git a/lib/std/special/compiler_rt/divti3_test.zig b/lib/std/special/compiler_rt/divti3_test.zig index e1c1babae..8601cfae0 100644 --- a/lib/std/special/compiler_rt/divti3_test.zig +++ b/lib/std/special/compiler_rt/divti3_test.zig @@ -14,8 +14,8 @@ test "divti3" { test__divti3(-2, 1, -2); test__divti3(-2, -1, 2); - test__divti3(@bitCast(i128, u128(0x8 << 124)), 1, @bitCast(i128, u128(0x8 << 124))); - test__divti3(@bitCast(i128, u128(0x8 << 124)), -1, @bitCast(i128, u128(0x8 << 124))); - test__divti3(@bitCast(i128, u128(0x8 << 124)), -2, @bitCast(i128, u128(0x4 << 124))); - test__divti3(@bitCast(i128, u128(0x8 << 124)), 2, @bitCast(i128, u128(0xc << 124))); + test__divti3(@bitCast(i128, @as(u128, 0x8 << 124)), 1, @bitCast(i128, @as(u128, 0x8 << 124))); + test__divti3(@bitCast(i128, @as(u128, 0x8 << 124)), -1, @bitCast(i128, @as(u128, 0x8 << 124))); + test__divti3(@bitCast(i128, @as(u128, 0x8 << 124)), -2, @bitCast(i128, @as(u128, 0x4 << 124))); + test__divti3(@bitCast(i128, @as(u128, 0x8 << 124)), 2, @bitCast(i128, @as(u128, 0xc << 124))); } diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/std/special/compiler_rt/extendXfYf2.zig index e10667843..4e63d93ac 100644 --- a/lib/std/special/compiler_rt/extendXfYf2.zig +++ b/lib/std/special/compiler_rt/extendXfYf2.zig @@ -2,20 +2,30 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -pub extern fn __extendsfdf2(a: f32) f64 { - return @inlineCall(extendXfYf2, f64, f32, @bitCast(u32, a)); +pub fn __extendsfdf2(a: f32) callconv(.C) f64 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f64, f32, @bitCast(u32, a) }); } -pub extern fn __extenddftf2(a: f64) f128 { - return @inlineCall(extendXfYf2, f128, f64, @bitCast(u64, a)); +pub fn __extenddftf2(a: f64) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f64, @bitCast(u64, a) }); } -pub extern fn __extendsftf2(a: f32) f128 { - return @inlineCall(extendXfYf2, f128, f32, @bitCast(u32, a)); +pub fn __extendsftf2(a: f32) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f32, @bitCast(u32, a) }); } -pub extern fn __extendhfsf2(a: u16) f32 { - return @inlineCall(extendXfYf2, f32, f16, a); +pub fn __extendhfsf2(a: u16) callconv(.C) f32 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); +} + +pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg}); +} + +pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __extendsfdf2, .{arg}); } const CHAR_BIT = 8; @@ -49,7 +59,7 @@ fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: @IntType(false, @t const dstInfExp = (1 << dstExpBits) - 1; const dstExpBias = dstInfExp >> 1; - const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; + const dstMinNormal: dst_rep_t = @as(dst_rep_t, 1) << dstSigBits; // Break a into a sign and representation of the absolute value const aRep: src_rep_t = @bitCast(src_rep_t, a); @@ -61,7 +71,7 @@ fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: @IntType(false, @t // a is a normal number. // Extend to the destination type by shifting the significand and // exponent into the proper position and rebiasing the exponent. - absResult = dst_rep_t(aAbs) << (dstSigBits - srcSigBits); + absResult = @as(dst_rep_t, aAbs) << (dstSigBits - srcSigBits); absResult += (dstExpBias - srcExpBias) << dstSigBits; } else if (aAbs >= srcInfinity) { // a is NaN or infinity. @@ -69,15 +79,15 @@ fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: @IntType(false, @t // bit (if needed) and right-aligning the rest of the trailing NaN // payload field. absResult = dstInfExp << dstSigBits; - absResult |= dst_rep_t(aAbs & srcQNaN) << (dstSigBits - srcSigBits); - absResult |= dst_rep_t(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); + absResult |= @as(dst_rep_t, aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= @as(dst_rep_t, aAbs & srcNaNCode) << (dstSigBits - srcSigBits); } else if (aAbs != 0) { // a is denormal. // renormalize the significand and clear the leading bit, then insert // the correct adjusted exponent in the destination type. const scale: u32 = @clz(src_rep_t, aAbs) - - @clz(src_rep_t, src_rep_t(srcMinNormal)); - absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); + @clz(src_rep_t, @as(src_rep_t, srcMinNormal)); + absResult = @as(dst_rep_t, aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); absResult ^= dstMinNormal; const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1; absResult |= @intCast(dst_rep_t, resultExponent) << dstSigBits; @@ -87,7 +97,7 @@ fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: @IntType(false, @t } // Apply the signbit to (dst_t)abs(a). - const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << (dstBits - srcBits); + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @as(dst_rep_t, sign) << (dstBits - srcBits); return @bitCast(dst_t, result); } diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index 050a79982..aa2faae90 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; @@ -87,7 +88,10 @@ test "extenddftf2" { test "extendhfsf2" { test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN - test__extendhfsf2(0x7c01, 0x7f802000); // sNaN + // On x86 the NaN becomes quiet because the return is pushed on the x87 + // stack due to ABI requirements + if (builtin.arch != .i386 and builtin.os == .windows) + test__extendhfsf2(0x7c01, 0x7f802000); // sNaN test__extendhfsf2(0, 0); // 0 test__extendhfsf2(0x8000, 0x80000000); // -0 @@ -130,11 +134,11 @@ test "extendsftf2" { } fn makeQNaN64() f64 { - return @bitCast(f64, u64(0x7ff8000000000000)); + return @bitCast(f64, @as(u64, 0x7ff8000000000000)); } fn makeInf64() f64 { - return @bitCast(f64, u64(0x7ff0000000000000)); + return @bitCast(f64, @as(u64, 0x7ff0000000000000)); } fn makeNaN64(rand: u64) f64 { @@ -142,7 +146,7 @@ fn makeNaN64(rand: u64) f64 { } fn makeQNaN32() f32 { - return @bitCast(f32, u32(0x7fc00000)); + return @bitCast(f32, @as(u32, 0x7fc00000)); } fn makeNaN32(rand: u32) f32 { @@ -150,5 +154,5 @@ fn makeNaN32(rand: u32) f32 { } fn makeInf32() f32 { - return @bitCast(f32, u32(0x7f800000)); + return @bitCast(f32, @as(u32, 0x7f800000)); } diff --git a/lib/std/special/compiler_rt/fixdfdi.zig b/lib/std/special/compiler_rt/fixdfdi.zig index c108fd15a..11b500912 100644 --- a/lib/std/special/compiler_rt/fixdfdi.zig +++ b/lib/std/special/compiler_rt/fixdfdi.zig @@ -1,11 +1,16 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixdfdi(a: f64) i64 { +pub fn __fixdfdi(a: f64) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); return fixint(f64, i64, a); } +pub fn __aeabi_d2lz(arg: f64) callconv(.AAPCS) i64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixdfdi, .{arg}); +} + test "import fixdfdi" { _ = @import("fixdfdi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixdfdi_test.zig b/lib/std/special/compiler_rt/fixdfdi_test.zig index 1ba8a4f87..92dbdc2a5 100644 --- a/lib/std/special/compiler_rt/fixdfdi_test.zig +++ b/lib/std/special/compiler_rt/fixdfdi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixdfdi(a: f64, expected: i64) void { const x = __fixdfdi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u64({x})\n", a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u64, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u64, {x})\n", .{a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u64, expected)}); testing.expect(x == expected); } test "fixdfdi" { - //warn("\n"); + //warn("\n", .{}); test__fixdfdi(-math.f64_max, math.minInt(i64)); test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); diff --git a/lib/std/special/compiler_rt/fixdfsi.zig b/lib/std/special/compiler_rt/fixdfsi.zig index 83a17b2b0..8a6d8da34 100644 --- a/lib/std/special/compiler_rt/fixdfsi.zig +++ b/lib/std/special/compiler_rt/fixdfsi.zig @@ -1,11 +1,16 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixdfsi(a: f64) i32 { +pub fn __fixdfsi(a: f64) callconv(.C) i32 { @setRuntimeSafety(builtin.is_test); return fixint(f64, i32, a); } +pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixdfsi, .{a}); +} + test "import fixdfsi" { _ = @import("fixdfsi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixdfsi_test.zig b/lib/std/special/compiler_rt/fixdfsi_test.zig index fa5ff72e8..fb9051889 100644 --- a/lib/std/special/compiler_rt/fixdfsi_test.zig +++ b/lib/std/special/compiler_rt/fixdfsi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixdfsi(a: f64, expected: i32) void { const x = __fixdfsi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u64({x})\n", a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u32, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u64, {x})\n", .{a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u32, expected)}); testing.expect(x == expected); } test "fixdfsi" { - //warn("\n"); + //warn("\n", .{}); test__fixdfsi(-math.f64_max, math.minInt(i32)); test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); diff --git a/lib/std/special/compiler_rt/fixdfti.zig b/lib/std/special/compiler_rt/fixdfti.zig index e30f885cf..0e21f0ba1 100644 --- a/lib/std/special/compiler_rt/fixdfti.zig +++ b/lib/std/special/compiler_rt/fixdfti.zig @@ -1,7 +1,7 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixdfti(a: f64) i128 { +pub fn __fixdfti(a: f64) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); return fixint(f64, i128, a); } diff --git a/lib/std/special/compiler_rt/fixdfti_test.zig b/lib/std/special/compiler_rt/fixdfti_test.zig index 4ab2c04cd..f66e4a3d8 100644 --- a/lib/std/special/compiler_rt/fixdfti_test.zig +++ b/lib/std/special/compiler_rt/fixdfti_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixdfti(a: f64, expected: i128) void { const x = __fixdfti(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u64({x})\n", a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u128, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u64, {x})\n", .{a, @bitCast(u64, a), x, x, expected, expected, @bitCast(u128, expected)}); testing.expect(x == expected); } test "fixdfti" { - //warn("\n"); + //warn("\n", .{}); test__fixdfti(-math.f64_max, math.minInt(i128)); test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); diff --git a/lib/std/special/compiler_rt/fixint.zig b/lib/std/special/compiler_rt/fixint.zig index fd31798cc..426ee1d58 100644 --- a/lib/std/special/compiler_rt/fixint.zig +++ b/lib/std/special/compiler_rt/fixint.zig @@ -25,11 +25,11 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { const typeWidth = rep_t.bit_count; const exponentBits = (typeWidth - significandBits - 1); - const signBit = (rep_t(1) << (significandBits + exponentBits)); + const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (rep_t(1) << significandBits); + const implicitBit = (@as(rep_t, 1) << significandBits); const significandMask = (implicitBit - 1); // Break a into sign, exponent, significand @@ -51,7 +51,7 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { // If the value is too large for the integer type, saturate. if (@intCast(usize, exponent) >= fixint_t.bit_count) { - return if (negative) fixint_t(minInt(fixint_t)) else fixint_t(maxInt(fixint_t)); + return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t)); } // If 0 <= exponent < significandBits, right shift else left shift diff --git a/lib/std/special/compiler_rt/fixint_test.zig b/lib/std/special/compiler_rt/fixint_test.zig index a876e1726..4efb1e6ab 100644 --- a/lib/std/special/compiler_rt/fixint_test.zig +++ b/lib/std/special/compiler_rt/fixint_test.zig @@ -8,7 +8,7 @@ const fixint = @import("fixint.zig").fixint; fn test__fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t, expected: fixint_t) void { const x = fixint(fp_t, fixint_t, a); - //warn("a={} x={}:{x} expected={}:{x})\n", a, x, x, expected, expected); + //warn("a={} x={}:{x} expected={}:{x})\n", .{a, x, x, expected, expected}); testing.expect(x == expected); } @@ -81,8 +81,8 @@ test "fixint.i3" { test "fixint.i32" { test__fixint(f64, i32, -math.inf_f64, math.minInt(i32)); test__fixint(f64, i32, -math.f64_max, math.minInt(i32)); - test__fixint(f64, i32, f64(math.minInt(i32)), math.minInt(i32)); - test__fixint(f64, i32, f64(math.minInt(i32)) + 1, math.minInt(i32) + 1); + test__fixint(f64, i32, @as(f64, math.minInt(i32)), math.minInt(i32)); + test__fixint(f64, i32, @as(f64, math.minInt(i32)) + 1, math.minInt(i32) + 1); test__fixint(f64, i32, -2.0, -2); test__fixint(f64, i32, -1.9, -1); test__fixint(f64, i32, -1.1, -1); @@ -96,8 +96,8 @@ test "fixint.i32" { test__fixint(f64, i32, 0.1, 0); test__fixint(f64, i32, 0.9, 0); test__fixint(f64, i32, 1.0, 1); - test__fixint(f64, i32, f64(math.maxInt(i32)) - 1, math.maxInt(i32) - 1); - test__fixint(f64, i32, f64(math.maxInt(i32)), math.maxInt(i32)); + test__fixint(f64, i32, @as(f64, math.maxInt(i32)) - 1, math.maxInt(i32) - 1); + test__fixint(f64, i32, @as(f64, math.maxInt(i32)), math.maxInt(i32)); test__fixint(f64, i32, math.f64_max, math.maxInt(i32)); test__fixint(f64, i32, math.inf_f64, math.maxInt(i32)); } @@ -105,9 +105,9 @@ test "fixint.i32" { test "fixint.i64" { test__fixint(f64, i64, -math.inf_f64, math.minInt(i64)); test__fixint(f64, i64, -math.f64_max, math.minInt(i64)); - test__fixint(f64, i64, f64(math.minInt(i64)), math.minInt(i64)); - test__fixint(f64, i64, f64(math.minInt(i64)) + 1, math.minInt(i64)); - test__fixint(f64, i64, f64(math.minInt(i64) / 2), math.minInt(i64) / 2); + test__fixint(f64, i64, @as(f64, math.minInt(i64)), math.minInt(i64)); + test__fixint(f64, i64, @as(f64, math.minInt(i64)) + 1, math.minInt(i64)); + test__fixint(f64, i64, @as(f64, math.minInt(i64) / 2), math.minInt(i64) / 2); test__fixint(f64, i64, -2.0, -2); test__fixint(f64, i64, -1.9, -1); test__fixint(f64, i64, -1.1, -1); @@ -121,8 +121,8 @@ test "fixint.i64" { test__fixint(f64, i64, 0.1, 0); test__fixint(f64, i64, 0.9, 0); test__fixint(f64, i64, 1.0, 1); - test__fixint(f64, i64, f64(math.maxInt(i64)) - 1, math.maxInt(i64)); - test__fixint(f64, i64, f64(math.maxInt(i64)), math.maxInt(i64)); + test__fixint(f64, i64, @as(f64, math.maxInt(i64)) - 1, math.maxInt(i64)); + test__fixint(f64, i64, @as(f64, math.maxInt(i64)), math.maxInt(i64)); test__fixint(f64, i64, math.f64_max, math.maxInt(i64)); test__fixint(f64, i64, math.inf_f64, math.maxInt(i64)); } @@ -130,8 +130,8 @@ test "fixint.i64" { test "fixint.i128" { test__fixint(f64, i128, -math.inf_f64, math.minInt(i128)); test__fixint(f64, i128, -math.f64_max, math.minInt(i128)); - test__fixint(f64, i128, f64(math.minInt(i128)), math.minInt(i128)); - test__fixint(f64, i128, f64(math.minInt(i128)) + 1, math.minInt(i128)); + test__fixint(f64, i128, @as(f64, math.minInt(i128)), math.minInt(i128)); + test__fixint(f64, i128, @as(f64, math.minInt(i128)) + 1, math.minInt(i128)); test__fixint(f64, i128, -2.0, -2); test__fixint(f64, i128, -1.9, -1); test__fixint(f64, i128, -1.1, -1); @@ -145,8 +145,8 @@ test "fixint.i128" { test__fixint(f64, i128, 0.1, 0); test__fixint(f64, i128, 0.9, 0); test__fixint(f64, i128, 1.0, 1); - test__fixint(f64, i128, f64(math.maxInt(i128)) - 1, math.maxInt(i128)); - test__fixint(f64, i128, f64(math.maxInt(i128)), math.maxInt(i128)); + test__fixint(f64, i128, @as(f64, math.maxInt(i128)) - 1, math.maxInt(i128)); + test__fixint(f64, i128, @as(f64, math.maxInt(i128)), math.maxInt(i128)); test__fixint(f64, i128, math.f64_max, math.maxInt(i128)); test__fixint(f64, i128, math.inf_f64, math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/fixsfdi.zig b/lib/std/special/compiler_rt/fixsfdi.zig index ffa81d13a..d0d958cdd 100644 --- a/lib/std/special/compiler_rt/fixsfdi.zig +++ b/lib/std/special/compiler_rt/fixsfdi.zig @@ -1,11 +1,16 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixsfdi(a: f32) i64 { +pub fn __fixsfdi(a: f32) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); return fixint(f32, i64, a); } +pub fn __aeabi_f2lz(arg: f32) callconv(.AAPCS) i64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixsfdi, .{arg}); +} + test "import fixsfdi" { _ = @import("fixsfdi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixsfdi_test.zig b/lib/std/special/compiler_rt/fixsfdi_test.zig index bfd715425..5888c9804 100644 --- a/lib/std/special/compiler_rt/fixsfdi_test.zig +++ b/lib/std/special/compiler_rt/fixsfdi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixsfdi(a: f32, expected: i64) void { const x = __fixsfdi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u32({x})\n", a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u64, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u32, {x})\n", .{a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u64, expected)}); testing.expect(x == expected); } test "fixsfdi" { - //warn("\n"); + //warn("\n", .{}); test__fixsfdi(-math.f32_max, math.minInt(i64)); test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); diff --git a/lib/std/special/compiler_rt/fixsfsi.zig b/lib/std/special/compiler_rt/fixsfsi.zig index 9a94b4395..84395d0fa 100644 --- a/lib/std/special/compiler_rt/fixsfsi.zig +++ b/lib/std/special/compiler_rt/fixsfsi.zig @@ -1,11 +1,16 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixsfsi(a: f32) i32 { +pub fn __fixsfsi(a: f32) callconv(.C) i32 { @setRuntimeSafety(builtin.is_test); return fixint(f32, i32, a); } +pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixsfsi, .{a}); +} + test "import fixsfsi" { _ = @import("fixsfsi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixsfsi_test.zig b/lib/std/special/compiler_rt/fixsfsi_test.zig index 30406fea2..844235bc9 100644 --- a/lib/std/special/compiler_rt/fixsfsi_test.zig +++ b/lib/std/special/compiler_rt/fixsfsi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixsfsi(a: f32, expected: i32) void { const x = __fixsfsi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u32({x})\n", a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u32, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u32, {x})\n", .{a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u32, expected)}); testing.expect(x == expected); } test "fixsfsi" { - //warn("\n"); + //warn("\n", .{}); test__fixsfsi(-math.f32_max, math.minInt(i32)); test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); diff --git a/lib/std/special/compiler_rt/fixsfti.zig b/lib/std/special/compiler_rt/fixsfti.zig index 806a1678a..d7ce4b3a6 100644 --- a/lib/std/special/compiler_rt/fixsfti.zig +++ b/lib/std/special/compiler_rt/fixsfti.zig @@ -1,7 +1,7 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixsfti(a: f32) i128 { +pub fn __fixsfti(a: f32) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); return fixint(f32, i128, a); } diff --git a/lib/std/special/compiler_rt/fixsfti_test.zig b/lib/std/special/compiler_rt/fixsfti_test.zig index 7136f7cfe..9eb2883f3 100644 --- a/lib/std/special/compiler_rt/fixsfti_test.zig +++ b/lib/std/special/compiler_rt/fixsfti_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixsfti(a: f32, expected: i128) void { const x = __fixsfti(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u128({x})\n", a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u128, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u128, {x})\n", .{a, @bitCast(u32, a), x, x, expected, expected, @bitCast(u128, expected)}); testing.expect(x == expected); } test "fixsfti" { - //warn("\n"); + //warn("\n", .{}); test__fixsfti(-math.f32_max, math.minInt(i128)); test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); diff --git a/lib/std/special/compiler_rt/fixtfdi.zig b/lib/std/special/compiler_rt/fixtfdi.zig index 8d99231b7..0ef3aa225 100644 --- a/lib/std/special/compiler_rt/fixtfdi.zig +++ b/lib/std/special/compiler_rt/fixtfdi.zig @@ -1,7 +1,7 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixtfdi(a: f128) i64 { +pub fn __fixtfdi(a: f128) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); return fixint(f128, i64, a); } diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig index 7c6354764..6baa9011c 100644 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ b/lib/std/special/compiler_rt/fixtfdi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixtfdi(a: f128, expected: i64) void { const x = __fixtfdi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u64({x})\n", a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u64, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u64, {x})\n", .{a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u64, expected)}); testing.expect(x == expected); } test "fixtfdi" { - //warn("\n"); + //warn("\n", .{}); test__fixtfdi(-math.f128_max, math.minInt(i64)); test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); diff --git a/lib/std/special/compiler_rt/fixtfsi.zig b/lib/std/special/compiler_rt/fixtfsi.zig index f3f83634b..15e89a11b 100644 --- a/lib/std/special/compiler_rt/fixtfsi.zig +++ b/lib/std/special/compiler_rt/fixtfsi.zig @@ -1,7 +1,7 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixtfsi(a: f128) i32 { +pub fn __fixtfsi(a: f128) callconv(.C) i32 { @setRuntimeSafety(builtin.is_test); return fixint(f128, i32, a); } diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig index da769089d..c7294fe25 100644 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ b/lib/std/special/compiler_rt/fixtfsi_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixtfsi(a: f128, expected: i32) void { const x = __fixtfsi(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u32({x})\n", a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u32, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u32, {x})\n", .{a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u32, expected)}); testing.expect(x == expected); } test "fixtfsi" { - //warn("\n"); + //warn("\n", .{}); test__fixtfsi(-math.f128_max, math.minInt(i32)); test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); diff --git a/lib/std/special/compiler_rt/fixtfti.zig b/lib/std/special/compiler_rt/fixtfti.zig index 07d38f2c3..733fa1eed 100644 --- a/lib/std/special/compiler_rt/fixtfti.zig +++ b/lib/std/special/compiler_rt/fixtfti.zig @@ -1,7 +1,7 @@ const fixint = @import("fixint.zig").fixint; const builtin = @import("builtin"); -pub extern fn __fixtfti(a: f128) i128 { +pub fn __fixtfti(a: f128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); return fixint(f128, i128, a); } diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig index 02dba7fd6..6b8218e2f 100644 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ b/lib/std/special/compiler_rt/fixtfti_test.zig @@ -6,12 +6,12 @@ const warn = std.debug.warn; fn test__fixtfti(a: f128, expected: i128) void { const x = __fixtfti(a); - //warn("a={}:{x} x={}:{x} expected={}:{x}:u128({x})\n", a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u128, expected)); + //warn("a={}:{x} x={}:{x} expected={}:{x}:@as(u128, {x})\n", .{a, @bitCast(u128, a), x, x, expected, expected, @bitCast(u128, expected)}); testing.expect(x == expected); } test "fixtfti" { - //warn("\n"); + //warn("\n", .{}); test__fixtfti(-math.f128_max, math.minInt(i128)); test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); diff --git a/lib/std/special/compiler_rt/fixuint.zig b/lib/std/special/compiler_rt/fixuint.zig index 55a113b36..977d3c16c 100644 --- a/lib/std/special/compiler_rt/fixuint.zig +++ b/lib/std/special/compiler_rt/fixuint.zig @@ -19,11 +19,11 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t }; const typeWidth = rep_t.bit_count; const exponentBits = (typeWidth - significandBits - 1); - const signBit = (rep_t(1) << (significandBits + exponentBits)); + const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (rep_t(1) << significandBits); + const implicitBit = (@as(rep_t, 1) << significandBits); const significandMask = (implicitBit - 1); // Break a into sign, exponent, significand @@ -31,7 +31,7 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t const absMask = signBit - 1; const aAbs: rep_t = aRep & absMask; - const sign = if ((aRep & signBit) != 0) i32(-1) else i32(1); + const sign = if ((aRep & signBit) != 0) @as(i32, -1) else @as(i32, 1); const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; const significand: rep_t = (aAbs & significandMask) | implicitBit; @@ -39,7 +39,7 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t if (sign == -1 or exponent < 0) return 0; // If the value is too large for the integer type, saturate. - if (@intCast(c_uint, exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); + if (@intCast(c_uint, exponent) >= fixuint_t.bit_count) return ~@as(fixuint_t, 0); // If 0 <= exponent < significandBits, right shift to get the result. // Otherwise, shift left. diff --git a/lib/std/special/compiler_rt/fixunsdfdi.zig b/lib/std/special/compiler_rt/fixunsdfdi.zig index 1fa7ed758..800c9b114 100644 --- a/lib/std/special/compiler_rt/fixunsdfdi.zig +++ b/lib/std/special/compiler_rt/fixunsdfdi.zig @@ -1,11 +1,16 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunsdfdi(a: f64) u64 { +pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { @setRuntimeSafety(builtin.is_test); return fixuint(f64, u64, a); } +pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixunsdfdi, .{a}); +} + test "import fixunsdfdi" { _ = @import("fixunsdfdi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixunsdfsi.zig b/lib/std/special/compiler_rt/fixunsdfsi.zig index a77cb8df8..156c21a88 100644 --- a/lib/std/special/compiler_rt/fixunsdfsi.zig +++ b/lib/std/special/compiler_rt/fixunsdfsi.zig @@ -1,11 +1,16 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunsdfsi(a: f64) u32 { +pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { @setRuntimeSafety(builtin.is_test); return fixuint(f64, u32, a); } +pub fn __aeabi_d2uiz(arg: f64) callconv(.AAPCS) u32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixunsdfsi, .{arg}); +} + test "import fixunsdfsi" { _ = @import("fixunsdfsi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixunsdfti.zig b/lib/std/special/compiler_rt/fixunsdfti.zig index 6e1ded46e..3dcec6bf8 100644 --- a/lib/std/special/compiler_rt/fixunsdfti.zig +++ b/lib/std/special/compiler_rt/fixunsdfti.zig @@ -1,7 +1,7 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunsdfti(a: f64) u128 { +pub fn __fixunsdfti(a: f64) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return fixuint(f64, u128, a); } diff --git a/lib/std/special/compiler_rt/fixunssfdi.zig b/lib/std/special/compiler_rt/fixunssfdi.zig index 36d4acc28..ec00a7ee3 100644 --- a/lib/std/special/compiler_rt/fixunssfdi.zig +++ b/lib/std/special/compiler_rt/fixunssfdi.zig @@ -1,11 +1,16 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunssfdi(a: f32) u64 { +pub fn __fixunssfdi(a: f32) callconv(.C) u64 { @setRuntimeSafety(builtin.is_test); return fixuint(f32, u64, a); } +pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixunssfdi, .{a}); +} + test "import fixunssfdi" { _ = @import("fixunssfdi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixunssfsi.zig b/lib/std/special/compiler_rt/fixunssfsi.zig index 53130286a..a4e5a323f 100644 --- a/lib/std/special/compiler_rt/fixunssfsi.zig +++ b/lib/std/special/compiler_rt/fixunssfsi.zig @@ -1,11 +1,16 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunssfsi(a: f32) u32 { +pub fn __fixunssfsi(a: f32) callconv(.C) u32 { @setRuntimeSafety(builtin.is_test); return fixuint(f32, u32, a); } +pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __fixunssfsi, .{a}); +} + test "import fixunssfsi" { _ = @import("fixunssfsi_test.zig"); } diff --git a/lib/std/special/compiler_rt/fixunssfti.zig b/lib/std/special/compiler_rt/fixunssfti.zig index f0cd788d2..dff9eb749 100644 --- a/lib/std/special/compiler_rt/fixunssfti.zig +++ b/lib/std/special/compiler_rt/fixunssfti.zig @@ -1,7 +1,7 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunssfti(a: f32) u128 { +pub fn __fixunssfti(a: f32) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return fixuint(f32, u128, a); } diff --git a/lib/std/special/compiler_rt/fixunstfdi.zig b/lib/std/special/compiler_rt/fixunstfdi.zig index e35204470..907116c16 100644 --- a/lib/std/special/compiler_rt/fixunstfdi.zig +++ b/lib/std/special/compiler_rt/fixunstfdi.zig @@ -1,7 +1,7 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunstfdi(a: f128) u64 { +pub fn __fixunstfdi(a: f128) callconv(.C) u64 { @setRuntimeSafety(builtin.is_test); return fixuint(f128, u64, a); } diff --git a/lib/std/special/compiler_rt/fixunstfsi.zig b/lib/std/special/compiler_rt/fixunstfsi.zig index 579c55979..c66a3f18b 100644 --- a/lib/std/special/compiler_rt/fixunstfsi.zig +++ b/lib/std/special/compiler_rt/fixunstfsi.zig @@ -1,7 +1,7 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunstfsi(a: f128) u32 { +pub fn __fixunstfsi(a: f128) callconv(.C) u32 { @setRuntimeSafety(builtin.is_test); return fixuint(f128, u32, a); } diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig index e70963691..286567629 100644 --- a/lib/std/special/compiler_rt/fixunstfsi_test.zig +++ b/lib/std/special/compiler_rt/fixunstfsi_test.zig @@ -6,7 +6,7 @@ fn test__fixunstfsi(a: f128, expected: u32) void { testing.expect(x == expected); } -const inf128 = @bitCast(f128, u128(0x7fff0000000000000000000000000000)); +const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfsi" { test__fixunstfsi(inf128, 0xffffffff); diff --git a/lib/std/special/compiler_rt/fixunstfti.zig b/lib/std/special/compiler_rt/fixunstfti.zig index cd6178164..1867c6923 100644 --- a/lib/std/special/compiler_rt/fixunstfti.zig +++ b/lib/std/special/compiler_rt/fixunstfti.zig @@ -1,7 +1,7 @@ const fixuint = @import("fixuint.zig").fixuint; const builtin = @import("builtin"); -pub extern fn __fixunstfti(a: f128) u128 { +pub fn __fixunstfti(a: f128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return fixuint(f128, u128, a); } diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig index 833e4779d..62a9bbfec 100644 --- a/lib/std/special/compiler_rt/fixunstfti_test.zig +++ b/lib/std/special/compiler_rt/fixunstfti_test.zig @@ -6,7 +6,7 @@ fn test__fixunstfti(a: f128, expected: u128) void { testing.expect(x == expected); } -const inf128 = @bitCast(f128, u128(0x7fff0000000000000000000000000000)); +const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); test "fixunstfti" { test__fixunstfti(inf128, 0xffffffffffffffffffffffffffffffff); diff --git a/lib/std/special/compiler_rt/floatdidf.zig b/lib/std/special/compiler_rt/floatdidf.zig index 161013641..afe9e906f 100644 --- a/lib/std/special/compiler_rt/floatdidf.zig +++ b/lib/std/special/compiler_rt/floatdidf.zig @@ -4,7 +4,7 @@ const std = @import("std"); const twop52: f64 = 0x1.0p52; const twop32: f64 = 0x1.0p32; -pub extern fn __floatdidf(a: i64) f64 { +pub fn __floatdidf(a: i64) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); if (a == 0) return 0; @@ -17,6 +17,11 @@ pub extern fn __floatdidf(a: i64) f64 { return (high - twop52) + @bitCast(f64, low); } +pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatdidf, .{arg}); +} + test "import floatdidf" { _ = @import("floatdidf_test.zig"); } diff --git a/lib/std/special/compiler_rt/floatsiXf.zig b/lib/std/special/compiler_rt/floatsiXf.zig index 7e05a3ebf..ef3aba34d 100644 --- a/lib/std/special/compiler_rt/floatsiXf.zig +++ b/lib/std/special/compiler_rt/floatsiXf.zig @@ -6,24 +6,24 @@ fn floatsiXf(comptime T: type, a: i32) T { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, T.bit_count); - const S = @IntType(false, T.bit_count - @clz(Z, Z(T.bit_count) - 1)); + const S = @IntType(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1)); if (a == 0) { - return T(0.0); + return @as(T, 0.0); } const significandBits = std.math.floatMantissaBits(T); const exponentBits = std.math.floatExponentBits(T); const exponentBias = ((1 << exponentBits - 1) - 1); - const implicitBit = Z(1) << significandBits; - const signBit = Z(1 << Z.bit_count - 1); + const implicitBit = @as(Z, 1) << significandBits; + const signBit = @as(Z, 1 << Z.bit_count - 1); const sign = a >> 31; // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31). const abs_a = (a ^ sign) -% sign; // The exponent is the width of abs(a) - const exp = Z(31 - @clz(i32, abs_a)); + const exp = @as(Z, 31 - @clz(i32, abs_a)); const sign_bit = if (sign < 0) signBit else 0; @@ -53,19 +53,29 @@ fn floatsiXf(comptime T: type, a: i32) T { return @bitCast(T, result); } -pub extern fn __floatsisf(arg: i32) f32 { +pub fn __floatsisf(arg: i32) callconv(.C) f32 { @setRuntimeSafety(builtin.is_test); - return @inlineCall(floatsiXf, f32, arg); + return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f32, arg }); } -pub extern fn __floatsidf(arg: i32) f64 { +pub fn __floatsidf(arg: i32) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); - return @inlineCall(floatsiXf, f64, arg); + return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f64, arg }); } -pub extern fn __floatsitf(arg: i32) f128 { +pub fn __floatsitf(arg: i32) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); - return @inlineCall(floatsiXf, f128, arg); + return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f128, arg }); +} + +pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatsidf, .{arg}); +} + +pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatsisf, .{arg}); } fn test_one_floatsitf(a: i32, expected: u128) void { diff --git a/lib/std/special/compiler_rt/floattidf.zig b/lib/std/special/compiler_rt/floattidf.zig index 42ef6df7e..6b5013287 100644 --- a/lib/std/special/compiler_rt/floattidf.zig +++ b/lib/std/special/compiler_rt/floattidf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const DBL_MANT_DIG = 53; -pub extern fn __floattidf(arg: i128) f64 { +pub fn __floattidf(arg: i128) callconv(.C) f64 { @setRuntimeSafety(is_test); if (arg == 0) @@ -47,7 +47,7 @@ pub extern fn __floattidf(arg: i128) f64 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/floattisf.zig b/lib/std/special/compiler_rt/floattisf.zig index f397d130e..3f020dadd 100644 --- a/lib/std/special/compiler_rt/floattisf.zig +++ b/lib/std/special/compiler_rt/floattisf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const FLT_MANT_DIG = 24; -pub extern fn __floattisf(arg: i128) f32 { +pub fn __floattisf(arg: i128) callconv(.C) f32 { @setRuntimeSafety(is_test); if (arg == 0) @@ -48,7 +48,7 @@ pub extern fn __floattisf(arg: i128) f32 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << FLT_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/floattitf.zig b/lib/std/special/compiler_rt/floattitf.zig index b05282e29..90bc24130 100644 --- a/lib/std/special/compiler_rt/floattitf.zig +++ b/lib/std/special/compiler_rt/floattitf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const LDBL_MANT_DIG = 113; -pub extern fn __floattitf(arg: i128) f128 { +pub fn __floattitf(arg: i128) callconv(.C) f128 { @setRuntimeSafety(is_test); if (arg == 0) @@ -47,7 +47,7 @@ pub extern fn __floattitf(arg: i128) f128 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/floatundidf.zig b/lib/std/special/compiler_rt/floatundidf.zig index 68759a2ac..1efe55da4 100644 --- a/lib/std/special/compiler_rt/floatundidf.zig +++ b/lib/std/special/compiler_rt/floatundidf.zig @@ -5,7 +5,7 @@ const twop52: f64 = 0x1.0p52; const twop84: f64 = 0x1.0p84; const twop84_plus_twop52: f64 = 0x1.00000001p84; -pub extern fn __floatundidf(a: u64) f64 { +pub fn __floatundidf(a: u64) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); if (a == 0) return 0; @@ -19,6 +19,11 @@ pub extern fn __floatundidf(a: u64) f64 { return (@bitCast(f64, high) - twop84_plus_twop52) + @bitCast(f64, low); } +pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatundidf, .{arg}); +} + test "import floatundidf" { _ = @import("floatundidf_test.zig"); } diff --git a/lib/std/special/compiler_rt/floatundisf.zig b/lib/std/special/compiler_rt/floatundisf.zig new file mode 100644 index 000000000..41ff02dae --- /dev/null +++ b/lib/std/special/compiler_rt/floatundisf.zig @@ -0,0 +1,91 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const maxInt = std.math.maxInt; + +const FLT_MANT_DIG = 24; + +pub fn __floatundisf(arg: u64) callconv(.C) f32 { + @setRuntimeSafety(builtin.is_test); + + if (arg == 0) return 0; + + var a = arg; + const N: usize = @TypeOf(a).bit_count; + // Number of significant digits + const sd = N - @clz(u64, a); + // 8 exponent + var e = @intCast(u32, sd) - 1; + + if (sd > FLT_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit FLT_MANT_DIG-1 bits to the right of 1 + // Q = bit FLT_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + FLT_MANT_DIG + 1 => a <<= 1, + FLT_MANT_DIG + 2 => {}, + else => { + const shift_amt = @intCast(u6, ((N + FLT_MANT_DIG + 2) - sd)); + const all_ones: u64 = maxInt(u64); + a = (a >> @intCast(u6, sd - (FLT_MANT_DIG + 2))) | + @boolToInt(a & (all_ones >> shift_amt) != 0); + }, + } + // Or P into R + a |= @boolToInt((a & 4) != 0); + // round - this step may add a significant bit + a += 1; + // dump Q and R + a >>= 2; + // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits + if ((a & (@as(u64, 1) << FLT_MANT_DIG)) != 0) { + a >>= 1; + e += 1; + } + // a is now rounded to FLT_MANT_DIG bits + } else { + a <<= @intCast(u6, FLT_MANT_DIG - sd); + // a is now rounded to FLT_MANT_DIG bits + } + + const result: u32 = ((e + 127) << 23) | // exponent + @truncate(u32, a & 0x007FFFFF); // mantissa + return @bitCast(f32, result); +} + +pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatundisf, .{arg}); +} + +fn test__floatundisf(a: u64, expected: f32) void { + std.testing.expectEqual(expected, __floatundisf(a)); +} + +test "floatundisf" { + test__floatundisf(0, 0.0); + test__floatundisf(1, 1.0); + test__floatundisf(2, 2.0); + test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62F); + test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62F); + test__floatundisf(0x8000008000000000, 0x1p+63F); + test__floatundisf(0x8000010000000000, 0x1.000002p+63F); + test__floatundisf(0x8000000000000000, 0x1p+63F); + test__floatundisf(0x8000000000000001, 0x1p+63F); + test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64F); + test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64F); + test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50F); + test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50F); + test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50F); +} diff --git a/lib/std/special/compiler_rt/floatunditf.zig b/lib/std/special/compiler_rt/floatunditf.zig index 9af83a2b5..b574d21bd 100644 --- a/lib/std/special/compiler_rt/floatunditf.zig +++ b/lib/std/special/compiler_rt/floatunditf.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; const std = @import("std"); -pub extern fn __floatunditf(a: u128) f128 { +pub fn __floatunditf(a: u128) callconv(.C) f128 { @setRuntimeSafety(is_test); if (a == 0) { diff --git a/lib/std/special/compiler_rt/floatunsidf.zig b/lib/std/special/compiler_rt/floatunsidf.zig index efeb4c267..771b4938e 100644 --- a/lib/std/special/compiler_rt/floatunsidf.zig +++ b/lib/std/special/compiler_rt/floatunsidf.zig @@ -2,22 +2,27 @@ const builtin = @import("builtin"); const std = @import("std"); const maxInt = std.math.maxInt; -const implicitBit = u64(1) << 52; +const implicitBit = @as(u64, 1) << 52; -pub extern fn __floatunsidf(arg: u32) f64 { +pub fn __floatunsidf(arg: u32) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); if (arg == 0) return 0.0; // The exponent is the width of abs(a) - const exp = u64(31) - @clz(u32, arg); + const exp = @as(u64, 31) - @clz(u32, arg); // Shift a into the significand field and clear the implicit bit const shift = @intCast(u6, 52 - exp); - const mant = u64(arg) << shift ^ implicitBit; + const mant = @as(u64, arg) << shift ^ implicitBit; return @bitCast(f64, mant | (exp + 1023) << 52); } +pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatunsidf, .{arg}); +} + fn test_one_floatunsidf(a: u32, expected: u64) void { const r = __floatunsidf(a); std.testing.expect(@bitCast(u64, r) == expected); diff --git a/lib/std/special/compiler_rt/floatunsisf.zig b/lib/std/special/compiler_rt/floatunsisf.zig new file mode 100644 index 000000000..c11243e6e --- /dev/null +++ b/lib/std/special/compiler_rt/floatunsisf.zig @@ -0,0 +1,58 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const maxInt = std.math.maxInt; + +const significandBits = 23; +const exponentBias = 127; +const implicitBit = @as(u32, 1) << significandBits; + +pub fn __floatunsisf(arg: u32) callconv(.C) f32 { + @setRuntimeSafety(builtin.is_test); + + if (arg == 0) return 0.0; + + // The exponent is the width of abs(a) + const exp = @as(u32, 31) - @clz(u32, arg); + + var mantissa: u32 = undefined; + if (exp <= significandBits) { + // Shift a into the significand field and clear the implicit bit + const shift = @intCast(u5, significandBits - exp); + mantissa = @as(u32, arg) << shift ^ implicitBit; + } else { + const shift = @intCast(u5, exp - significandBits); + // Round to the nearest number after truncation + mantissa = @as(u32, arg) >> shift ^ implicitBit; + // Align to the left and check if the truncated part is halfway over + const round = arg << @intCast(u5, 31 - shift); + mantissa += @boolToInt(round > 0x80000000); + // Tie to even + mantissa += mantissa & 1; + } + + // Use the addition instead of a or since we may have a carry from the + // mantissa to the exponent + var result = mantissa; + result += (exp + exponentBias) << significandBits; + + return @bitCast(f32, result); +} + +pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __floatunsisf, .{arg}); +} + +fn test_one_floatunsisf(a: u32, expected: u32) void { + const r = __floatunsisf(a); + std.testing.expect(@bitCast(u32, r) == expected); +} + +test "floatunsisf" { + // Test the produced bit pattern + test_one_floatunsisf(0, 0); + test_one_floatunsisf(1, 0x3f800000); + test_one_floatunsisf(0x7FFFFFFF, 0x4f000000); + test_one_floatunsisf(0x80000000, 0x4f000000); + test_one_floatunsisf(0xFFFFFFFF, 0x4f800000); +} diff --git a/lib/std/special/compiler_rt/floatunsitf.zig b/lib/std/special/compiler_rt/floatunsitf.zig index 18397f8ad..a10ff6b70 100644 --- a/lib/std/special/compiler_rt/floatunsitf.zig +++ b/lib/std/special/compiler_rt/floatunsitf.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; const std = @import("std"); -pub extern fn __floatunsitf(a: u64) f128 { +pub fn __floatunsitf(a: u64) callconv(.C) f128 { @setRuntimeSafety(is_test); if (a == 0) { diff --git a/lib/std/special/compiler_rt/floatuntidf.zig b/lib/std/special/compiler_rt/floatuntidf.zig index 2cd38c730..5b5adc049 100644 --- a/lib/std/special/compiler_rt/floatuntidf.zig +++ b/lib/std/special/compiler_rt/floatuntidf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const DBL_MANT_DIG = 53; -pub extern fn __floatuntidf(arg: u128) f64 { +pub fn __floatuntidf(arg: u128) callconv(.C) f64 { @setRuntimeSafety(is_test); if (arg == 0) @@ -32,7 +32,7 @@ pub extern fn __floatuntidf(arg: u128) f64 { const shift_amt = @bitCast(i32, N + (DBL_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); a = (a >> @intCast(u7, sd - (DBL_MANT_DIG + 2))) | - @boolToInt((a & (u128(maxInt(u128)) >> shift_amt_u7)) != 0); + @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); }, } // finish @@ -40,7 +40,7 @@ pub extern fn __floatuntidf(arg: u128) f64 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/floatuntisf.zig b/lib/std/special/compiler_rt/floatuntisf.zig index 1b42b267d..33e6e41a8 100644 --- a/lib/std/special/compiler_rt/floatuntisf.zig +++ b/lib/std/special/compiler_rt/floatuntisf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const FLT_MANT_DIG = 24; -pub extern fn __floatuntisf(arg: u128) f32 { +pub fn __floatuntisf(arg: u128) callconv(.C) f32 { @setRuntimeSafety(is_test); if (arg == 0) @@ -32,7 +32,7 @@ pub extern fn __floatuntisf(arg: u128) f32 { const shift_amt = @bitCast(i32, N + (FLT_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); a = (a >> @intCast(u7, sd - (FLT_MANT_DIG + 2))) | - @boolToInt((a & (u128(maxInt(u128)) >> shift_amt_u7)) != 0); + @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); }, } // finish @@ -40,7 +40,7 @@ pub extern fn __floatuntisf(arg: u128) f32 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << FLT_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/floatuntitf.zig b/lib/std/special/compiler_rt/floatuntitf.zig index eddcf92ef..0b9607620 100644 --- a/lib/std/special/compiler_rt/floatuntitf.zig +++ b/lib/std/special/compiler_rt/floatuntitf.zig @@ -5,7 +5,7 @@ const maxInt = std.math.maxInt; const LDBL_MANT_DIG = 113; -pub extern fn __floatuntitf(arg: u128) f128 { +pub fn __floatuntitf(arg: u128) callconv(.C) f128 { @setRuntimeSafety(is_test); if (arg == 0) @@ -32,7 +32,7 @@ pub extern fn __floatuntitf(arg: u128) f128 { const shift_amt = @bitCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); a = (a >> @intCast(u7, sd - (LDBL_MANT_DIG + 2))) | - @boolToInt((a & (u128(maxInt(u128)) >> shift_amt_u7)) != 0); + @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); }, } // finish @@ -40,7 +40,7 @@ pub extern fn __floatuntitf(arg: u128) f128 { a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { + if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { a >>= 1; e += 1; } diff --git a/lib/std/special/compiler_rt/int.zig b/lib/std/special/compiler_rt/int.zig new file mode 100644 index 000000000..88f4d6696 --- /dev/null +++ b/lib/std/special/compiler_rt/int.zig @@ -0,0 +1,580 @@ +// Builtin functions that operate on integer types +const builtin = @import("builtin"); +const testing = @import("std").testing; + +const udivmod = @import("udivmod.zig").udivmod; + +pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 { + @setRuntimeSafety(builtin.is_test); + + const d = __divdi3(a, b); + rem.* = a -% (d *% b); + return d; +} + +pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 { + @setRuntimeSafety(builtin.is_test); + return udivmod(u64, a, b, maybe_rem); +} + +test "test_udivmoddi4" { + _ = @import("udivmoddi4_test.zig"); +} + +pub fn __divdi3(a: i64, b: i64) callconv(.C) i64 { + @setRuntimeSafety(builtin.is_test); + + // Set aside the sign of the quotient. + const sign = @bitCast(u64, (a ^ b) >> 63); + // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). + const abs_a = (a ^ (a >> 63)) -% (a >> 63); + const abs_b = (b ^ (b >> 63)) -% (b >> 63); + // Unsigned division + const res = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), null); + // Apply sign of quotient to result and return. + return @bitCast(i64, (res ^ sign) -% sign); +} + +test "test_divdi3" { + const cases = [_][3]i64{ + [_]i64{ 0, 1, 0 }, + [_]i64{ 0, -1, 0 }, + [_]i64{ 2, 1, 2 }, + [_]i64{ 2, -1, -2 }, + [_]i64{ -2, 1, -2 }, + [_]i64{ -2, -1, 2 }, + + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 1, @bitCast(i64, @as(u64, 0x8000000000000000)) }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), -1, @bitCast(i64, @as(u64, 0x8000000000000000)) }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), -2, 0x4000000000000000 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 2, @bitCast(i64, @as(u64, 0xC000000000000000)) }, + }; + + for (cases) |case| { + test_one_divdi3(case[0], case[1], case[2]); + } +} + +fn test_one_divdi3(a: i64, b: i64, expected_q: i64) void { + const q: i64 = __divdi3(a, b); + testing.expect(q == expected_q); +} + +pub fn __moddi3(a: i64, b: i64) callconv(.C) i64 { + @setRuntimeSafety(builtin.is_test); + + // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). + const abs_a = (a ^ (a >> 63)) -% (a >> 63); + const abs_b = (b ^ (b >> 63)) -% (b >> 63); + // Unsigned division + var r: u64 = undefined; + _ = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), &r); + // Apply the sign of the dividend and return. + return (@bitCast(i64, r) ^ (a >> 63)) -% (a >> 63); +} + +test "test_moddi3" { + const cases = [_][3]i64{ + [_]i64{ 0, 1, 0 }, + [_]i64{ 0, -1, 0 }, + [_]i64{ 5, 3, 2 }, + [_]i64{ 5, -3, 2 }, + [_]i64{ -5, 3, -2 }, + [_]i64{ -5, -3, -2 }, + + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 1, 0 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), -1, 0 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 2, 0 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), -2, 0 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 3, -2 }, + [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), -3, -2 }, + }; + + for (cases) |case| { + test_one_moddi3(case[0], case[1], case[2]); + } +} + +fn test_one_moddi3(a: i64, b: i64, expected_r: i64) void { + const r: i64 = __moddi3(a, b); + testing.expect(r == expected_r); +} + +pub fn __udivdi3(a: u64, b: u64) callconv(.C) u64 { + @setRuntimeSafety(builtin.is_test); + return __udivmoddi4(a, b, null); +} + +pub fn __umoddi3(a: u64, b: u64) callconv(.C) u64 { + @setRuntimeSafety(builtin.is_test); + + var r: u64 = undefined; + _ = __udivmoddi4(a, b, &r); + return r; +} + +test "test_umoddi3" { + test_one_umoddi3(0, 1, 0); + test_one_umoddi3(2, 1, 0); + test_one_umoddi3(0x8000000000000000, 1, 0x0); + test_one_umoddi3(0x8000000000000000, 2, 0x0); + test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); +} + +fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { + const r = __umoddi3(a, b); + testing.expect(r == expected_r); +} + +pub fn __divmodsi4(a: i32, b: i32, rem: *i32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + + const d = __divsi3(a, b); + rem.* = a -% (d * b); + return d; +} + +test "test_divmodsi4" { + const cases = [_][4]i32{ + [_]i32{ 0, 1, 0, 0 }, + [_]i32{ 0, -1, 0, 0 }, + [_]i32{ 2, 1, 2, 0 }, + [_]i32{ 2, -1, -2, 0 }, + [_]i32{ -2, 1, -2, 0 }, + [_]i32{ -2, -1, 2, 0 }, + [_]i32{ 7, 5, 1, 2 }, + [_]i32{ -7, 5, -1, -2 }, + [_]i32{ 19, 5, 3, 4 }, + [_]i32{ 19, -5, -3, 4 }, + + [_]i32{ @bitCast(i32, @as(u32, 0x80000000)), 8, @bitCast(i32, @as(u32, 0xf0000000)), 0 }, + [_]i32{ @bitCast(i32, @as(u32, 0x80000007)), 8, @bitCast(i32, @as(u32, 0xf0000001)), -1 }, + }; + + for (cases) |case| { + test_one_divmodsi4(case[0], case[1], case[2], case[3]); + } +} + +fn test_one_divmodsi4(a: i32, b: i32, expected_q: i32, expected_r: i32) void { + var r: i32 = undefined; + const q: i32 = __divmodsi4(a, b, &r); + testing.expect(q == expected_q and r == expected_r); +} + +pub fn __udivmodsi4(a: u32, b: u32, rem: *u32) callconv(.C) u32 { + @setRuntimeSafety(builtin.is_test); + + const d = __udivsi3(a, b); + rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); + return d; +} + +pub fn __divsi3(n: i32, d: i32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + + // Set aside the sign of the quotient. + const sign = @bitCast(u32, (n ^ d) >> 31); + // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). + const abs_n = (n ^ (n >> 31)) -% (n >> 31); + const abs_d = (d ^ (d >> 31)) -% (d >> 31); + // abs(a) / abs(b) + const res = @bitCast(u32, abs_n) / @bitCast(u32, abs_d); + // Apply sign of quotient to result and return. + return @bitCast(i32, (res ^ sign) -% sign); +} + +test "test_divsi3" { + const cases = [_][3]i32{ + [_]i32{ 0, 1, 0 }, + [_]i32{ 0, -1, 0 }, + [_]i32{ 2, 1, 2 }, + [_]i32{ 2, -1, -2 }, + [_]i32{ -2, 1, -2 }, + [_]i32{ -2, -1, 2 }, + + [_]i32{ @bitCast(i32, @as(u32, 0x80000000)), 1, @bitCast(i32, @as(u32, 0x80000000)) }, + [_]i32{ @bitCast(i32, @as(u32, 0x80000000)), -1, @bitCast(i32, @as(u32, 0x80000000)) }, + [_]i32{ @bitCast(i32, @as(u32, 0x80000000)), -2, 0x40000000 }, + [_]i32{ @bitCast(i32, @as(u32, 0x80000000)), 2, @bitCast(i32, @as(u32, 0xC0000000)) }, + }; + + for (cases) |case| { + test_one_divsi3(case[0], case[1], case[2]); + } +} + +fn test_one_divsi3(a: i32, b: i32, expected_q: i32) void { + const q: i32 = __divsi3(a, b); + testing.expect(q == expected_q); +} + +pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 { + @setRuntimeSafety(builtin.is_test); + + const n_uword_bits: c_uint = u32.bit_count; + // special cases + if (d == 0) return 0; // ?! + if (n == 0) return 0; + var sr = @bitCast(c_uint, @as(c_int, @clz(u32, d)) - @as(c_int, @clz(u32, n))); + // 0 <= sr <= n_uword_bits - 1 or sr large + if (sr > n_uword_bits - 1) { + // d > r + return 0; + } + if (sr == n_uword_bits - 1) { + // d == 1 + return n; + } + sr += 1; + // 1 <= sr <= n_uword_bits - 1 + // Not a special case + var q: u32 = n << @intCast(u5, n_uword_bits - sr); + var r: u32 = n >> @intCast(u5, sr); + var carry: u32 = 0; + while (sr > 0) : (sr -= 1) { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); + q = (q << 1) | carry; + // carry = 0; + // if (r.all >= d.all) + // { + // r.all -= d.all; + // carry = 1; + // } + const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); + carry = @intCast(u32, s & 1); + r -= d & @bitCast(u32, s); + } + q = (q << 1) | carry; + return q; +} + +test "test_udivsi3" { + const cases = [_][3]u32{ + [_]u32{ 0x00000000, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000002, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000003, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000000, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000000, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000000, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000000, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x00000001, 0x00000001, 0x00000001 }, + [_]u32{ 0x00000001, 0x00000002, 0x00000000 }, + [_]u32{ 0x00000001, 0x00000003, 0x00000000 }, + [_]u32{ 0x00000001, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000001, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000001, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000001, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000001, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000001, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000001, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000001, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x00000002, 0x00000001, 0x00000002 }, + [_]u32{ 0x00000002, 0x00000002, 0x00000001 }, + [_]u32{ 0x00000002, 0x00000003, 0x00000000 }, + [_]u32{ 0x00000002, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000002, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000002, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000002, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000002, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000002, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000002, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000002, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x00000003, 0x00000001, 0x00000003 }, + [_]u32{ 0x00000003, 0x00000002, 0x00000001 }, + [_]u32{ 0x00000003, 0x00000003, 0x00000001 }, + [_]u32{ 0x00000003, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000003, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000003, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000003, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000003, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000003, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000003, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000003, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x00000010, 0x00000001, 0x00000010 }, + [_]u32{ 0x00000010, 0x00000002, 0x00000008 }, + [_]u32{ 0x00000010, 0x00000003, 0x00000005 }, + [_]u32{ 0x00000010, 0x00000010, 0x00000001 }, + [_]u32{ 0x00000010, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000010, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000010, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000010, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000010, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000010, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000010, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x078644FA, 0x00000001, 0x078644FA }, + [_]u32{ 0x078644FA, 0x00000002, 0x03C3227D }, + [_]u32{ 0x078644FA, 0x00000003, 0x028216FE }, + [_]u32{ 0x078644FA, 0x00000010, 0x0078644F }, + [_]u32{ 0x078644FA, 0x078644FA, 0x00000001 }, + [_]u32{ 0x078644FA, 0x0747AE14, 0x00000001 }, + [_]u32{ 0x078644FA, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x078644FA, 0x80000000, 0x00000000 }, + [_]u32{ 0x078644FA, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x078644FA, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x078644FA, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x00000001, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0x00000002, 0x03A3D70A }, + [_]u32{ 0x0747AE14, 0x00000003, 0x026D3A06 }, + [_]u32{ 0x0747AE14, 0x00000010, 0x00747AE1 }, + [_]u32{ 0x0747AE14, 0x078644FA, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x0747AE14, 0x00000001 }, + [_]u32{ 0x0747AE14, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x80000000, 0x00000000 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0x00000001, 0x7FFFFFFF }, + [_]u32{ 0x7FFFFFFF, 0x00000002, 0x3FFFFFFF }, + [_]u32{ 0x7FFFFFFF, 0x00000003, 0x2AAAAAAA }, + [_]u32{ 0x7FFFFFFF, 0x00000010, 0x07FFFFFF }, + [_]u32{ 0x7FFFFFFF, 0x078644FA, 0x00000011 }, + [_]u32{ 0x7FFFFFFF, 0x0747AE14, 0x00000011 }, + [_]u32{ 0x7FFFFFFF, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0x7FFFFFFF, 0x80000000, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x80000000, 0x00000001, 0x80000000 }, + [_]u32{ 0x80000000, 0x00000002, 0x40000000 }, + [_]u32{ 0x80000000, 0x00000003, 0x2AAAAAAA }, + [_]u32{ 0x80000000, 0x00000010, 0x08000000 }, + [_]u32{ 0x80000000, 0x078644FA, 0x00000011 }, + [_]u32{ 0x80000000, 0x0747AE14, 0x00000011 }, + [_]u32{ 0x80000000, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0x80000000, 0x80000000, 0x00000001 }, + [_]u32{ 0x80000000, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x80000000, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x80000000, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0xFFFFFFFD, 0x00000001, 0xFFFFFFFD }, + [_]u32{ 0xFFFFFFFD, 0x00000002, 0x7FFFFFFE }, + [_]u32{ 0xFFFFFFFD, 0x00000003, 0x55555554 }, + [_]u32{ 0xFFFFFFFD, 0x00000010, 0x0FFFFFFF }, + [_]u32{ 0xFFFFFFFD, 0x078644FA, 0x00000022 }, + [_]u32{ 0xFFFFFFFD, 0x0747AE14, 0x00000023 }, + [_]u32{ 0xFFFFFFFD, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0xFFFFFFFD, 0x80000000, 0x00000001 }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFD, 0x00000001 }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE }, + [_]u32{ 0xFFFFFFFE, 0x00000002, 0x7FFFFFFF }, + [_]u32{ 0xFFFFFFFE, 0x00000003, 0x55555554 }, + [_]u32{ 0xFFFFFFFE, 0x00000010, 0x0FFFFFFF }, + [_]u32{ 0xFFFFFFFE, 0x078644FA, 0x00000022 }, + [_]u32{ 0xFFFFFFFE, 0x0747AE14, 0x00000023 }, + [_]u32{ 0xFFFFFFFE, 0x7FFFFFFF, 0x00000002 }, + [_]u32{ 0xFFFFFFFE, 0x80000000, 0x00000001 }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFD, 0x00000001 }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFE, 0x00000001 }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF }, + [_]u32{ 0xFFFFFFFF, 0x00000002, 0x7FFFFFFF }, + [_]u32{ 0xFFFFFFFF, 0x00000003, 0x55555555 }, + [_]u32{ 0xFFFFFFFF, 0x00000010, 0x0FFFFFFF }, + [_]u32{ 0xFFFFFFFF, 0x078644FA, 0x00000022 }, + [_]u32{ 0xFFFFFFFF, 0x0747AE14, 0x00000023 }, + [_]u32{ 0xFFFFFFFF, 0x7FFFFFFF, 0x00000002 }, + [_]u32{ 0xFFFFFFFF, 0x80000000, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFD, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001 }, + }; + + for (cases) |case| { + test_one_udivsi3(case[0], case[1], case[2]); + } +} + +fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { + const q: u32 = __udivsi3(a, b); + testing.expect(q == expected_q); +} + +pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + + return n -% __divsi3(n, d) *% d; +} + +test "test_modsi3" { + const cases = [_][3]i32{ + [_]i32{ 0, 1, 0 }, + [_]i32{ 0, -1, 0 }, + [_]i32{ 5, 3, 2 }, + [_]i32{ 5, -3, 2 }, + [_]i32{ -5, 3, -2 }, + [_]i32{ -5, -3, -2 }, + [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 1, 0x0 }, + [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 2, 0x0 }, + [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -2, 0x0 }, + [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 3, -2 }, + [_]i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -3, -2 }, + }; + + for (cases) |case| { + test_one_modsi3(case[0], case[1], case[2]); + } +} + +fn test_one_modsi3(a: i32, b: i32, expected_r: i32) void { + const r: i32 = __modsi3(a, b); + testing.expect(r == expected_r); +} + +pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 { + @setRuntimeSafety(builtin.is_test); + + return n -% __udivsi3(n, d) *% d; +} + +test "test_umodsi3" { + const cases = [_][3]u32{ + [_]u32{ 0x00000000, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000002, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000003, 0x00000000 }, + [_]u32{ 0x00000000, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000000, 0x078644FA, 0x00000000 }, + [_]u32{ 0x00000000, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x00000000, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x00000000, 0x80000000, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0x00000000, 0xFFFFFFFF, 0x00000000 }, + [_]u32{ 0x00000001, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000001, 0x00000002, 0x00000001 }, + [_]u32{ 0x00000001, 0x00000003, 0x00000001 }, + [_]u32{ 0x00000001, 0x00000010, 0x00000001 }, + [_]u32{ 0x00000001, 0x078644FA, 0x00000001 }, + [_]u32{ 0x00000001, 0x0747AE14, 0x00000001 }, + [_]u32{ 0x00000001, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0x00000001, 0x80000000, 0x00000001 }, + [_]u32{ 0x00000001, 0xFFFFFFFD, 0x00000001 }, + [_]u32{ 0x00000001, 0xFFFFFFFE, 0x00000001 }, + [_]u32{ 0x00000001, 0xFFFFFFFF, 0x00000001 }, + [_]u32{ 0x00000002, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000002, 0x00000002, 0x00000000 }, + [_]u32{ 0x00000002, 0x00000003, 0x00000002 }, + [_]u32{ 0x00000002, 0x00000010, 0x00000002 }, + [_]u32{ 0x00000002, 0x078644FA, 0x00000002 }, + [_]u32{ 0x00000002, 0x0747AE14, 0x00000002 }, + [_]u32{ 0x00000002, 0x7FFFFFFF, 0x00000002 }, + [_]u32{ 0x00000002, 0x80000000, 0x00000002 }, + [_]u32{ 0x00000002, 0xFFFFFFFD, 0x00000002 }, + [_]u32{ 0x00000002, 0xFFFFFFFE, 0x00000002 }, + [_]u32{ 0x00000002, 0xFFFFFFFF, 0x00000002 }, + [_]u32{ 0x00000003, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000003, 0x00000002, 0x00000001 }, + [_]u32{ 0x00000003, 0x00000003, 0x00000000 }, + [_]u32{ 0x00000003, 0x00000010, 0x00000003 }, + [_]u32{ 0x00000003, 0x078644FA, 0x00000003 }, + [_]u32{ 0x00000003, 0x0747AE14, 0x00000003 }, + [_]u32{ 0x00000003, 0x7FFFFFFF, 0x00000003 }, + [_]u32{ 0x00000003, 0x80000000, 0x00000003 }, + [_]u32{ 0x00000003, 0xFFFFFFFD, 0x00000003 }, + [_]u32{ 0x00000003, 0xFFFFFFFE, 0x00000003 }, + [_]u32{ 0x00000003, 0xFFFFFFFF, 0x00000003 }, + [_]u32{ 0x00000010, 0x00000001, 0x00000000 }, + [_]u32{ 0x00000010, 0x00000002, 0x00000000 }, + [_]u32{ 0x00000010, 0x00000003, 0x00000001 }, + [_]u32{ 0x00000010, 0x00000010, 0x00000000 }, + [_]u32{ 0x00000010, 0x078644FA, 0x00000010 }, + [_]u32{ 0x00000010, 0x0747AE14, 0x00000010 }, + [_]u32{ 0x00000010, 0x7FFFFFFF, 0x00000010 }, + [_]u32{ 0x00000010, 0x80000000, 0x00000010 }, + [_]u32{ 0x00000010, 0xFFFFFFFD, 0x00000010 }, + [_]u32{ 0x00000010, 0xFFFFFFFE, 0x00000010 }, + [_]u32{ 0x00000010, 0xFFFFFFFF, 0x00000010 }, + [_]u32{ 0x078644FA, 0x00000001, 0x00000000 }, + [_]u32{ 0x078644FA, 0x00000002, 0x00000000 }, + [_]u32{ 0x078644FA, 0x00000003, 0x00000000 }, + [_]u32{ 0x078644FA, 0x00000010, 0x0000000A }, + [_]u32{ 0x078644FA, 0x078644FA, 0x00000000 }, + [_]u32{ 0x078644FA, 0x0747AE14, 0x003E96E6 }, + [_]u32{ 0x078644FA, 0x7FFFFFFF, 0x078644FA }, + [_]u32{ 0x078644FA, 0x80000000, 0x078644FA }, + [_]u32{ 0x078644FA, 0xFFFFFFFD, 0x078644FA }, + [_]u32{ 0x078644FA, 0xFFFFFFFE, 0x078644FA }, + [_]u32{ 0x078644FA, 0xFFFFFFFF, 0x078644FA }, + [_]u32{ 0x0747AE14, 0x00000001, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x00000002, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x00000003, 0x00000002 }, + [_]u32{ 0x0747AE14, 0x00000010, 0x00000004 }, + [_]u32{ 0x0747AE14, 0x078644FA, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0x0747AE14, 0x00000000 }, + [_]u32{ 0x0747AE14, 0x7FFFFFFF, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0x80000000, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFD, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFE, 0x0747AE14 }, + [_]u32{ 0x0747AE14, 0xFFFFFFFF, 0x0747AE14 }, + [_]u32{ 0x7FFFFFFF, 0x00000001, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0x00000002, 0x00000001 }, + [_]u32{ 0x7FFFFFFF, 0x00000003, 0x00000001 }, + [_]u32{ 0x7FFFFFFF, 0x00000010, 0x0000000F }, + [_]u32{ 0x7FFFFFFF, 0x078644FA, 0x00156B65 }, + [_]u32{ 0x7FFFFFFF, 0x0747AE14, 0x043D70AB }, + [_]u32{ 0x7FFFFFFF, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0x7FFFFFFF, 0x80000000, 0x7FFFFFFF }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFD, 0x7FFFFFFF }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFE, 0x7FFFFFFF }, + [_]u32{ 0x7FFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF }, + [_]u32{ 0x80000000, 0x00000001, 0x00000000 }, + [_]u32{ 0x80000000, 0x00000002, 0x00000000 }, + [_]u32{ 0x80000000, 0x00000003, 0x00000002 }, + [_]u32{ 0x80000000, 0x00000010, 0x00000000 }, + [_]u32{ 0x80000000, 0x078644FA, 0x00156B66 }, + [_]u32{ 0x80000000, 0x0747AE14, 0x043D70AC }, + [_]u32{ 0x80000000, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0x80000000, 0x80000000, 0x00000000 }, + [_]u32{ 0x80000000, 0xFFFFFFFD, 0x80000000 }, + [_]u32{ 0x80000000, 0xFFFFFFFE, 0x80000000 }, + [_]u32{ 0x80000000, 0xFFFFFFFF, 0x80000000 }, + [_]u32{ 0xFFFFFFFD, 0x00000001, 0x00000000 }, + [_]u32{ 0xFFFFFFFD, 0x00000002, 0x00000001 }, + [_]u32{ 0xFFFFFFFD, 0x00000003, 0x00000001 }, + [_]u32{ 0xFFFFFFFD, 0x00000010, 0x0000000D }, + [_]u32{ 0xFFFFFFFD, 0x078644FA, 0x002AD6C9 }, + [_]u32{ 0xFFFFFFFD, 0x0747AE14, 0x01333341 }, + [_]u32{ 0xFFFFFFFD, 0x7FFFFFFF, 0x7FFFFFFE }, + [_]u32{ 0xFFFFFFFD, 0x80000000, 0x7FFFFFFD }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFD, 0x00000000 }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFD }, + [_]u32{ 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFD }, + [_]u32{ 0xFFFFFFFE, 0x00000001, 0x00000000 }, + [_]u32{ 0xFFFFFFFE, 0x00000002, 0x00000000 }, + [_]u32{ 0xFFFFFFFE, 0x00000003, 0x00000002 }, + [_]u32{ 0xFFFFFFFE, 0x00000010, 0x0000000E }, + [_]u32{ 0xFFFFFFFE, 0x078644FA, 0x002AD6CA }, + [_]u32{ 0xFFFFFFFE, 0x0747AE14, 0x01333342 }, + [_]u32{ 0xFFFFFFFE, 0x7FFFFFFF, 0x00000000 }, + [_]u32{ 0xFFFFFFFE, 0x80000000, 0x7FFFFFFE }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFD, 0x00000001 }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 }, + [_]u32{ 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE }, + [_]u32{ 0xFFFFFFFF, 0x00000001, 0x00000000 }, + [_]u32{ 0xFFFFFFFF, 0x00000002, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0x00000003, 0x00000000 }, + [_]u32{ 0xFFFFFFFF, 0x00000010, 0x0000000F }, + [_]u32{ 0xFFFFFFFF, 0x078644FA, 0x002AD6CB }, + [_]u32{ 0xFFFFFFFF, 0x0747AE14, 0x01333343 }, + [_]u32{ 0xFFFFFFFF, 0x7FFFFFFF, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0x80000000, 0x7FFFFFFF }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFD, 0x00000002 }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001 }, + [_]u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + }; + + for (cases) |case| { + test_one_umodsi3(case[0], case[1], case[2]); + } +} + +fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) void { + const r: u32 = __umodsi3(a, b); + testing.expect(r == expected_r); +} diff --git a/lib/std/special/compiler_rt/lshrti3.zig b/lib/std/special/compiler_rt/lshrti3.zig index 329968ae4..043be5e2a 100644 --- a/lib/std/special/compiler_rt/lshrti3.zig +++ b/lib/std/special/compiler_rt/lshrti3.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __lshrti3(a: i128, b: i32) i128 { +pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { var input = twords{ .all = a }; var result: twords = undefined; diff --git a/lib/std/special/compiler_rt/modti3.zig b/lib/std/special/compiler_rt/modti3.zig index 16f2f38ba..a9d1f4846 100644 --- a/lib/std/special/compiler_rt/modti3.zig +++ b/lib/std/special/compiler_rt/modti3.zig @@ -6,7 +6,7 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __modti3(a: i128, b: i128) i128 { +pub fn __modti3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); const s_a = a >> (i128.bit_count - 1); // s = a < 0 ? -1 : 0 @@ -21,8 +21,11 @@ pub extern fn __modti3(a: i128, b: i128) i128 { } const v128 = @Vector(2, u64); -pub extern fn __modti3_windows_x86_64(a: v128, b: v128) v128 { - return @bitCast(v128, @inlineCall(__modti3, @bitCast(i128, a), @bitCast(i128, b))); +pub fn __modti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, @call(.{ .modifier = .always_inline }, __modti3, .{ + @bitCast(i128, a), + @bitCast(i128, b), + })); } test "import modti3" { diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig index 51d40ad26..103692f61 100644 --- a/lib/std/special/compiler_rt/mulXf3.zig +++ b/lib/std/special/compiler_rt/mulXf3.zig @@ -6,16 +6,26 @@ const std = @import("std"); const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __multf3(a: f128, b: f128) f128 { +pub fn __multf3(a: f128, b: f128) callconv(.C) f128 { return mulXf3(f128, a, b); } -pub extern fn __muldf3(a: f64, b: f64) f64 { +pub fn __muldf3(a: f64, b: f64) callconv(.C) f64 { return mulXf3(f64, a, b); } -pub extern fn __mulsf3(a: f32, b: f32) f32 { +pub fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { return mulXf3(f32, a, b); } +pub fn __aeabi_fmul(a: f32, b: f32) callconv(.C) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __mulsf3, .{ a, b }); +} + +pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __muldf3, .{ a, b }); +} + fn mulXf3(comptime T: type, a: T, b: T) T { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, T.bit_count); @@ -24,11 +34,11 @@ fn mulXf3(comptime T: type, a: T, b: T) T { const significandBits = std.math.floatMantissaBits(T); const exponentBits = std.math.floatExponentBits(T); - const signBit = (Z(1) << (significandBits + exponentBits)); + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (Z(1) << significandBits); + const implicitBit = (@as(Z, 1) << significandBits); const quietBit = implicitBit >> 1; const significandMask = implicitBit - 1; @@ -122,7 +132,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T { // a zero of the appropriate sign. Mathematically there is no need to // handle this case separately, but we make it a special case to // simplify the shift logic. - const shift: u32 = @truncate(u32, Z(1) -% @bitCast(u32, productExponent)); + const shift: u32 = @truncate(u32, @as(Z, 1) -% @bitCast(u32, productExponent)); if (shift >= typeWidth) return @bitCast(T, productSign); // Otherwise, shift the significand of the result so that the round @@ -131,7 +141,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T { } else { // Result is normal before rounding; insert the exponent. productHi &= significandMask; - productHi |= Z(@bitCast(u32, productExponent)) << significandBits; + productHi |= @as(Z, @bitCast(u32, productExponent)) << significandBits; } // Insert the sign of the result: @@ -150,7 +160,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { switch (Z) { u32 => { // 32x32 --> 64 bit multiply - const product = u64(a) * u64(b); + const product = @as(u64, a) * @as(u64, b); hi.* = @truncate(u32, product >> 32); lo.* = @truncate(u32, product); }, @@ -179,9 +189,9 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; }, u128 => { - const Word_LoMask = u64(0x00000000ffffffff); - const Word_HiMask = u64(0xffffffff00000000); - const Word_FullMask = u64(0xffffffffffffffff); + const Word_LoMask = @as(u64, 0x00000000ffffffff); + const Word_HiMask = @as(u64, 0xffffffff00000000); + const Word_FullMask = @as(u64, 0xffffffffffffffff); const S = struct { fn Word_1(x: u128) u64 { return @truncate(u32, x >> 96); @@ -217,22 +227,22 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { const product43: u64 = S.Word_4(a) * S.Word_3(b); const product44: u64 = S.Word_4(a) * S.Word_4(b); - const sum0: u128 = u128(product44); - const sum1: u128 = u128(product34) +% - u128(product43); - const sum2: u128 = u128(product24) +% - u128(product33) +% - u128(product42); - const sum3: u128 = u128(product14) +% - u128(product23) +% - u128(product32) +% - u128(product41); - const sum4: u128 = u128(product13) +% - u128(product22) +% - u128(product31); - const sum5: u128 = u128(product12) +% - u128(product21); - const sum6: u128 = u128(product11); + const sum0: u128 = @as(u128, product44); + const sum1: u128 = @as(u128, product34) +% + @as(u128, product43); + const sum2: u128 = @as(u128, product24) +% + @as(u128, product33) +% + @as(u128, product42); + const sum3: u128 = @as(u128, product14) +% + @as(u128, product23) +% + @as(u128, product32) +% + @as(u128, product41); + const sum4: u128 = @as(u128, product13) +% + @as(u128, product22) +% + @as(u128, product31); + const sum5: u128 = @as(u128, product12) +% + @as(u128, product21); + const sum6: u128 = @as(u128, product11); const r0: u128 = (sum0 & Word_FullMask) +% ((sum1 & Word_LoMask) << 32); @@ -258,7 +268,7 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { @setRuntimeSafety(builtin.is_test); const Z = @IntType(false, T.bit_count); const significandBits = std.math.floatMantissaBits(T); - const implicitBit = Z(1) << significandBits; + const implicitBit = @as(Z, 1) << significandBits; const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); significand.* <<= @intCast(std.math.Log2Int(Z), shift); diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index 1c0c0fc04..57dc38532 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -2,8 +2,8 @@ // // https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/test/builtins/Unit/multf3_test.c -const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64); -const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64); +const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); +const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); const __multf3 = @import("mulXf3.zig").__multf3; @@ -39,7 +39,7 @@ fn test__multf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { } fn makeNaN128(rand: u64) f128 { - const int_result = u128(0x7fff000000000000 | (rand & 0xffffffffffff)) << 64; + const int_result = @as(u128, 0x7fff000000000000 | (rand & 0xffffffffffff)) << 64; const float_result = @bitCast(f128, int_result); return float_result; } @@ -55,15 +55,15 @@ test "multf3" { // any * any test__multf3( - @bitCast(f128, u128(0x40042eab345678439abcdefea5678234)), - @bitCast(f128, u128(0x3ffeedcb34a235253948765432134675)), + @bitCast(f128, @as(u128, 0x40042eab345678439abcdefea5678234)), + @bitCast(f128, @as(u128, 0x3ffeedcb34a235253948765432134675)), 0x400423e7f9e3c9fc, 0xd906c2c2a85777c4, ); test__multf3( - @bitCast(f128, u128(0x3fcd353e45674d89abacc3a2ebf3ff50)), - @bitCast(f128, u128(0x3ff6ed8764648369535adf4be3214568)), + @bitCast(f128, @as(u128, 0x3fcd353e45674d89abacc3a2ebf3ff50)), + @bitCast(f128, @as(u128, 0x3ff6ed8764648369535adf4be3214568)), 0x3fc52a163c6223fc, 0xc94c4bf0430768b4, ); @@ -76,8 +76,8 @@ test "multf3" { ); test__multf3( - @bitCast(f128, u128(0x3f154356473c82a9fabf2d22ace345df)), - @bitCast(f128, u128(0x3e38eda98765476743ab21da23d45679)), + @bitCast(f128, @as(u128, 0x3f154356473c82a9fabf2d22ace345df)), + @bitCast(f128, @as(u128, 0x3e38eda98765476743ab21da23d45679)), 0x3d4f37c1a3137cae, 0xfc6807048bc2836a, ); diff --git a/lib/std/special/compiler_rt/muldi3.zig b/lib/std/special/compiler_rt/muldi3.zig index 7700777c1..4f605d441 100644 --- a/lib/std/special/compiler_rt/muldi3.zig +++ b/lib/std/special/compiler_rt/muldi3.zig @@ -21,7 +21,7 @@ fn __muldsi3(a: u32, b: u32) i64 { @setRuntimeSafety(builtin.is_test); const bits_in_word_2 = @sizeOf(i32) * 8 / 2; - const lower_mask = (~u32(0)) >> bits_in_word_2; + const lower_mask = (~@as(u32, 0)) >> bits_in_word_2; var r: dwords = undefined; r.s.low = (a & lower_mask) *% (b & lower_mask); @@ -39,7 +39,7 @@ fn __muldsi3(a: u32, b: u32) i64 { return r.all; } -pub extern fn __muldi3(a: i64, b: i64) i64 { +pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); const x = dwords{ .all = a }; diff --git a/lib/std/special/compiler_rt/mulodi4.zig b/lib/std/special/compiler_rt/mulodi4.zig index 82e9ef325..b70d1657e 100644 --- a/lib/std/special/compiler_rt/mulodi4.zig +++ b/lib/std/special/compiler_rt/mulodi4.zig @@ -3,10 +3,10 @@ const compiler_rt = @import("../compiler_rt.zig"); const maxInt = std.math.maxInt; const minInt = std.math.minInt; -pub extern fn __mulodi4(a: i64, b: i64, overflow: *c_int) i64 { +pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); - const min = @bitCast(i64, u64(1 << (i64.bit_count - 1))); + const min = @bitCast(i64, @as(u64, 1 << (i64.bit_count - 1))); const max = ~min; overflow.* = 0; diff --git a/lib/std/special/compiler_rt/mulodi4_test.zig b/lib/std/special/compiler_rt/mulodi4_test.zig index 7575c7704..803d60e8f 100644 --- a/lib/std/special/compiler_rt/mulodi4_test.zig +++ b/lib/std/special/compiler_rt/mulodi4_test.zig @@ -52,34 +52,34 @@ test "mulodi4" { test__mulodi4(0x7FFFFFFFFFFFFFFF, -2, 2, 1); test__mulodi4(-2, 0x7FFFFFFFFFFFFFFF, 2, 1); - test__mulodi4(0x7FFFFFFFFFFFFFFF, -1, @bitCast(i64, u64(0x8000000000000001)), 0); - test__mulodi4(-1, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, u64(0x8000000000000001)), 0); + test__mulodi4(0x7FFFFFFFFFFFFFFF, -1, @bitCast(i64, @as(u64, 0x8000000000000001)), 0); + test__mulodi4(-1, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, @as(u64, 0x8000000000000001)), 0); test__mulodi4(0x7FFFFFFFFFFFFFFF, 0, 0, 0); test__mulodi4(0, 0x7FFFFFFFFFFFFFFF, 0, 0); test__mulodi4(0x7FFFFFFFFFFFFFFF, 1, 0x7FFFFFFFFFFFFFFF, 0); test__mulodi4(1, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0); - test__mulodi4(0x7FFFFFFFFFFFFFFF, 2, @bitCast(i64, u64(0x8000000000000001)), 1); - test__mulodi4(2, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, u64(0x8000000000000001)), 1); + test__mulodi4(0x7FFFFFFFFFFFFFFF, 2, @bitCast(i64, @as(u64, 0x8000000000000001)), 1); + test__mulodi4(2, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, @as(u64, 0x8000000000000001)), 1); - test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), -2, @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(-2, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), -1, @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(-1, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 0, 0, 0); - test__mulodi4(0, @bitCast(i64, u64(0x8000000000000000)), 0, 0); - test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 1, @bitCast(i64, u64(0x8000000000000000)), 0); - test__mulodi4(1, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 0); - test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 2, @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(2, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000000)), -2, @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(-2, @bitCast(i64, @as(u64, 0x8000000000000000)), @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000000)), -1, @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(-1, @bitCast(i64, @as(u64, 0x8000000000000000)), @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000000)), 0, 0, 0); + test__mulodi4(0, @bitCast(i64, @as(u64, 0x8000000000000000)), 0, 0); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000000)), 1, @bitCast(i64, @as(u64, 0x8000000000000000)), 0); + test__mulodi4(1, @bitCast(i64, @as(u64, 0x8000000000000000)), @bitCast(i64, @as(u64, 0x8000000000000000)), 0); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000000)), 2, @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(2, @bitCast(i64, @as(u64, 0x8000000000000000)), @bitCast(i64, @as(u64, 0x8000000000000000)), 1); - test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), -2, @bitCast(i64, u64(0x8000000000000001)), 1); - test__mulodi4(-2, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000001)), 1); - test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), -1, 0x7FFFFFFFFFFFFFFF, 0); - test__mulodi4(-1, @bitCast(i64, u64(0x8000000000000001)), 0x7FFFFFFFFFFFFFFF, 0); - test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 0, 0, 0); - test__mulodi4(0, @bitCast(i64, u64(0x8000000000000001)), 0, 0); - test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 1, @bitCast(i64, u64(0x8000000000000001)), 0); - test__mulodi4(1, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000001)), 0); - test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 2, @bitCast(i64, u64(0x8000000000000000)), 1); - test__mulodi4(2, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000000)), 1); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000001)), -2, @bitCast(i64, @as(u64, 0x8000000000000001)), 1); + test__mulodi4(-2, @bitCast(i64, @as(u64, 0x8000000000000001)), @bitCast(i64, @as(u64, 0x8000000000000001)), 1); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000001)), -1, 0x7FFFFFFFFFFFFFFF, 0); + test__mulodi4(-1, @bitCast(i64, @as(u64, 0x8000000000000001)), 0x7FFFFFFFFFFFFFFF, 0); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000001)), 0, 0, 0); + test__mulodi4(0, @bitCast(i64, @as(u64, 0x8000000000000001)), 0, 0); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000001)), 1, @bitCast(i64, @as(u64, 0x8000000000000001)), 0); + test__mulodi4(1, @bitCast(i64, @as(u64, 0x8000000000000001)), @bitCast(i64, @as(u64, 0x8000000000000001)), 0); + test__mulodi4(@bitCast(i64, @as(u64, 0x8000000000000001)), 2, @bitCast(i64, @as(u64, 0x8000000000000000)), 1); + test__mulodi4(2, @bitCast(i64, @as(u64, 0x8000000000000001)), @bitCast(i64, @as(u64, 0x8000000000000000)), 1); } diff --git a/lib/std/special/compiler_rt/muloti4.zig b/lib/std/special/compiler_rt/muloti4.zig index ccde8e3e6..190959b80 100644 --- a/lib/std/special/compiler_rt/muloti4.zig +++ b/lib/std/special/compiler_rt/muloti4.zig @@ -1,10 +1,10 @@ const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 { +pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); - const min = @bitCast(i128, u128(1 << (i128.bit_count - 1))); + const min = @bitCast(i128, @as(u128, 1 << (i128.bit_count - 1))); const max = ~min; overflow.* = 0; diff --git a/lib/std/special/compiler_rt/muloti4_test.zig b/lib/std/special/compiler_rt/muloti4_test.zig index 00144a883..f94a13e04 100644 --- a/lib/std/special/compiler_rt/muloti4_test.zig +++ b/lib/std/special/compiler_rt/muloti4_test.zig @@ -39,38 +39,38 @@ test "muloti4" { test__muloti4(2097152, -4398046511103, -9223372036852678656, 0); test__muloti4(-2097152, -4398046511103, 9223372036852678656, 0); - test__muloti4(@bitCast(i128, u128(0x00000000000000B504F333F9DE5BE000)), @bitCast(i128, u128(0x000000000000000000B504F333F9DE5B)), @bitCast(i128, u128(0x7FFFFFFFFFFFF328DF915DA296E8A000)), 0); - test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); - test__muloti4(-2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x00000000000000B504F333F9DE5BE000)), @bitCast(i128, @as(u128, 0x000000000000000000B504F333F9DE5B)), @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFF328DF915DA296E8A000)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); + test__muloti4(-2, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); - test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0); - test__muloti4(-1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0); - test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0, 0); - test__muloti4(0, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0); - test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); - test__muloti4(1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); - test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); - test__muloti4(2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0); + test__muloti4(-1, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0, 0); + test__muloti4(0, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0); + test__muloti4(@bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 1, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); + test__muloti4(1, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); + test__muloti4(2, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0, 0); - test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0); - test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 0); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), -2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(-2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), -1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(-1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 0, 0, 0); + test__muloti4(0, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 0, 0); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 0); + test__muloti4(1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); - test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); - test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0, 0); - test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0); - test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0); - test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); - test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), -2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); + test__muloti4(-2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), -1, @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); + test__muloti4(-1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), @bitCast(i128, @as(u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0, 0, 0); + test__muloti4(0, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0, 0); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0); + test__muloti4(1, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 0); + test__muloti4(@bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), 2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); + test__muloti4(2, @bitCast(i128, @as(u128, 0x80000000000000000000000000000001)), @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 1); } diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/std/special/compiler_rt/multi3.zig index 799b1f575..f46bd5542 100644 --- a/lib/std/special/compiler_rt/multi3.zig +++ b/lib/std/special/compiler_rt/multi3.zig @@ -5,7 +5,7 @@ const compiler_rt = @import("../compiler_rt.zig"); // ae684fad6d34858c014c94da69c15e7774a633c3 // 2018-08-13 -pub extern fn __multi3(a: i128, b: i128) i128 { +pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); const x = twords{ .all = a }; const y = twords{ .all = b }; @@ -15,13 +15,16 @@ pub extern fn __multi3(a: i128, b: i128) i128 { } const v128 = @Vector(2, u64); -pub extern fn __multi3_windows_x86_64(a: v128, b: v128) v128 { - return @bitCast(v128, @inlineCall(__multi3, @bitCast(i128, a), @bitCast(i128, b))); +pub fn __multi3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, @call(.{ .modifier = .always_inline }, __multi3, .{ + @bitCast(i128, a), + @bitCast(i128, b), + })); } fn __mulddi3(a: u64, b: u64) i128 { const bits_in_dword_2 = (@sizeOf(i64) * 8) / 2; - const lower_mask = ~u64(0) >> bits_in_dword_2; + const lower_mask = ~@as(u64, 0) >> bits_in_dword_2; var r: twords = undefined; r.s.low = (a & lower_mask) *% (b & lower_mask); var t: u64 = r.s.low >> bits_in_dword_2; diff --git a/lib/std/special/compiler_rt/negXf2.zig b/lib/std/special/compiler_rt/negXf2.zig index b71a503c1..c41b1b48d 100644 --- a/lib/std/special/compiler_rt/negXf2.zig +++ b/lib/std/special/compiler_rt/negXf2.zig @@ -1,13 +1,23 @@ const std = @import("std"); -pub extern fn __negsf2(a: f32) f32 { +pub fn __negsf2(a: f32) callconv(.C) f32 { return negXf2(f32, a); } -pub extern fn __negdf2(a: f64) f64 { +pub fn __negdf2(a: f64) callconv(.C) f64 { return negXf2(f64, a); } +pub fn __aeabi_fneg(arg: f32) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __negsf2, .{arg}); +} + +pub fn __aeabi_dneg(arg: f64) callconv(.AAPCS) f64 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __negdf2, .{arg}); +} + fn negXf2(comptime T: type, a: T) T { const Z = @IntType(false, T.bit_count); @@ -15,7 +25,7 @@ fn negXf2(comptime T: type, a: T) T { const significandBits = std.math.floatMantissaBits(T); const exponentBits = std.math.floatExponentBits(T); - const signBit = (Z(1) << (significandBits + exponentBits)); + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); return @bitCast(T, @bitCast(Z, a) ^ signBit); } diff --git a/lib/std/special/compiler_rt/popcountdi2.zig b/lib/std/special/compiler_rt/popcountdi2.zig index ea36b0ec4..b7e7220c7 100644 --- a/lib/std/special/compiler_rt/popcountdi2.zig +++ b/lib/std/special/compiler_rt/popcountdi2.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); // ported from llvm compiler-rt 8.0.0rc3 95e1c294cb0415a377a7b1d6c7c7d4f89e1c04e4 -pub extern fn __popcountdi2(a: i64) i32 { +pub fn __popcountdi2(a: i64) callconv(.C) i32 { var x2 = @bitCast(u64, a); x2 = x2 - ((x2 >> 1) & 0x5555555555555555); // Every 2 bits holds the sum of every pair of bits (32) diff --git a/lib/std/special/compiler_rt/popcountdi2_test.zig b/lib/std/special/compiler_rt/popcountdi2_test.zig index bedcbcd1d..fe02786e1 100644 --- a/lib/std/special/compiler_rt/popcountdi2_test.zig +++ b/lib/std/special/compiler_rt/popcountdi2_test.zig @@ -20,8 +20,8 @@ test "popcountdi2" { test__popcountdi2(0); test__popcountdi2(1); test__popcountdi2(2); - test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFD))); - test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFE))); - test__popcountdi2(@bitCast(i64, u64(0xFFFFFFFFFFFFFFFF))); + test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFD))); + test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFE))); + test__popcountdi2(@bitCast(i64, @as(u64, 0xFFFFFFFFFFFFFFFF))); // TODO some fuzz testing } diff --git a/lib/std/special/compiler_rt/stack_probe.zig b/lib/std/special/compiler_rt/stack_probe.zig index c3e534c8e..dd441b2f3 100644 --- a/lib/std/special/compiler_rt/stack_probe.zig +++ b/lib/std/special/compiler_rt/stack_probe.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); // Zig's own stack-probe routine (available only on x86 and x86_64) -pub nakedcc fn zig_probe_stack() void { +pub fn zig_probe_stack() callconv(.Naked) void { @setRuntimeSafety(false); // Versions of the Linux kernel before 5.1 treat any access below SP as @@ -180,27 +180,27 @@ fn win_probe_stack_adjust_sp() void { // ___chkstk (__alloca) | yes | yes | // ___chkstk_ms | no | no | -pub nakedcc fn _chkstk() void { +pub fn _chkstk() callconv(.Naked) void { @setRuntimeSafety(false); - @inlineCall(win_probe_stack_adjust_sp); + @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}); } -pub nakedcc fn __chkstk() void { +pub fn __chkstk() callconv(.Naked) void { @setRuntimeSafety(false); switch (builtin.arch) { - .i386 => @inlineCall(win_probe_stack_adjust_sp), - .x86_64 => @inlineCall(win_probe_stack_only), + .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}), + .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}), else => unreachable, } } -pub nakedcc fn ___chkstk() void { +pub fn ___chkstk() callconv(.Naked) void { @setRuntimeSafety(false); - @inlineCall(win_probe_stack_adjust_sp); + @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}); } -pub nakedcc fn __chkstk_ms() void { +pub fn __chkstk_ms() callconv(.Naked) void { @setRuntimeSafety(false); - @inlineCall(win_probe_stack_only); + @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}); } -pub nakedcc fn ___chkstk_ms() void { +pub fn ___chkstk_ms() callconv(.Naked) void { @setRuntimeSafety(false); - @inlineCall(win_probe_stack_only); + @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}); } diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index e4c4aa38a..0a09dea84 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -1,25 +1,40 @@ const std = @import("std"); -pub extern fn __truncsfhf2(a: f32) u16 { +pub fn __truncsfhf2(a: f32) callconv(.C) u16 { return @bitCast(u16, truncXfYf2(f16, f32, a)); } -pub extern fn __truncdfhf2(a: f64) u16 { +pub fn __truncdfhf2(a: f64) callconv(.C) u16 { return @bitCast(u16, truncXfYf2(f16, f64, a)); } -pub extern fn __trunctfsf2(a: f128) f32 { +pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return truncXfYf2(f32, f128, a); } -pub extern fn __trunctfdf2(a: f128) f64 { +pub fn __trunctfdf2(a: f128) callconv(.C) f64 { return truncXfYf2(f64, f128, a); } -pub extern fn __truncdfsf2(a: f64) f32 { +pub fn __truncdfsf2(a: f64) callconv(.C) f32 { return truncXfYf2(f32, f64, a); } +pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __truncdfsf2, .{a}); +} + +pub fn __aeabi_d2h(a: f64) callconv(.AAPCS) u16 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __truncdfhf2, .{a}); +} + +pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { + @setRuntimeSafety(false); + return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); +} + inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); @@ -69,7 +84,7 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t // destination format. We can convert by simply right-shifting with // rounding and adjusting the exponent. absResult = @truncate(dst_rep_t, aAbs >> (srcSigBits - dstSigBits)); - absResult -%= dst_rep_t(srcExpBias - dstExpBias) << dstSigBits; + absResult -%= @as(dst_rep_t, srcExpBias - dstExpBias) << dstSigBits; const roundBits: src_rep_t = aAbs & roundMask; if (roundBits > halfway) { diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig index eccf7efb7..baec2a445 100644 --- a/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -152,11 +152,11 @@ fn test__trunctfsf2(a: f128, expected: u32) void { test "trunctfsf2" { // qnan - test__trunctfsf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7fc00000); + test__trunctfsf2(@bitCast(f128, @as(u128, 0x7fff800000000000 << 64)), 0x7fc00000); // nan - test__trunctfsf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7fc08000); + test__trunctfsf2(@bitCast(f128, @as(u128, (0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7fc08000); // inf - test__trunctfsf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7f800000); + test__trunctfsf2(@bitCast(f128, @as(u128, 0x7fff000000000000 << 64)), 0x7f800000); // zero test__trunctfsf2(0.0, 0x0); @@ -187,11 +187,11 @@ fn test__trunctfdf2(a: f128, expected: u64) void { test "trunctfdf2" { // qnan - test__trunctfdf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7ff8000000000000); + test__trunctfdf2(@bitCast(f128, @as(u128, 0x7fff800000000000 << 64)), 0x7ff8000000000000); // nan - test__trunctfdf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7ff8100000000000); + test__trunctfdf2(@bitCast(f128, @as(u128, (0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7ff8100000000000); // inf - test__trunctfdf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7ff0000000000000); + test__trunctfdf2(@bitCast(f128, @as(u128, 0x7fff000000000000 << 64)), 0x7ff0000000000000); // zero test__trunctfdf2(0.0, 0x0); @@ -217,18 +217,18 @@ fn test__truncdfsf2(a: f64, expected: u32) void { } } - @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", rep, expected); + @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", .{ rep, expected }); @panic("__trunctfsf2 test failure"); } test "truncdfsf2" { // nan & qnan - test__truncdfsf2(@bitCast(f64, u64(0x7ff8000000000000)), 0x7fc00000); - test__truncdfsf2(@bitCast(f64, u64(0x7ff0000000000001)), 0x7fc00000); + test__truncdfsf2(@bitCast(f64, @as(u64, 0x7ff8000000000000)), 0x7fc00000); + test__truncdfsf2(@bitCast(f64, @as(u64, 0x7ff0000000000001)), 0x7fc00000); // inf - test__truncdfsf2(@bitCast(f64, u64(0x7ff0000000000000)), 0x7f800000); - test__truncdfsf2(@bitCast(f64, u64(0xfff0000000000000)), 0xff800000); + test__truncdfsf2(@bitCast(f64, @as(u64, 0x7ff0000000000000)), 0x7f800000); + test__truncdfsf2(@bitCast(f64, @as(u64, 0xfff0000000000000)), 0xff800000); test__truncdfsf2(0.0, 0x0); test__truncdfsf2(1.0, 0x3f800000); diff --git a/lib/std/special/compiler_rt/udivmod.zig b/lib/std/special/compiler_rt/udivmod.zig index c3066153f..96fb7b3bd 100644 --- a/lib/std/special/compiler_rt/udivmod.zig +++ b/lib/std/special/compiler_rt/udivmod.zig @@ -76,7 +76,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // K K // --- // K 0 - sr = @bitCast(c_uint, c_int(@clz(SingleInt, d[high])) - c_int(@clz(SingleInt, n[high]))); + sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high]))); // 0 <= sr <= SingleInt.bit_count - 2 or sr large if (sr > SingleInt.bit_count - 2) { if (maybe_rem) |rem| { @@ -114,7 +114,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // K X // --- // 0 K - sr = 1 + SingleInt.bit_count + c_uint(@clz(SingleInt, d[low])) - c_uint(@clz(SingleInt, n[high])); + sr = 1 + SingleInt.bit_count + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high])); // 2 <= sr <= DoubleInt.bit_count - 1 // q.all = a << (DoubleInt.bit_count - sr); // r.all = a >> sr; @@ -140,7 +140,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // K X // --- // K K - sr = @bitCast(c_uint, c_int(@clz(SingleInt, d[high])) - c_int(@clz(SingleInt, n[high]))); + sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high]))); // 0 <= sr <= SingleInt.bit_count - 1 or sr large if (sr > SingleInt.bit_count - 1) { if (maybe_rem) |rem| { diff --git a/lib/std/special/compiler_rt/udivmoddi4.zig b/lib/std/special/compiler_rt/udivmoddi4.zig deleted file mode 100644 index de86c845e..000000000 --- a/lib/std/special/compiler_rt/udivmoddi4.zig +++ /dev/null @@ -1,11 +0,0 @@ -const udivmod = @import("udivmod.zig").udivmod; -const builtin = @import("builtin"); - -pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) u64 { - @setRuntimeSafety(builtin.is_test); - return udivmod(u64, a, b, maybe_rem); -} - -test "import udivmoddi4" { - _ = @import("udivmoddi4_test.zig"); -} diff --git a/lib/std/special/compiler_rt/udivmoddi4_test.zig b/lib/std/special/compiler_rt/udivmoddi4_test.zig index 17581fc56..48a38faa0 100644 --- a/lib/std/special/compiler_rt/udivmoddi4_test.zig +++ b/lib/std/special/compiler_rt/udivmoddi4_test.zig @@ -1,6 +1,6 @@ // Disable formatting to avoid unnecessary source repository bloat. // zig fmt: off -const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; +const __udivmoddi4 = @import("int.zig").__udivmoddi4; const testing = @import("std").testing; fn test__udivmoddi4(a: u64, b: u64, expected_q: u64, expected_r: u64) void { diff --git a/lib/std/special/compiler_rt/udivmodti4.zig b/lib/std/special/compiler_rt/udivmodti4.zig index c74dff512..7a3405c3e 100644 --- a/lib/std/special/compiler_rt/udivmodti4.zig +++ b/lib/std/special/compiler_rt/udivmodti4.zig @@ -2,13 +2,13 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { +pub fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } const v128 = @Vector(2, u64); -pub extern fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) v128 { +pub fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) callconv(.C) v128 { @setRuntimeSafety(builtin.is_test); return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), maybe_rem)); } diff --git a/lib/std/special/compiler_rt/udivti3.zig b/lib/std/special/compiler_rt/udivti3.zig index ab451859b..6283285e0 100644 --- a/lib/std/special/compiler_rt/udivti3.zig +++ b/lib/std/special/compiler_rt/udivti3.zig @@ -1,13 +1,13 @@ const udivmodti4 = @import("udivmodti4.zig"); const builtin = @import("builtin"); -pub extern fn __udivti3(a: u128, b: u128) u128 { +pub fn __udivti3(a: u128, b: u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return udivmodti4.__udivmodti4(a, b, null); } const v128 = @Vector(2, u64); -pub extern fn __udivti3_windows_x86_64(a: v128, b: v128) v128 { +pub fn __udivti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { @setRuntimeSafety(builtin.is_test); return udivmodti4.__udivmodti4_windows_x86_64(a, b, null); } diff --git a/lib/std/special/compiler_rt/umodti3.zig b/lib/std/special/compiler_rt/umodti3.zig index 7add0b2ff..4f9890f51 100644 --- a/lib/std/special/compiler_rt/umodti3.zig +++ b/lib/std/special/compiler_rt/umodti3.zig @@ -2,7 +2,7 @@ const udivmodti4 = @import("udivmodti4.zig"); const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); -pub extern fn __umodti3(a: u128, b: u128) u128 { +pub fn __umodti3(a: u128, b: u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); var r: u128 = undefined; _ = udivmodti4.__udivmodti4(a, b, &r); @@ -10,6 +10,9 @@ pub extern fn __umodti3(a: u128, b: u128) u128 { } const v128 = @Vector(2, u64); -pub extern fn __umodti3_windows_x86_64(a: v128, b: v128) v128 { - return @bitCast(v128, @inlineCall(__umodti3, @bitCast(u128, a), @bitCast(u128, b))); +pub fn __umodti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, @call(.{ .modifier = .always_inline }, __umodti3, .{ + @bitCast(u128, a), + @bitCast(u128, b), + })); } diff --git a/lib/std/special/docs/index.html b/lib/std/special/docs/index.html new file mode 100644 index 000000000..d1eeb89c2 --- /dev/null +++ b/lib/std/special/docs/index.html @@ -0,0 +1,566 @@ + + + + + Documentation - Zig + + + + +
    +
    + +
    +
    +
    + +

    Loading...

    + + +

    + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + + + + + diff --git a/lib/std/special/docs/main.js b/lib/std/special/docs/main.js new file mode 100644 index 000000000..138f79887 --- /dev/null +++ b/lib/std/special/docs/main.js @@ -0,0 +1,1964 @@ +(function() { + var domStatus = document.getElementById("status"); + var domSectNav = document.getElementById("sectNav"); + var domListNav = document.getElementById("listNav"); + var domSectPkgs = document.getElementById("sectPkgs"); + var domListPkgs = document.getElementById("listPkgs"); + var domSectTypes = document.getElementById("sectTypes"); + var domListTypes = document.getElementById("listTypes"); + var domSectNamespaces = document.getElementById("sectNamespaces"); + var domListNamespaces = document.getElementById("listNamespaces"); + var domSectErrSets = document.getElementById("sectErrSets"); + var domListErrSets = document.getElementById("listErrSets"); + var domSectFns = document.getElementById("sectFns"); + var domListFns = document.getElementById("listFns"); + var domSectFields = document.getElementById("sectFields"); + var domListFields = document.getElementById("listFields"); + var domSectGlobalVars = document.getElementById("sectGlobalVars"); + var domListGlobalVars = document.getElementById("listGlobalVars"); + var domSectValues = document.getElementById("sectValues"); + var domListValues = document.getElementById("listValues"); + var domFnProto = document.getElementById("fnProto"); + var domFnProtoCode = document.getElementById("fnProtoCode"); + var domSectParams = document.getElementById("sectParams"); + var domListParams = document.getElementById("listParams"); + var domTldDocs = document.getElementById("tldDocs"); + var domSectFnErrors = document.getElementById("sectFnErrors"); + var domListFnErrors = document.getElementById("listFnErrors"); + var domTableFnErrors = document.getElementById("tableFnErrors"); + var domFnErrorsAnyError = document.getElementById("fnErrorsAnyError"); + var domFnExamples = document.getElementById("fnExamples"); + var domListFnExamples = document.getElementById("listFnExamples"); + var domFnNoExamples = document.getElementById("fnNoExamples"); + var domDeclNoRef = document.getElementById("declNoRef"); + var domSearch = document.getElementById("search"); + var domSectSearchResults = document.getElementById("sectSearchResults"); + var domListSearchResults = document.getElementById("listSearchResults"); + var domSectSearchNoResults = document.getElementById("sectSearchNoResults"); + var domSectInfo = document.getElementById("sectInfo"); + var domTdTarget = document.getElementById("tdTarget"); + var domTdZigVer = document.getElementById("tdZigVer"); + var domHdrName = document.getElementById("hdrName"); + var domHelpModal = document.getElementById("helpDialog"); + + var searchTimer = null; + var escapeHtmlReplacements = { "&": "&", '"': """, "<": "<", ">": ">" }; + + var typeKinds = indexTypeKinds(); + var typeTypeId = findTypeTypeId(); + var pointerSizeEnum = { One: 0, Many: 1, Slice: 2, C: 3 }; + + // for each package, is an array with packages to get to this one + var canonPkgPaths = computeCanonicalPackagePaths(); + // for each decl, is an array with {declNames, pkgNames} to get to this one + var canonDeclPaths = null; // lazy; use getCanonDeclPath + // for each type, is an array with {declNames, pkgNames} to get to this one + var canonTypeDecls = null; // lazy; use getCanonTypeDecl + + var curNav = { + // each element is a package name, e.g. @import("a") then within there @import("b") + // starting implicitly from root package + pkgNames: [], + // same as above except actual packages, not names + pkgObjs: [], + // Each element is a decl name, `a.b.c`, a is 0, b is 1, c is 2, etc. + // empty array means refers to the package itself + declNames: [], + // these will be all types, except the last one may be a type or a decl + declObjs: [], + + // (a, b, c, d) comptime call; result is the value the docs refer to + callName: null, + }; + var curNavSearch = ""; + var curSearchIndex = -1; + var imFeelingLucky = false; + + var rootIsStd = detectRootIsStd(); + + // map of decl index to list of non-generic fn indexes + var nodesToFnsMap = indexNodesToFns(); + // map of decl index to list of comptime fn calls + var nodesToCallsMap = indexNodesToCalls(); + + domSearch.addEventListener('keydown', onSearchKeyDown, false); + window.addEventListener('hashchange', onHashChange, false); + window.addEventListener('keydown', onWindowKeyDown, false); + onHashChange(); + + function renderTitle() { + var list = curNav.pkgNames.concat(curNav.declNames); + var suffix = " - Zig"; + if (list.length === 0) { + if (rootIsStd) { + document.title = "std" + suffix; + } else { + document.title = zigAnalysis.params.rootName + suffix; + } + } else { + document.title = list.join('.') + suffix; + } + } + + function render() { + domStatus.classList.add("hidden"); + domFnProto.classList.add("hidden"); + domSectParams.classList.add("hidden"); + domTldDocs.classList.add("hidden"); + domSectPkgs.classList.add("hidden"); + domSectTypes.classList.add("hidden"); + domSectNamespaces.classList.add("hidden"); + domSectErrSets.classList.add("hidden"); + domSectFns.classList.add("hidden"); + domSectFields.classList.add("hidden"); + domSectSearchResults.classList.add("hidden"); + domSectSearchNoResults.classList.add("hidden"); + domSectInfo.classList.add("hidden"); + domHdrName.classList.add("hidden"); + domSectNav.classList.add("hidden"); + domSectFnErrors.classList.add("hidden"); + domFnExamples.classList.add("hidden"); + domFnNoExamples.classList.add("hidden"); + domDeclNoRef.classList.add("hidden"); + domFnErrorsAnyError.classList.add("hidden"); + domTableFnErrors.classList.add("hidden"); + domSectGlobalVars.classList.add("hidden"); + domSectValues.classList.add("hidden"); + + renderTitle(); + renderInfo(); + renderPkgList(); + + if (curNavSearch !== "") { + return renderSearch(); + } + + var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + var pkg = rootPkg; + curNav.pkgObjs = [pkg]; + for (var i = 0; i < curNav.pkgNames.length; i += 1) { + var childPkg = zigAnalysis.packages[pkg.table[curNav.pkgNames[i]]]; + if (childPkg == null) { + return render404(); + } + pkg = childPkg; + curNav.pkgObjs.push(pkg); + } + + var decl = zigAnalysis.types[pkg.main]; + curNav.declObjs = [decl]; + for (var i = 0; i < curNav.declNames.length; i += 1) { + var childDecl = findSubDecl(decl, curNav.declNames[i]); + if (childDecl == null) { + return render404(); + } + var container = getDeclContainerType(childDecl); + if (container == null) { + if (i + 1 === curNav.declNames.length) { + curNav.declObjs.push(childDecl); + break; + } else { + return render404(); + } + } + decl = container; + curNav.declObjs.push(decl); + } + + renderNav(); + + var lastDecl = curNav.declObjs[curNav.declObjs.length - 1]; + if (lastDecl.pubDecls != null) { + renderContainer(lastDecl); + } + if (lastDecl.kind == null) { + return renderUnknownDecl(lastDecl); + } else if (lastDecl.kind === 'var') { + return renderVar(lastDecl); + } else if (lastDecl.kind === 'const' && lastDecl.type != null) { + var typeObj = zigAnalysis.types[lastDecl.type]; + if (typeObj.kind === typeKinds.Fn) { + return renderFn(lastDecl); + } else { + return renderValue(lastDecl); + } + } else { + renderType(lastDecl); + } + } + + function renderUnknownDecl(decl) { + domDeclNoRef.classList.remove("hidden"); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + domTldDocs.innerHTML = markdown(docs); + } else { + domTldDocs.innerHTML = '

    There are no doc comments for this declaration.

    '; + } + domTldDocs.classList.remove("hidden"); + } + + function typeIsErrSet(typeIndex) { + var typeObj = zigAnalysis.types[typeIndex]; + return typeObj.kind === typeKinds.ErrorSet; + } + + function typeIsStructWithNoFields(typeIndex) { + var typeObj = zigAnalysis.types[typeIndex]; + if (typeObj.kind !== typeKinds.Struct) + return false; + return typeObj.fields == null || typeObj.fields.length === 0; + } + + function typeIsGenericFn(typeIndex) { + var typeObj = zigAnalysis.types[typeIndex]; + if (typeObj.kind !== typeKinds.Fn) { + return false; + } + return typeObj.generic; + } + + function renderFn(fnDecl) { + domFnProtoCode.innerHTML = typeIndexName(fnDecl.type, true, true, fnDecl); + + var docsSource = null; + var srcNode = zigAnalysis.astNodes[fnDecl.src]; + if (srcNode.docs != null) { + docsSource = srcNode.docs; + } + + var typeObj = zigAnalysis.types[fnDecl.type]; + renderFnParamDocs(fnDecl, typeObj); + + var errSetTypeIndex = null; + if (typeObj.ret != null) { + var retType = zigAnalysis.types[typeObj.ret]; + if (retType.kind === typeKinds.ErrorSet) { + errSetTypeIndex = typeObj.ret; + } else if (retType.kind === typeKinds.ErrorUnion) { + errSetTypeIndex = retType.err; + } + } + if (errSetTypeIndex != null) { + var errSetType = zigAnalysis.types[errSetTypeIndex]; + renderErrorSet(errSetType); + } + + var fnObj = zigAnalysis.fns[fnDecl.value]; + var protoSrcIndex = fnObj.src; + if (typeIsGenericFn(fnDecl.type)) { + var instantiations = nodesToFnsMap[protoSrcIndex]; + var calls = nodesToCallsMap[protoSrcIndex]; + if (instantiations == null && calls == null) { + domFnNoExamples.classList.remove("hidden"); + } else if (calls != null) { + if (fnObj.combined === undefined) fnObj.combined = allCompTimeFnCallsResult(calls); + if (fnObj.combined != null) renderContainer(fnObj.combined); + + resizeDomList(domListFnExamples, calls.length, '
  • '); + + for (var callI = 0; callI < calls.length; callI += 1) { + var liDom = domListFnExamples.children[callI]; + liDom.innerHTML = getCallHtml(fnDecl, calls[callI]); + } + + domFnExamples.classList.remove("hidden"); + } else if (instantiations != null) { + // TODO + } + } else { + + domFnExamples.classList.add("hidden"); + domFnNoExamples.classList.add("hidden"); + } + + var protoSrcNode = zigAnalysis.astNodes[protoSrcIndex]; + if (docsSource == null && protoSrcNode != null && protoSrcNode.docs != null) { + docsSource = protoSrcNode.docs; + } + if (docsSource != null) { + domTldDocs.innerHTML = markdown(docsSource); + domTldDocs.classList.remove("hidden"); + } + domFnProto.classList.remove("hidden"); + } + + function renderFnParamDocs(fnDecl, typeObj) { + var docCount = 0; + + var fnObj = zigAnalysis.fns[fnDecl.value]; + var fnNode = zigAnalysis.astNodes[fnObj.src]; + var fields = fnNode.fields; + var isVarArgs = fnNode.varArgs; + + for (var i = 0; i < fields.length; i += 1) { + var field = fields[i]; + var fieldNode = zigAnalysis.astNodes[field]; + if (fieldNode.docs != null) { + docCount += 1; + } + } + if (docCount == 0) { + return; + } + + resizeDomList(domListParams, docCount, '
    '); + var domIndex = 0; + + for (var i = 0; i < fields.length; i += 1) { + var field = fields[i]; + var fieldNode = zigAnalysis.astNodes[field]; + if (fieldNode.docs == null) { + continue; + } + var divDom = domListParams.children[domIndex]; + domIndex += 1; + var argTypeIndex = typeObj.args[i]; + + var html = '
    ' + escapeHtml(fieldNode.name) + ": ";
    +            if (isVarArgs && i === typeObj.args.length - 1) {
    +                html += '...';
    +            } else if (argTypeIndex != null) {
    +                html += typeIndexName(argTypeIndex, true, true);
    +            } else {
    +                html += 'var';
    +            }
    +
    +            html += ',
    '; + + var docs = fieldNode.docs; + if (docs != null) { + html += markdown(docs); + } + divDom.innerHTML = html; + } + domSectParams.classList.remove("hidden"); + } + + function renderNav() { + var len = curNav.pkgNames.length + curNav.declNames.length; + resizeDomList(domListNav, len, '
  • '); + var list = []; + var hrefPkgNames = []; + var hrefDeclNames = []; + for (var i = 0; i < curNav.pkgNames.length; i += 1) { + hrefPkgNames.push(curNav.pkgNames[i]); + list.push({ + name: curNav.pkgNames[i], + link: navLink(hrefPkgNames, hrefDeclNames), + }); + } + for (var i = 0; i < curNav.declNames.length; i += 1) { + hrefDeclNames.push(curNav.declNames[i]); + list.push({ + name: curNav.declNames[i], + link: navLink(hrefPkgNames, hrefDeclNames), + }); + } + + for (var i = 0; i < list.length; i += 1) { + var liDom = domListNav.children[i]; + var aDom = liDom.children[0]; + aDom.textContent = list[i].name; + aDom.setAttribute('href', list[i].link); + if (i + 1 == list.length) { + aDom.classList.add("active"); + } else { + aDom.classList.remove("active"); + } + } + + domSectNav.classList.remove("hidden"); + } + + function renderInfo() { + domTdZigVer.textContent = zigAnalysis.params.zigVersion; + domTdTarget.textContent = zigAnalysis.params.builds[0].target; + + domSectInfo.classList.remove("hidden"); + } + + function render404() { + domStatus.textContent = "404 Not Found"; + domStatus.classList.remove("hidden"); + } + + function renderPkgList() { + var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + var list = []; + for (var key in rootPkg.table) { + if (key === "root" && rootIsStd) continue; + var pkgIndex = rootPkg.table[key]; + if (zigAnalysis.packages[pkgIndex] == null) continue; + list.push({ + name: key, + pkg: pkgIndex, + }); + } + list.sort(function(a, b) { + return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase()); + }); + + if (list.length !== 0) { + resizeDomList(domListPkgs, list.length, '
  • '); + for (var i = 0; i < list.length; i += 1) { + var liDom = domListPkgs.children[i]; + var aDom = liDom.children[0]; + aDom.textContent = list[i].name; + aDom.setAttribute('href', navLinkPkg(list[i].pkg)); + if (list[i].name === curNav.pkgNames[0]) { + aDom.classList.add("active"); + } else { + aDom.classList.remove("active"); + } + } + + domSectPkgs.classList.remove("hidden"); + } + } + + function navLink(pkgNames, declNames, callName) { + if (pkgNames.length === 0 && declNames.length === 0) { + return '#'; + } else if (declNames.length === 0 && callName == null) { + return '#' + pkgNames.join('.'); + } else if (callName == null) { + return '#' + pkgNames.join('.') + ';' + declNames.join('.'); + } else { + return '#' + pkgNames.join('.') + ';' + declNames.join('.') + ';' + callName; + } + } + + function navLinkPkg(pkgIndex) { + return navLink(canonPkgPaths[pkgIndex], []); + } + + function navLinkDecl(childName) { + return navLink(curNav.pkgNames, curNav.declNames.concat([childName])); + } + + function navLinkCall(callObj) { + var declNamesCopy = curNav.declNames.concat([]); + var callName = declNamesCopy.pop(); + + callName += '('; + for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { + if (arg_i !== 0) callName += ','; + var argObj = callObj.args[arg_i]; + callName += getValueText(argObj.type, argObj.value, false, false); + } + callName += ')'; + + declNamesCopy.push(callName); + return navLink(curNav.pkgNames, declNamesCopy); + } + + function resizeDomListDl(dlDom, desiredLen) { + // add the missing dom entries + var i, ev; + for (i = dlDom.childElementCount / 2; i < desiredLen; i += 1) { + dlDom.insertAdjacentHTML('beforeend', '
    '); + } + // remove extra dom entries + while (desiredLen < dlDom.childElementCount / 2) { + dlDom.removeChild(dlDom.lastChild); + dlDom.removeChild(dlDom.lastChild); + } + } + + function resizeDomList(listDom, desiredLen, templateHtml) { + // add the missing dom entries + var i, ev; + for (i = listDom.childElementCount; i < desiredLen; i += 1) { + listDom.insertAdjacentHTML('beforeend', templateHtml); + } + // remove extra dom entries + while (desiredLen < listDom.childElementCount) { + listDom.removeChild(listDom.lastChild); + } + } + + function typeIndexName(typeIndex, wantHtml, wantLink, fnDecl, linkFnNameDecl) { + var typeObj = zigAnalysis.types[typeIndex]; + var declNameOk = declCanRepresentTypeKind(typeObj.kind); + if (wantLink) { + var declIndex = getCanonTypeDecl(typeIndex); + var declPath = getCanonDeclPath(declIndex); + if (declPath == null) { + return typeName(typeObj, wantHtml, wantLink, fnDecl, linkFnNameDecl); + } + var name = (wantLink && declCanRepresentTypeKind(typeObj.kind)) ? + declPath.declNames[declPath.declNames.length - 1] : + typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl); + if (wantLink && wantHtml) { + return '' + name + ''; + } else { + return name; + } + } else { + return typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl); + } + } + + function shouldSkipParamName(typeIndex, paramName) { + var typeObj = zigAnalysis.types[typeIndex]; + if (typeObj.kind === typeKinds.Pointer && getPtrSize(typeObj) === pointerSizeEnum.One) { + typeIndex = typeObj.elem; + } + return typeIndexName(typeIndex, false, true).toLowerCase() === paramName; + } + + function getPtrSize(typeObj) { + return (typeObj.len == null) ? pointerSizeEnum.One : typeObj.len; + } + + function getCallHtml(fnDecl, callIndex) { + var callObj = zigAnalysis.calls[callIndex]; + + // TODO make these links work + //var html = '' + escapeHtml(fnDecl.name) + '('; + var html = escapeHtml(fnDecl.name) + '('; + for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { + if (arg_i !== 0) html += ', '; + var argObj = callObj.args[arg_i]; + html += getValueText(argObj.type, argObj.value, true, true); + } + html += ')'; + return html; + } + + function getValueText(typeIndex, value, wantHtml, wantLink) { + var typeObj = zigAnalysis.types[typeIndex]; + switch (typeObj.kind) { + case typeKinds.Type: + return typeIndexName(value, wantHtml, wantLink); + case typeKinds.Fn: + var fnObj = zigAnalysis.fns[value]; + return typeIndexName(fnObj.type, wantHtml, wantLink); + case typeKinds.Int: + if (wantHtml) { + return '' + value + ''; + } else { + return value + ""; + } + default: + console.trace("TODO implement getValueText for this type:", zigAnalysis.typeKinds[typeObj.kind]); + } + } + + function typeName(typeObj, wantHtml, wantSubLink, fnDecl, linkFnNameDecl) { + switch (typeObj.kind) { + case typeKinds.Array: + var name = "["; + if (wantHtml) { + name += '' + typeObj.len + ''; + } else { + name += typeObj.len; + } + name += "]"; + name += typeIndexName(typeObj.elem, wantHtml, wantSubLink, null); + return name; + case typeKinds.Optional: + return "?" + typeIndexName(typeObj.child, wantHtml, wantSubLink, fnDecl, linkFnNameDecl); + case typeKinds.Pointer: + var name = ""; + switch (typeObj.len) { + case pointerSizeEnum.One: + default: + name += "*"; + break; + case pointerSizeEnum.Many: + name += "[*]"; + break; + case pointerSizeEnum.Slice: + name += "[]"; + break; + case pointerSizeEnum.C: + name += "[*c]"; + break; + } + if (typeObj['const']) { + if (wantHtml) { + name += 'const '; + } else { + name += "const "; + } + } + if (typeObj['volatile']) { + if (wantHtml) { + name += 'volatile '; + } else { + name += "volatile "; + } + } + if (typeObj.align != null) { + if (wantHtml) { + name += 'align('; + } else { + name += "align("; + } + if (wantHtml) { + name += '' + typeObj.align + ''; + } else { + name += typeObj.align; + } + if (typeObj.hostIntBytes != null) { + name += ":"; + if (wantHtml) { + name += '' + typeObj.bitOffsetInHost + ''; + } else { + name += typeObj.bitOffsetInHost; + } + name += ":"; + if (wantHtml) { + name += '' + typeObj.hostIntBytes + ''; + } else { + name += typeObj.hostIntBytes; + } + } + name += ") "; + } + name += typeIndexName(typeObj.elem, wantHtml, wantSubLink, null); + return name; + case typeKinds.Float: + if (wantHtml) { + return 'f' + typeObj.bits + ''; + } else { + return "f" + typeObj.bits; + } + case typeKinds.Int: + var signed = (typeObj.i != null) ? 'i' : 'u'; + var bits = typeObj[signed]; + if (wantHtml) { + return '' + signed + bits + ''; + } else { + return signed + bits; + } + case typeKinds.ComptimeInt: + if (wantHtml) { + return 'comptime_int'; + } else { + return "comptime_int"; + } + case typeKinds.ComptimeFloat: + if (wantHtml) { + return 'comptime_float'; + } else { + return "comptime_float"; + } + case typeKinds.Type: + if (wantHtml) { + return 'type'; + } else { + return "type"; + } + case typeKinds.Bool: + if (wantHtml) { + return 'bool'; + } else { + return "bool"; + } + case typeKinds.Void: + if (wantHtml) { + return 'void'; + } else { + return "void"; + } + case typeKinds.EnumLiteral: + if (wantHtml) { + return '(enum literal)'; + } else { + return "(enum literal)"; + } + case typeKinds.NoReturn: + if (wantHtml) { + return 'noreturn'; + } else { + return "noreturn"; + } + case typeKinds.ErrorSet: + if (typeObj.errors == null) { + if (wantHtml) { + return 'anyerror'; + } else { + return "anyerror"; + } + } else { + if (wantHtml) { + return escapeHtml(typeObj.name); + } else { + return typeObj.name; + } + } + case typeKinds.ErrorUnion: + var errSetTypeObj = zigAnalysis.types[typeObj.err]; + var payloadHtml = typeIndexName(typeObj.payload, wantHtml, wantSubLink, null); + if (fnDecl != null && errSetTypeObj.fn === fnDecl.value) { + // function index parameter supplied and this is the inferred error set of it + return "!" + payloadHtml; + } else { + return typeIndexName(typeObj.err, wantHtml, wantSubLink, null) + "!" + payloadHtml; + } + case typeKinds.Fn: + var payloadHtml = ""; + if (wantHtml) { + payloadHtml += 'fn'; + if (fnDecl != null) { + payloadHtml += ' '; + if (linkFnNameDecl != null) { + payloadHtml += '' + + escapeHtml(fnDecl.name) + ''; + } else { + payloadHtml += escapeHtml(fnDecl.name); + } + payloadHtml += ''; + } + } else { + payloadHtml += 'fn' + } + payloadHtml += '('; + if (typeObj.args != null) { + var fields = null; + var isVarArgs = false; + if (fnDecl != null) { + var fnObj = zigAnalysis.fns[fnDecl.value]; + var fnNode = zigAnalysis.astNodes[fnObj.src]; + fields = fnNode.fields; + isVarArgs = fnNode.varArgs; + } + + for (var i = 0; i < typeObj.args.length; i += 1) { + if (i != 0) { + payloadHtml += ', '; + } + + var argTypeIndex = typeObj.args[i]; + + if (fields != null) { + var paramNode = zigAnalysis.astNodes[fields[i]]; + + if (paramNode.varArgs) { + payloadHtml += '...'; + continue; + } + + if (paramNode.noalias) { + if (wantHtml) { + payloadHtml += 'noalias '; + } else { + payloadHtml += 'noalias '; + } + } + + if (paramNode.comptime) { + if (wantHtml) { + payloadHtml += 'comptime '; + } else { + payloadHtml += 'comptime '; + } + } + + var paramName = paramNode.name; + if (paramName != null) { + // skip if it matches the type name + if (argTypeIndex == null || !shouldSkipParamName(argTypeIndex, paramName)) { + payloadHtml += paramName + ': '; + } + } + } + + if (isVarArgs && i === typeObj.args.length - 1) { + payloadHtml += '...'; + } else if (argTypeIndex != null) { + payloadHtml += typeIndexName(argTypeIndex, wantHtml, wantSubLink); + } else if (wantHtml) { + payloadHtml += 'var'; + } else { + payloadHtml += 'var'; + } + } + } + + payloadHtml += ') '; + if (typeObj.ret != null) { + payloadHtml += typeIndexName(typeObj.ret, wantHtml, wantSubLink, fnDecl); + } else if (wantHtml) { + payloadHtml += 'var'; + } else { + payloadHtml += 'var'; + } + return payloadHtml; + default: + if (wantHtml) { + return escapeHtml(typeObj.name); + } else { + return typeObj.name; + } + } + } + + function renderType(typeObj) { + var name; + if (rootIsStd && typeObj === zigAnalysis.types[zigAnalysis.packages[zigAnalysis.rootPkg].main]) { + name = "std"; + } else { + name = typeName(typeObj, false, false); + } + if (name != null && name != "") { + domHdrName.innerText = name + " (" + zigAnalysis.typeKinds[typeObj.kind] + ")"; + domHdrName.classList.remove("hidden"); + } + if (typeObj.kind == typeKinds.ErrorSet) { + renderErrorSet(typeObj); + } + } + + function renderErrorSet(errSetType) { + if (errSetType.errors == null) { + domFnErrorsAnyError.classList.remove("hidden"); + } else { + var errorList = []; + for (var i = 0; i < errSetType.errors.length; i += 1) { + var errObj = zigAnalysis.errors[errSetType.errors[i]]; + var srcObj = zigAnalysis.astNodes[errObj.src]; + errorList.push({ + err: errObj, + docs: srcObj.docs, + }); + } + errorList.sort(function(a, b) { + return operatorCompare(a.err.name.toLowerCase(), b.err.name.toLowerCase()); + }); + + resizeDomListDl(domListFnErrors, errorList.length); + for (var i = 0; i < errorList.length; i += 1) { + var nameTdDom = domListFnErrors.children[i * 2 + 0]; + var descTdDom = domListFnErrors.children[i * 2 + 1]; + nameTdDom.textContent = errorList[i].err.name; + var docs = errorList[i].docs; + if (docs != null) { + descTdDom.innerHTML = markdown(docs); + } else { + descTdDom.textContent = ""; + } + } + domTableFnErrors.classList.remove("hidden"); + } + domSectFnErrors.classList.remove("hidden"); + } + + function allCompTimeFnCallsHaveTypeResult(typeIndex, value) { + var srcIndex = zigAnalysis.fns[value].src; + var calls = nodesToCallsMap[srcIndex]; + if (calls == null) return false; + for (var i = 0; i < calls.length; i += 1) { + var call = zigAnalysis.calls[calls[i]]; + if (call.result.type !== typeTypeId) return false; + } + return true; + } + + function allCompTimeFnCallsResult(calls) { + var firstTypeObj = null; + var containerObj = { + privDecls: [], + }; + for (var callI = 0; callI < calls.length; callI += 1) { + var call = zigAnalysis.calls[calls[callI]]; + if (call.result.type !== typeTypeId) return null; + var typeObj = zigAnalysis.types[call.result.value]; + if (!typeKindIsContainer(typeObj.kind)) return null; + if (firstTypeObj == null) { + firstTypeObj = typeObj; + containerObj.src = typeObj.src; + } else if (firstTypeObj.src !== typeObj.src) { + return null; + } + + if (containerObj.fields == null) { + containerObj.fields = (typeObj.fields || []).concat([]); + } else for (var fieldI = 0; fieldI < typeObj.fields.length; fieldI += 1) { + var prev = containerObj.fields[fieldI]; + var next = typeObj.fields[fieldI]; + if (prev === next) continue; + if (typeof(prev) === 'object') { + if (prev[next] == null) prev[next] = typeObj; + } else { + containerObj.fields[fieldI] = {}; + containerObj.fields[fieldI][prev] = firstTypeObj; + containerObj.fields[fieldI][next] = typeObj; + } + } + + if (containerObj.pubDecls == null) { + containerObj.pubDecls = (typeObj.pubDecls || []).concat([]); + } else for (var declI = 0; declI < typeObj.pubDecls.length; declI += 1) { + var prev = containerObj.pubDecls[declI]; + var next = typeObj.pubDecls[declI]; + if (prev === next) continue; + // TODO instead of showing "examples" as the public declarations, + // do logic like this: + //if (typeof(prev) !== 'object') { + // var newDeclId = zigAnalysis.decls.length; + // prev = clone(zigAnalysis.decls[prev]); + // prev.id = newDeclId; + // zigAnalysis.decls.push(prev); + // containerObj.pubDecls[declI] = prev; + //} + //mergeDecls(prev, next, firstTypeObj, typeObj); + } + } + for (var declI = 0; declI < containerObj.pubDecls.length; declI += 1) { + var decl = containerObj.pubDecls[declI]; + if (typeof(decl) === 'object') { + containerObj.pubDecls[declI] = containerObj.pubDecls[declI].id; + } + } + return containerObj; + } + + function mergeDecls(declObj, nextDeclIndex, firstTypeObj, typeObj) { + var nextDeclObj = zigAnalysis.decls[nextDeclIndex]; + if (declObj.type != null && nextDeclObj.type != null && declObj.type !== nextDeclObj.type) { + if (typeof(declObj.type) !== 'object') { + var prevType = declObj.type; + declObj.type = {}; + declObj.type[prevType] = firstTypeObj; + declObj.value = null; + } + declObj.type[nextDeclObj.type] = typeObj; + } else if (declObj.type == null && nextDeclObj != null) { + declObj.type = nextDeclObj.type; + } + if (declObj.value != null && nextDeclObj.value != null && declObj.value !== nextDeclObj.value) { + if (typeof(declObj.value) !== 'object') { + var prevValue = declObj.value; + declObj.value = {}; + declObj.value[prevValue] = firstTypeObj; + } + declObj.value[nextDeclObj.value] = typeObj; + } else if (declObj.value == null && nextDeclObj.value != null) { + declObj.value = nextDeclObj.value; + } + } + + function renderValue(decl) { + domFnProtoCode.innerHTML = 'const ' + + escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + domTldDocs.innerHTML = markdown(docs); + domTldDocs.classList.remove("hidden"); + } + + domFnProto.classList.remove("hidden"); + } + + function renderVar(decl) { + domFnProtoCode.innerHTML = 'var ' + + escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + domTldDocs.innerHTML = markdown(docs); + domTldDocs.classList.remove("hidden"); + } + + domFnProto.classList.remove("hidden"); + } + + function renderContainer(container) { + var typesList = []; + var namespacesList = []; + var errSetsList = []; + var fnsList = []; + var varsList = []; + var valsList = []; + + for (var i = 0; i < container.pubDecls.length; i += 1) { + var decl = zigAnalysis.decls[container.pubDecls[i]]; + + if (decl.kind === 'var') { + varsList.push(decl); + continue; + } else if (decl.kind === 'const' && decl.type != null) { + if (decl.type === typeTypeId) { + if (typeIsErrSet(decl.value)) { + errSetsList.push(decl); + } else if (typeIsStructWithNoFields(decl.value)) { + namespacesList.push(decl); + } else { + typesList.push(decl); + } + } else { + var typeKind = zigAnalysis.types[decl.type].kind; + if (typeKind === typeKinds.Fn) { + if (allCompTimeFnCallsHaveTypeResult(decl.type, decl.value)) { + typesList.push(decl); + } else { + fnsList.push(decl); + } + } else { + valsList.push(decl); + } + } + } + } + typesList.sort(byNameProperty); + namespacesList.sort(byNameProperty); + errSetsList.sort(byNameProperty); + fnsList.sort(byNameProperty); + varsList.sort(byNameProperty); + valsList.sort(byNameProperty); + + if (container.src != null) { + var docs = zigAnalysis.astNodes[container.src].docs; + if (docs != null) { + domTldDocs.innerHTML = markdown(docs); + domTldDocs.classList.remove("hidden"); + } + } + + if (typesList.length !== 0) { + resizeDomList(domListTypes, typesList.length, '
  • '); + for (var i = 0; i < typesList.length; i += 1) { + var liDom = domListTypes.children[i]; + var aDom = liDom.children[0]; + var decl = typesList[i]; + aDom.textContent = decl.name; + aDom.setAttribute('href', navLinkDecl(decl.name)); + } + domSectTypes.classList.remove("hidden"); + } + if (namespacesList.length !== 0) { + resizeDomList(domListNamespaces, namespacesList.length, '
  • '); + for (var i = 0; i < namespacesList.length; i += 1) { + var liDom = domListNamespaces.children[i]; + var aDom = liDom.children[0]; + var decl = namespacesList[i]; + aDom.textContent = decl.name; + aDom.setAttribute('href', navLinkDecl(decl.name)); + } + domSectNamespaces.classList.remove("hidden"); + } + + if (errSetsList.length !== 0) { + resizeDomList(domListErrSets, errSetsList.length, '
  • '); + for (var i = 0; i < errSetsList.length; i += 1) { + var liDom = domListErrSets.children[i]; + var aDom = liDom.children[0]; + var decl = errSetsList[i]; + aDom.textContent = decl.name; + aDom.setAttribute('href', navLinkDecl(decl.name)); + } + domSectErrSets.classList.remove("hidden"); + } + + if (fnsList.length !== 0) { + resizeDomList(domListFns, fnsList.length, ''); + for (var i = 0; i < fnsList.length; i += 1) { + var decl = fnsList[i]; + var trDom = domListFns.children[i]; + + var tdFnCode = trDom.children[0]; + var tdDesc = trDom.children[1]; + + tdFnCode.innerHTML = typeIndexName(decl.type, true, true, decl, navLinkDecl(decl.name)); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + tdDesc.innerHTML = shortDescMarkdown(docs); + } else { + tdDesc.textContent = ""; + } + } + domSectFns.classList.remove("hidden"); + } + + if (container.fields != null && container.fields.length !== 0) { + resizeDomList(domListFields, container.fields.length, '
    '); + + var containerNode = zigAnalysis.astNodes[container.src]; + for (var i = 0; i < container.fields.length; i += 1) { + var field = container.fields[i]; + var fieldNode = zigAnalysis.astNodes[containerNode.fields[i]]; + var divDom = domListFields.children[i]; + + var html = '
    ' + escapeHtml(fieldNode.name);
    +
    +                if (container.kind === typeKinds.Enum) {
    +                    html += ' = ' + field + '';
    +                } else {
    +                    html += ": ";
    +                    if (typeof(field) === 'object') {
    +                        html += 'var';
    +                    } else {
    +                        html += typeIndexName(field, true, true);
    +                    }
    +                }
    +
    +                html += ',
    '; + + var docs = fieldNode.docs; + if (docs != null) { + html += markdown(docs); + } + divDom.innerHTML = html; + } + domSectFields.classList.remove("hidden"); + } + + if (varsList.length !== 0) { + resizeDomList(domListGlobalVars, varsList.length, + ''); + for (var i = 0; i < varsList.length; i += 1) { + var decl = varsList[i]; + var trDom = domListGlobalVars.children[i]; + + var tdName = trDom.children[0]; + var tdNameA = tdName.children[0]; + var tdType = trDom.children[1]; + var tdDesc = trDom.children[2]; + + tdNameA.setAttribute('href', navLinkDecl(decl.name)); + tdNameA.textContent = decl.name; + + tdType.innerHTML = typeIndexName(decl.type, true, true); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + tdDesc.innerHTML = shortDescMarkdown(docs); + } else { + tdDesc.textContent = ""; + } + } + domSectGlobalVars.classList.remove("hidden"); + } + + if (valsList.length !== 0) { + resizeDomList(domListValues, valsList.length, + ''); + for (var i = 0; i < valsList.length; i += 1) { + var decl = valsList[i]; + var trDom = domListValues.children[i]; + + var tdName = trDom.children[0]; + var tdNameA = tdName.children[0]; + var tdType = trDom.children[1]; + var tdDesc = trDom.children[2]; + + tdNameA.setAttribute('href', navLinkDecl(decl.name)); + tdNameA.textContent = decl.name; + + tdType.innerHTML = typeIndexName(decl.type, true, true); + + var docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + tdDesc.innerHTML = shortDescMarkdown(docs); + } else { + tdDesc.textContent = ""; + } + } + domSectValues.classList.remove("hidden"); + } + } + + function operatorCompare(a, b) { + if (a === b) { + return 0; + } else if (a < b) { + return -1; + } else { + return 1; + } + } + + function detectRootIsStd() { + var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + if (rootPkg.table["std"] == null) { + // no std mapped into the root package + return false; + } + var stdPkg = zigAnalysis.packages[rootPkg.table["std"]]; + if (stdPkg == null) return false; + return rootPkg.file === stdPkg.file; + } + + function indexTypeKinds() { + var map = {}; + for (var i = 0; i < zigAnalysis.typeKinds.length; i += 1) { + map[zigAnalysis.typeKinds[i]] = i; + } + // This is just for debugging purposes, not needed to function + var assertList = ["Type","Void","Bool","NoReturn","Int","Float","Pointer","Array","Struct", + "ComptimeFloat","ComptimeInt","Undefined","Null","Optional","ErrorUnion","ErrorSet","Enum", + "Union","Fn","BoundFn","Opaque","Frame","AnyFrame","Vector","EnumLiteral"]; + for (var i = 0; i < assertList.length; i += 1) { + if (map[assertList[i]] == null) throw new Error("No type kind '" + assertList[i] + "' found"); + } + return map; + } + + function findTypeTypeId() { + for (var i = 0; i < zigAnalysis.types.length; i += 1) { + if (zigAnalysis.types[i].kind == typeKinds.Type) { + return i; + } + } + throw new Error("No type 'type' found"); + } + + function updateCurNav() { + curNav = { + pkgNames: [], + pkgObjs: [], + declNames: [], + declObjs: [], + }; + curNavSearch = ""; + + if (location.hash[0] === '#' && location.hash.length > 1) { + var query = location.hash.substring(1); + var qpos = query.indexOf("?"); + if (qpos === -1) { + nonSearchPart = query; + } else { + nonSearchPart = query.substring(0, qpos); + curNavSearch = decodeURIComponent(query.substring(qpos + 1)); + } + + var parts = nonSearchPart.split(";"); + curNav.pkgNames = decodeURIComponent(parts[0]).split("."); + if (parts[1] != null) { + curNav.declNames = decodeURIComponent(parts[1]).split("."); + } + } + + if (curNav.pkgNames.length === 0 && rootIsStd) { + curNav.pkgNames = ["std"]; + } + } + + function onHashChange() { + updateCurNav(); + if (domSearch.value !== curNavSearch) { + domSearch.value = curNavSearch; + } + render(); + if (imFeelingLucky) { + imFeelingLucky = false; + activateSelectedResult(); + } + } + + function findSubDecl(parentType, childName) { + if (parentType.pubDecls == null) throw new Error("parent object has no public decls"); + for (var i = 0; i < parentType.pubDecls.length; i += 1) { + var declIndex = parentType.pubDecls[i]; + var childDecl = zigAnalysis.decls[declIndex]; + if (childDecl.name === childName) { + return childDecl; + } + } + return null; + } + + function getDeclContainerType(decl) { + if (decl.type === typeTypeId) { + return zigAnalysis.types[decl.value]; + } + return null; + } + + function computeCanonicalPackagePaths() { + var list = new Array(zigAnalysis.packages.length); + // Now we try to find all the packages from root. + var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + // Breadth-first to keep the path shortest possible. + var stack = [{ + path: [], + pkg: rootPkg, + }]; + while (stack.length !== 0) { + var item = stack.shift(); + for (var key in item.pkg.table) { + var childPkgIndex = item.pkg.table[key]; + if (list[childPkgIndex] != null) continue; + var childPkg = zigAnalysis.packages[childPkgIndex]; + if (childPkg == null) continue; + + var newPath = item.path.concat([key]) + list[childPkgIndex] = newPath; + stack.push({ + path: newPath, + pkg: childPkg, + }); + } + } + return list; + } + + function typeKindIsContainer(typeKind) { + return typeKind === typeKinds.Struct || + typeKind === typeKinds.Union || + typeKind === typeKinds.Enum; + } + + function declCanRepresentTypeKind(typeKind) { + return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind); + } + + function computeCanonDeclPaths() { + var list = new Array(zigAnalysis.decls.length); + canonTypeDecls = new Array(zigAnalysis.types.length); + + for (var pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { + if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue; + var pkg = zigAnalysis.packages[pkgI]; + var pkgNames = canonPkgPaths[pkgI]; + var stack = [{ + declNames: [], + type: zigAnalysis.types[pkg.main], + }]; + while (stack.length !== 0) { + var item = stack.shift(); + + if (item.type.pubDecls != null) { + for (var declI = 0; declI < item.type.pubDecls.length; declI += 1) { + var mainDeclIndex = item.type.pubDecls[declI]; + if (list[mainDeclIndex] != null) continue; + + var decl = zigAnalysis.decls[mainDeclIndex]; + if (decl.type === typeTypeId && + declCanRepresentTypeKind(zigAnalysis.types[decl.value].kind)) + { + canonTypeDecls[decl.value] = mainDeclIndex; + } + var declNames = item.declNames.concat([decl.name]); + list[mainDeclIndex] = { + pkgNames: pkgNames, + declNames: declNames, + }; + var containerType = getDeclContainerType(decl); + if (containerType != null) { + stack.push({ + declNames: declNames, + type: containerType, + }); + } + } + } + } + } + return list; + } + + function getCanonDeclPath(index) { + if (canonDeclPaths == null) { + canonDeclPaths = computeCanonDeclPaths(); + } + return canonDeclPaths[index]; + } + + function getCanonTypeDecl(index) { + getCanonDeclPath(0); + return canonTypeDecls[index]; + } + + function escapeHtml(text) { + return text.replace(/[&"<>]/g, function (m) { + return escapeHtmlReplacements[m]; + }); + } + + function shortDescMarkdown(docs) { + var parts = docs.trim().split("\n"); + var firstLine = parts[0]; + return markdown(firstLine); + } + + function markdown(input) { + const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR + const lines = []; + + // PHASE 1: + // Dissect lines and determine the type for each line. + // Also computes indentation level and removes unnecessary whitespace + + var is_reading_code = false; + var code_indent = 0; + for (var line_no = 0; line_no < raw_lines.length; line_no++) { + const raw_line = raw_lines[line_no]; + + const line = { + indent: 0, + raw_text: raw_line, + text: raw_line.trim(), + type: "p", // p, h1 โ€ฆ h6, code, ul, ol, blockquote, skip, empty + }; + + if (!is_reading_code) { + while ((line.indent < line.raw_text.length) && line.raw_text[line.indent] == ' ') { + line.indent += 1; + } + + if (line.text.startsWith("######")) { + line.type = "h6"; + line.text = line.text.substr(6); + } + else if (line.text.startsWith("#####")) { + line.type = "h5"; + line.text = line.text.substr(5); + } + else if (line.text.startsWith("####")) { + line.type = "h4"; + line.text = line.text.substr(4); + } + else if (line.text.startsWith("###")) { + line.type = "h3"; + line.text = line.text.substr(3); + } + else if (line.text.startsWith("##")) { + line.type = "h2"; + line.text = line.text.substr(2); + } + else if (line.text.startsWith("#")) { + line.type = "h1"; + line.text = line.text.substr(1); + } + else if (line.text.startsWith("-")) { + line.type = "ul"; + line.text = line.text.substr(1); + } + else if (line.text.match(/^\d+\..*$/)) { // if line starts with {number}{dot} + const match = line.text.match(/(\d+)\./); + line.type = "ul"; + line.text = line.text.substr(match[0].length); + line.ordered_number = Number(match[1].length); + } + else if (line.text == "```") { + line.type = "skip"; + is_reading_code = true; + code_indent = line.indent; + } + else if (line.text == "") { + line.type = "empty"; + } + } + else { + if (line.text == "```") { + is_reading_code = false; + line.type = "skip"; + } else { + line.type = "code"; + line.text = line.raw_text.substr(code_indent); // remove the indent of the ``` from all the code block + } + } + + if (line.type != "skip") { + lines.push(line); + } + } + + // PHASE 2: + // Render HTML from markdown lines. + // Look at each line and emit fitting HTML code + + function markdownInlines(innerText, stopChar) { + + // inline types: + // **{INLINE}** : + // __{INLINE}__ : + // ~~{INLINE}~~ : + // *{INLINE}* : + // _{INLINE}_ : + // `{TEXT}` : + // [{INLINE}]({URL}) : + // ![{TEXT}]({URL}) : + // [[std;format.fmt]] : (inner link) + + const formats = [ + { + marker: "**", + tag: "strong", + }, + { + marker: "~~", + tag: "s", + }, + { + marker: "__", + tag: "u", + }, + { + marker: "*", + tag: "em", + } + ]; + + const stack = []; + + var innerHTML = ""; + var currentRun = ""; + + function flushRun() { + if (currentRun != "") { + innerHTML += escapeHtml(currentRun); + } + currentRun = ""; + } + + var parsing_code = false; + var codetag = ""; + var in_code = false; + + for (var i = 0; i < innerText.length; i++) { + + if (parsing_code && in_code) { + if (innerText.substr(i, codetag.length) == codetag) { + // remove leading and trailing whitespace if string both starts and ends with one. + if (currentRun[0] == " " && currentRun[currentRun.length - 1] == " ") { + currentRun = currentRun.substr(1, currentRun.length - 2); + } + flushRun(); + i += codetag.length - 1; + in_code = false; + parsing_code = false; + innerHTML += ""; + codetag = ""; + } else { + currentRun += innerText[i]; + } + continue; + } + + if (innerText[i] == "`") { + flushRun(); + if (!parsing_code) { + innerHTML += ""; + } + parsing_code = true; + codetag += "`"; + continue; + } + + if (parsing_code) { + currentRun += innerText[i]; + in_code = true; + } else { + var any = false; + for (var idx = (stack.length > 0 ? -1 : 0); idx < formats.length; idx++) { + const fmt = idx >= 0 ? formats[idx] : stack[stack.length - 1]; + if (innerText.substr(i, fmt.marker.length) == fmt.marker) { + flushRun(); + if (stack[stack.length - 1] == fmt) { + stack.pop(); + innerHTML += ""; + } else { + stack.push(fmt); + innerHTML += "<" + fmt.tag + ">"; + } + i += fmt.marker.length - 1; + any = true; + break; + } + } + if (!any) { + currentRun += innerText[i]; + } + } + } + flushRun(); + + while (stack.length > 0) { + const fmt = stack.pop(); + innerHTML += ""; + } + + return innerHTML; + } + + var html = ""; + for (var line_no = 0; line_no < lines.length; line_no++) { + const line = lines[line_no]; + + function previousLineIs(type) { + if (line_no > 0) { + return (lines[line_no - 1].type == type); + } else { + return false; + } + } + + function nextLineIs(type) { + if (line_no < (lines.length - 1)) { + return (lines[line_no + 1].type == type); + } else { + return false; + } + } + + function getPreviousLineIndent() { + if (line_no > 0) { + return lines[line_no - 1].indent; + } else { + return 0; + } + } + + function getNextLineIndent() { + if (line_no < (lines.length - 1)) { + return lines[line_no + 1].indent; + } else { + return 0; + } + } + + switch (line.type) { + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + html += "<" + line.type + ">" + markdownInlines(line.text) + "\n"; + break; + + case "ul": + case "ol": + if (!previousLineIs("ul") || getPreviousLineIndent() < line.indent) { + html += "<" + line.type + ">\n"; + } + + html += "
  • " + markdownInlines(line.text) + "
  • \n"; + + if (!nextLineIs("ul") || getNextLineIndent() < line.indent) { + html += "\n"; + } + break; + + case "p": + if (!previousLineIs("p")) { + html += "

    \n"; + } + html += markdownInlines(line.text) + "\n"; + if (!nextLineIs("p")) { + html += "

    \n"; + } + break; + + case "code": + if (!previousLineIs("code")) { + html += "
    ";
    +                    }
    +                    html += escapeHtml(line.text) + "\n";
    +                    if (!nextLineIs("code")) {
    +                        html += "
    \n"; + } + break; + } + } + + return html; + } + + function activateSelectedResult() { + if (domSectSearchResults.classList.contains("hidden")) { + return; + } + + var liDom = domListSearchResults.children[curSearchIndex]; + if (liDom == null && domListSearchResults.children.length !== 0) { + liDom = domListSearchResults.children[0]; + } + if (liDom != null) { + var aDom = liDom.children[0]; + location.href = aDom.getAttribute("href"); + curSearchIndex = -1; + } + domSearch.blur(); + } + + function onSearchKeyDown(ev) { + switch (getKeyString(ev)) { + case "Enter": + // detect if this search changes anything + var terms1 = getSearchTerms(); + startSearch(); + updateCurNav(); + var terms2 = getSearchTerms(); + // we might have to wait for onHashChange to trigger + imFeelingLucky = (terms1.join(' ') !== terms2.join(' ')); + if (!imFeelingLucky) activateSelectedResult(); + + ev.preventDefault(); + ev.stopPropagation(); + return; + case "Esc": + domSearch.value = ""; + domSearch.blur(); + curSearchIndex = -1; + ev.preventDefault(); + ev.stopPropagation(); + startSearch(); + return; + case "Up": + moveSearchCursor(-1); + ev.preventDefault(); + ev.stopPropagation(); + return; + case "Down": + moveSearchCursor(1); + ev.preventDefault(); + ev.stopPropagation(); + return; + default: + if (ev.shiftKey || ev.ctrlKey || ev.altKey) return; + + curSearchIndex = -1; + ev.stopPropagation(); + startAsyncSearch(); + return; + } + } + + function moveSearchCursor(dir) { + if (curSearchIndex < 0 || curSearchIndex >= domListSearchResults.children.length) { + if (dir > 0) { + curSearchIndex = -1 + dir; + } else if (dir < 0) { + curSearchIndex = domListSearchResults.children.length + dir; + } + } else { + curSearchIndex += dir; + } + if (curSearchIndex < 0) { + curSearchIndex = 0; + } + if (curSearchIndex >= domListSearchResults.children.length) { + curSearchIndex = domListSearchResults.children.length - 1; + } + renderSearchCursor(); + } + + function getKeyString(ev) { + var name; + var ignoreShift = false; + switch (ev.which) { + case 13: + name = "Enter"; + break; + case 27: + name = "Esc"; + break; + case 38: + name = "Up"; + break; + case 40: + name = "Down"; + break; + default: + ignoreShift = true; + name = (ev.key != null) ? ev.key : String.fromCharCode(ev.charCode || ev.keyCode); + } + if (!ignoreShift && ev.shiftKey) name = "Shift+" + name; + if (ev.altKey) name = "Alt+" + name; + if (ev.ctrlKey) name = "Ctrl+" + name; + return name; + } + + function onWindowKeyDown(ev) { + switch (getKeyString(ev)) { + case "Esc": + if (!domHelpModal.classList.contains("hidden")) { + domHelpModal.classList.add("hidden"); + ev.preventDefault(); + ev.stopPropagation(); + } + break; + case "s": + domSearch.focus(); + domSearch.select(); + ev.preventDefault(); + ev.stopPropagation(); + startAsyncSearch(); + break; + case "?": + ev.preventDefault(); + ev.stopPropagation(); + showHelpModal(); + break; + } + } + + function showHelpModal() { + domHelpModal.classList.remove("hidden"); + domHelpModal.style.left = (window.innerWidth / 2 - domHelpModal.clientWidth / 2) + "px"; + domHelpModal.style.top = (window.innerHeight / 2 - domHelpModal.clientHeight / 2) + "px"; + domHelpModal.focus(); + } + + function clearAsyncSearch() { + if (searchTimer != null) { + clearTimeout(searchTimer); + searchTimer = null; + } + } + + function startAsyncSearch() { + clearAsyncSearch(); + searchTimer = setTimeout(startSearch, 100); + } + function startSearch() { + clearAsyncSearch(); + var oldHash = location.hash; + var parts = oldHash.split("?"); + var newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); + location.hash = (parts.length === 1) ? (oldHash + newPart2) : (parts[0] + newPart2); + } + function getSearchTerms() { + var list = curNavSearch.trim().split(/[ \r\n\t]+/); + list.sort(); + return list; + } + function renderSearch() { + var matchedItems = []; + var ignoreCase = (curNavSearch.toLowerCase() === curNavSearch); + var terms = getSearchTerms(); + + decl_loop: for (var declIndex = 0; declIndex < zigAnalysis.decls.length; declIndex += 1) { + var canonPath = getCanonDeclPath(declIndex); + if (canonPath == null) continue; + + var decl = zigAnalysis.decls[declIndex]; + var lastPkgName = canonPath.pkgNames[canonPath.pkgNames.length - 1]; + var fullPathSearchText = lastPkgName + "." + canonPath.declNames.join('.'); + var astNode = zigAnalysis.astNodes[decl.src]; + var fileAndDocs = zigAnalysis.files[astNode.file]; + if (astNode.docs != null) { + fileAndDocs += "\n" + astNode.docs; + } + var fullPathSearchTextLower = fullPathSearchText; + if (ignoreCase) { + fullPathSearchTextLower = fullPathSearchTextLower.toLowerCase(); + fileAndDocs = fileAndDocs.toLowerCase(); + } + + var points = 0; + for (var termIndex = 0; termIndex < terms.length; termIndex += 1) { + var term = terms[termIndex]; + + // exact, case sensitive match of full decl path + if (fullPathSearchText === term) { + points += 4; + continue; + } + // exact, case sensitive match of just decl name + if (decl.name == term) { + points += 3; + continue; + } + // substring, case insensitive match of full decl path + if (fullPathSearchTextLower.indexOf(term) >= 0) { + points += 2; + continue; + } + if (fileAndDocs.indexOf(term) >= 0) { + points += 1; + continue; + } + + continue decl_loop; + } + + matchedItems.push({ + decl: decl, + path: canonPath, + points: points, + }); + } + + if (matchedItems.length !== 0) { + resizeDomList(domListSearchResults, matchedItems.length, '
  • '); + + matchedItems.sort(function(a, b) { + var cmp = operatorCompare(b.points, a.points); + if (cmp != 0) return cmp; + return operatorCompare(a.decl.name, b.decl.name); + }); + + for (var i = 0; i < matchedItems.length; i += 1) { + var liDom = domListSearchResults.children[i]; + var aDom = liDom.children[0]; + var match = matchedItems[i]; + var lastPkgName = match.path.pkgNames[match.path.pkgNames.length - 1]; + aDom.textContent = lastPkgName + "." + match.path.declNames.join('.'); + aDom.setAttribute('href', navLink(match.path.pkgNames, match.path.declNames)); + } + renderSearchCursor(); + + domSectSearchResults.classList.remove("hidden"); + } else { + domSectSearchNoResults.classList.remove("hidden"); + } + } + + function renderSearchCursor() { + for (var i = 0; i < domListSearchResults.children.length; i += 1) { + var liDom = domListSearchResults.children[i]; + if (curSearchIndex === i) { + liDom.classList.add("selected"); + } else { + liDom.classList.remove("selected"); + } + } + } + + function indexNodesToFns() { + var map = {}; + for (var i = 0; i < zigAnalysis.fns.length; i += 1) { + var fn = zigAnalysis.fns[i]; + if (typeIsGenericFn(fn.type)) continue; + if (map[fn.src] == null) { + map[fn.src] = [i]; + } else { + map[fn.src].push(i); + } + } + return map; + } + + function indexNodesToCalls() { + var map = {}; + for (var i = 0; i < zigAnalysis.calls.length; i += 1) { + var call = zigAnalysis.calls[i]; + var fn = zigAnalysis.fns[call.fn]; + if (map[fn.src] == null) { + map[fn.src] = [i]; + } else { + map[fn.src].push(i); + } + } + return map; + } + + function byNameProperty(a, b) { + return operatorCompare(a.name, b.name); + } + + function clone(obj) { + var res = {}; + for (var key in obj) { + res[key] = obj[key]; + } + return res; + } + + function firstObjectKey(obj) { + for (var key in obj) { + return key; + } + } +})(); diff --git a/lib/std/special/init-exe/src/main.zig b/lib/std/special/init-exe/src/main.zig index 128820d3e..5f35540dc 100644 --- a/lib/std/special/init-exe/src/main.zig +++ b/lib/std/special/init-exe/src/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); pub fn main() anyerror!void { - std.debug.warn("All your base are belong to us.\n"); + std.debug.warn("All your base are belong to us.\n", .{}); } diff --git a/lib/std/special/panic.zig b/lib/std/special/panic.zig deleted file mode 100644 index f42085126..000000000 --- a/lib/std/special/panic.zig +++ /dev/null @@ -1,31 +0,0 @@ -// This file is the default panic handler if the root source file does not -// have a `pub fn panic`. -// If this file wants to import other files *by name*, support for that would -// have to be added in the compiler. - -const builtin = @import("builtin"); -const std = @import("std"); - -pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { - @setCold(true); - switch (builtin.os) { - .freestanding => { - while (true) { - @breakpoint(); - } - }, - .wasi => { - std.debug.warn("{}", msg); - _ = std.os.wasi.proc_raise(std.os.wasi.SIGABRT); - unreachable; - }, - .uefi => { - // TODO look into using the debug info and logging helpful messages - std.os.abort(); - }, - else => { - const first_trace_addr = @returnAddress(); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); - }, - } -} diff --git a/lib/std/special/start_lib.zig b/lib/std/special/start_lib.zig deleted file mode 100644 index 701eee389..000000000 --- a/lib/std/special/start_lib.zig +++ /dev/null @@ -1,16 +0,0 @@ -// This file is included in the compilation unit when exporting a DLL on windows. - -const std = @import("std"); -const builtin = @import("builtin"); - -comptime { - @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); -} - -stdcallcc fn _DllMainCRTStartup( - hinstDLL: std.os.windows.HINSTANCE, - fdwReason: std.os.windows.DWORD, - lpReserved: std.os.windows.LPVOID, -) std.os.windows.BOOL { - return std.os.windows.TRUE; -} diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index db0129305..0335c8562 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -1,29 +1,45 @@ const std = @import("std"); const io = std.io; const builtin = @import("builtin"); -const test_fn_list = builtin.test_functions; -const warn = std.debug.warn; -pub fn main() !void { +pub fn main() anyerror!void { + const test_fn_list = builtin.test_functions; var ok_count: usize = 0; var skip_count: usize = 0; - for (test_fn_list) |test_fn, i| { - warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name); + var progress = std.Progress{}; + const root_node = progress.start("Test", test_fn_list.len) catch |err| switch (err) { + // TODO still run tests in this case + error.TimerUnsupported => @panic("timer unsupported"), + }; + for (test_fn_list) |test_fn, i| { + var test_node = root_node.start(test_fn.name, null); + test_node.activate(); + progress.refresh(); + if (progress.terminal == null) { + std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name }); + } if (test_fn.func()) |_| { ok_count += 1; - warn("OK\n"); + test_node.end(); + if (progress.terminal == null) std.debug.warn("OK\n", .{}); } else |err| switch (err) { error.SkipZigTest => { skip_count += 1; - warn("SKIP\n"); + test_node.end(); + progress.log("{}...SKIP\n", .{test_fn.name}); + if (progress.terminal == null) std.debug.warn("SKIP\n", .{}); + }, + else => { + progress.log("", .{}); + return err; }, - else => return err, } } + root_node.end(); if (ok_count == test_fn_list.len) { - warn("All tests passed.\n"); + std.debug.warn("All {} tests passed.\n", .{ok_count}); } else { - warn("{} passed; {} skipped.\n", ok_count, skip_count); + std.debug.warn("{} passed; {} skipped.\n", .{ ok_count, skip_count }); } } diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig index 905460a2d..1a3239a95 100644 --- a/lib/std/spinlock.zig +++ b/lib/std/spinlock.zig @@ -1,32 +1,85 @@ const std = @import("std.zig"); const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; -const assert = std.debug.assert; pub const SpinLock = struct { - lock: u8, // TODO use a bool or enum + state: State, + + const State = enum(u8) { + Unlocked, + Locked, + }; pub const Held = struct { spinlock: *SpinLock, pub fn release(self: Held) void { - assert(@atomicRmw(u8, &self.spinlock.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + @atomicStore(State, &self.spinlock.state, .Unlocked, .Release); } }; pub fn init() SpinLock { - return SpinLock{ .lock = 0 }; + return SpinLock{ .state = .Unlocked }; + } + + pub fn deinit(self: *SpinLock) void { + self.* = undefined; + } + + pub fn tryAcquire(self: *SpinLock) ?Held { + return switch (@atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire)) { + .Unlocked => Held{ .spinlock = self }, + .Locked => null, + }; } pub fn acquire(self: *SpinLock) Held { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - return Held{ .spinlock = self }; + while (true) { + return self.tryAcquire() orelse { + yield(); + continue; + }; + } + } + + pub fn yield() void { + // On native windows, SwitchToThread is too expensive, + // and yielding for 380-410 iterations was found to be + // a nice sweet spot. Posix systems on the other hand, + // especially linux, perform better by yielding the thread. + switch (builtin.os) { + .windows => loopHint(400), + else => std.os.sched_yield() catch loopHint(1), + } + } + + /// Hint to the cpu that execution is spinning + /// for the given amount of iterations. + pub fn loopHint(iterations: usize) void { + var i = iterations; + while (i != 0) : (i -= 1) { + switch (builtin.arch) { + // these instructions use a memory clobber as they + // flush the pipeline of any speculated reads/writes. + .i386, .x86_64 => asm volatile ("pause" + : + : + : "memory" + ), + .arm, .aarch64 => asm volatile ("yield" + : + : + : "memory" + ), + else => std.os.sched_yield() catch {}, + } + } } }; test "spinlock" { var lock = SpinLock.init(); + defer lock.deinit(); + const held = lock.acquire(); defer held.release(); } diff --git a/lib/std/special/start.zig b/lib/std/start.zig similarity index 53% rename from lib/std/special/start.zig rename to lib/std/start.zig index 205766d05..c3844e4d1 100644 --- a/lib/std/special/start.zig +++ b/lib/std/start.zig @@ -1,8 +1,8 @@ // This file is included in the compilation unit when exporting an executable. const root = @import("root"); -const std = @import("std"); -const builtin = @import("builtin"); +const std = @import("std.zig"); +const builtin = std.builtin; const assert = std.debug.assert; const uefi = std.os.uefi; @@ -17,33 +17,52 @@ const is_mips = switch (builtin.arch) { .mips, .mipsel, .mips64, .mips64el => true, else => false, }; +const start_sym_name = if (is_mips) "__start" else "_start"; comptime { - if (builtin.link_libc) { - @export("main", main, .Strong); - } else if (builtin.os == .windows) { - @export("WinMainCRTStartup", WinMainCRTStartup, .Strong); - } else if (is_wasm and builtin.os == .freestanding) { - @export("_start", wasm_freestanding_start, .Strong); - } else if (builtin.os == .uefi) { - @export("EfiMain", EfiMain, .Strong); - } else if (is_mips) { - if (!@hasDecl(root, "__start")) @export("__start", _start, .Strong); - } else { - if (!@hasDecl(root, "_start")) @export("_start", _start, .Strong); + if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { + if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { + @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); + } + } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { + if (builtin.link_libc and @hasDecl(root, "main")) { + if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { + @export(main, .{ .name = "main", .linkage = .Weak }); + } + } else if (builtin.os == .windows) { + if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) { + @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" }); + } + } else if (builtin.os == .uefi) { + if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); + } else if (is_wasm and builtin.os == .freestanding) { + if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); + } else if (builtin.os != .other and builtin.os != .freestanding) { + if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); + } } } -extern fn wasm_freestanding_start() void { - _ = callMain(); +fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, lpReserved: std.os.windows.LPVOID) callconv(.Stdcall) std.os.windows.BOOL { + if (@hasDecl(root, "DllMain")) { + return root.DllMain(hinstDLL, fdwReason, lpReserved); + } + + return std.os.windows.TRUE; } -extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize { +fn wasm_freestanding_start() callconv(.C) void { + // This is marked inline because for some reason LLVM in release mode fails to inline it, + // and we want fewer call frames in stack traces. + _ = @call(.{ .modifier = .always_inline }, callMain, .{}); +} + +fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize { const bad_efi_main_ret = "expected return type of main to be 'void', 'noreturn', or 'usize'"; uefi.handle = handle; uefi.system_table = system_table; - switch (@typeInfo(@typeOf(root.main).ReturnType)) { + switch (@typeInfo(@TypeOf(root.main).ReturnType)) { .NoReturn => { root.main(); }, @@ -61,9 +80,11 @@ extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) u } } -nakedcc fn _start() noreturn { +fn _start() callconv(.Naked) noreturn { if (builtin.os == builtin.Os.wasi) { - std.os.wasi.proc_exit(callMain()); + // This is marked inline because for some reason LLVM in release mode fails to inline it, + // and we want fewer call frames in stack traces. + std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{})); } switch (builtin.arch) { @@ -99,10 +120,10 @@ nakedcc fn _start() noreturn { } // If LLVM inlines stack variables into _start, they will overwrite // the command line argument data. - @noInlineCall(posixCallMainAndExit); + @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); } -extern fn WinMainCRTStartup() noreturn { +fn WinMainCRTStartup() callconv(.Stdcall) noreturn { @setAlignStack(16); if (!builtin.single_threaded) { _ = @import("start_windows_tls.zig"); @@ -110,7 +131,7 @@ extern fn WinMainCRTStartup() noreturn { std.debug.maybeEnableSegfaultHandler(); - std.os.windows.kernel32.ExitProcess(callMain()); + std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain()); } // TODO https://github.com/ziglang/zig/issues/265 @@ -119,16 +140,16 @@ fn posixCallMainAndExit() noreturn { @setAlignStack(16); } const argc = starting_stack_ptr[0]; - const argv = @ptrCast([*][*]u8, starting_stack_ptr + 1); + const argv = @ptrCast([*][*:0]u8, starting_stack_ptr + 1); - const envp_optional = @ptrCast([*]?[*]u8, argv + argc + 1); + const envp_optional = @ptrCast([*:null]?[*:0]u8, @alignCast(@alignOf(usize), argv + argc + 1)); var envp_count: usize = 0; while (envp_optional[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; + const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count]; if (builtin.os == .linux) { // Find the beginning of the auxiliary vector - const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); + const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); std.os.linux.elf_aux_maybe = auxv; // Initialize the TLS area const gnu_stack_phdr = std.os.linux.tls.initTLS() orelse @panic("ELF missing stack size"); @@ -158,26 +179,26 @@ fn posixCallMainAndExit() noreturn { // 0, //) catch @panic("out of memory"); //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {}; - //std.os.exit(@newStackCall(new_stack, callMainWithArgs, argc, argv, envp)); + //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp})); } - std.os.exit(@inlineCall(callMainWithArgs, argc, argv, envp)); + std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); } -fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { +fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; std.debug.maybeEnableSegfaultHandler(); - return callMain(); + return initEventLoopAndCallMain(); } -extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { +fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} - const envp = @ptrCast([*][*]u8, c_envp)[0..env_count]; - return @inlineCall(callMainWithArgs, @intCast(usize, c_argc), c_argv, envp); + const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; + return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); } // General error message for a malformed return type @@ -185,8 +206,42 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. -inline fn callMain() u8 { - switch (@typeInfo(@typeOf(root.main).ReturnType)) { +inline fn initEventLoopAndCallMain() u8 { + if (std.event.Loop.instance) |loop| { + if (!@hasDecl(root, "event_loop")) { + loop.init() catch |err| { + std.debug.warn("error: {}\n", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + return 1; + }; + defer loop.deinit(); + + var result: u8 = undefined; + var frame: @Frame(callMainAsync) = undefined; + _ = @asyncCall(&frame, &result, callMainAsync, loop); + loop.run(); + return result; + } + } + + // This is marked inline because for some reason LLVM in release mode fails to inline it, + // and we want fewer call frames in stack traces. + return @call(.{ .modifier = .always_inline }, callMain, .{}); +} + +async fn callMainAsync(loop: *std.event.Loop) u8 { + // This prevents the event loop from terminating at least until main() has returned. + loop.beginOneEvent(); + defer loop.finishOneEvent(); + return callMain(); +} + +// This is not marked inline because it is called with @asyncCall when +// there is an event loop. +pub fn callMain() u8 { + switch (@typeInfo(@TypeOf(root.main).ReturnType)) { .NoReturn => { root.main(); }, @@ -202,13 +257,13 @@ inline fn callMain() u8 { }, .ErrorUnion => { const result = root.main() catch |err| { - std.debug.warn("error: {}\n", @errorName(err)); + std.debug.warn("error: {}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } return 1; }; - switch (@typeInfo(@typeOf(result))) { + switch (@typeInfo(@TypeOf(result))) { .Void => return 0, .Int => |info| { if (info.bits != 8) { @@ -222,6 +277,3 @@ inline fn callMain() u8 { else => @compileError(bad_main_ret), } } - -const main_thread_tls_align = 32; -var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64; diff --git a/lib/std/special/start_windows_tls.zig b/lib/std/start_windows_tls.zig similarity index 81% rename from lib/std/special/start_windows_tls.zig rename to lib/std/start_windows_tls.zig index 71165d355..f6dd2bc13 100644 --- a/lib/std/special/start_windows_tls.zig +++ b/lib/std/start_windows_tls.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = std.builtin; export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; export var _tls_start: u8 linksection(".tls") = 0; @@ -6,6 +7,17 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0; export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; +comptime { + if (builtin.arch == .i386) { + // The __tls_array is the offset of the ThreadLocalStoragePointer field + // in the TEB block whose base address held in the %fs segment. + asm ( + \\ .global __tls_array + \\ __tls_array = 0x2C + ); + } +} + // TODO this is how I would like it to be expressed // TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate // why they do that. diff --git a/lib/std/statically_initialized_mutex.zig b/lib/std/statically_initialized_mutex.zig deleted file mode 100644 index 2ad47b5d9..000000000 --- a/lib/std/statically_initialized_mutex.zig +++ /dev/null @@ -1,105 +0,0 @@ -const std = @import("std.zig"); -const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; -const assert = std.debug.assert; -const expect = std.testing.expect; -const windows = std.os.windows; - -/// Lock may be held only once. If the same thread -/// tries to acquire the same mutex twice, it deadlocks. -/// This type is intended to be initialized statically. If you don't -/// require static initialization, use std.Mutex. -/// On Windows, this mutex allocates resources when it is -/// first used, and the resources cannot be freed. -/// On Linux, this is an alias of std.Mutex. -pub const StaticallyInitializedMutex = switch (builtin.os) { - builtin.Os.linux => std.Mutex, - builtin.Os.windows => struct { - lock: windows.CRITICAL_SECTION, - init_once: windows.RTL_RUN_ONCE, - - pub const Held = struct { - mutex: *StaticallyInitializedMutex, - - pub fn release(self: Held) void { - windows.kernel32.LeaveCriticalSection(&self.mutex.lock); - } - }; - - pub fn init() StaticallyInitializedMutex { - return StaticallyInitializedMutex{ - .lock = undefined, - .init_once = windows.INIT_ONCE_STATIC_INIT, - }; - } - - extern fn initCriticalSection( - InitOnce: *windows.RTL_RUN_ONCE, - Parameter: ?*c_void, - Context: ?*c_void, - ) windows.BOOL { - const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter)); - windows.kernel32.InitializeCriticalSection(lock); - return windows.TRUE; - } - - /// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better - /// implementation of a runtime initialized mutex, remove this function. - pub fn deinit(self: *StaticallyInitializedMutex) void { - windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null); - windows.kernel32.DeleteCriticalSection(&self.lock); - } - - pub fn acquire(self: *StaticallyInitializedMutex) Held { - windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null); - windows.kernel32.EnterCriticalSection(&self.lock); - return Held{ .mutex = self }; - } - }, - else => std.Mutex, -}; - -test "std.StaticallyInitializedMutex" { - const TestContext = struct { - data: i128, - - const TestContext = @This(); - const incr_count = 10000; - - var mutex = StaticallyInitializedMutex.init(); - - fn worker(ctx: *TestContext) void { - var i: usize = 0; - while (i != TestContext.incr_count) : (i += 1) { - const held = mutex.acquire(); - defer held.release(); - - ctx.data += 1; - } - } - }; - - var plenty_of_memory = try std.heap.direct_allocator.alloc(u8, 300 * 1024); - defer std.heap.direct_allocator.free(plenty_of_memory); - - var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); - var a = &fixed_buffer_allocator.allocator; - - var context = TestContext{ .data = 0 }; - - if (builtin.single_threaded) { - TestContext.worker(&context); - expect(context.data == TestContext.incr_count); - } else { - const thread_count = 10; - var threads: [thread_count]*std.Thread = undefined; - for (threads) |*t| { - t.* = try std.Thread.spawn(&context, TestContext.worker); - } - for (threads) |t| - t.wait(); - - expect(context.data == thread_count * TestContext.incr_count); - } -} diff --git a/lib/std/std.zig b/lib/std/std.zig index 1c64242c4..dd4d968ef 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -6,26 +6,29 @@ pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; pub const Buffer = @import("buffer.zig").Buffer; pub const BufferOutStream = @import("io.zig").BufferOutStream; +pub const ChildProcess = @import("child_process.zig").ChildProcess; pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const Mutex = @import("mutex.zig").Mutex; -pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian; pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray; -pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian; +pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian; pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; +pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian; pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; -pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; -pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; +pub const Progress = @import("progress.zig").Progress; +pub const ResetEvent = @import("reset_event.zig").ResetEvent; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; +pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; pub const SpinLock = @import("spinlock.zig").SpinLock; pub const StringHashMap = @import("hash_map.zig").StringHashMap; -pub const ChildProcess = @import("child_process.zig").ChildProcess; pub const TailQueue = @import("linked_list.zig").TailQueue; +pub const Target = @import("target.zig").Target; pub const Thread = @import("thread.zig").Thread; pub const atomic = @import("atomic.zig"); pub const base64 = @import("base64.zig"); pub const build = @import("build.zig"); +pub const builtin = @import("builtin.zig"); pub const c = @import("c.zig"); pub const coff = @import("coff.zig"); pub const crypto = @import("crypto.zig"); @@ -34,6 +37,7 @@ pub const debug = @import("debug.zig"); pub const dwarf = @import("dwarf.zig"); pub const elf = @import("elf.zig"); pub const event = @import("event.zig"); +pub const fifo = @import("fifo.zig"); pub const fmt = @import("fmt.zig"); pub const fs = @import("fs.zig"); pub const hash = @import("hash.zig"); @@ -61,62 +65,14 @@ pub const time = @import("time.zig"); pub const unicode = @import("unicode.zig"); pub const valgrind = @import("valgrind.zig"); pub const zig = @import("zig.zig"); +pub const start = @import("start.zig"); -test "std" { - // run tests from these - _ = @import("array_list.zig"); - _ = @import("atomic.zig"); - _ = @import("bloom_filter.zig"); - _ = @import("buf_map.zig"); - _ = @import("buf_set.zig"); - _ = @import("buffer.zig"); - _ = @import("hash_map.zig"); - _ = @import("linked_list.zig"); - _ = @import("mutex.zig"); - _ = @import("statically_initialized_mutex.zig"); - _ = @import("segmented_list.zig"); - _ = @import("spinlock.zig"); - _ = @import("child_process.zig"); - - _ = @import("ascii.zig"); - _ = @import("base64.zig"); - _ = @import("build.zig"); - _ = @import("c.zig"); - _ = @import("coff.zig"); - _ = @import("crypto.zig"); - _ = @import("cstr.zig"); - _ = @import("debug.zig"); - _ = @import("dwarf.zig"); - _ = @import("dynamic_library.zig"); - _ = @import("elf.zig"); - _ = @import("event.zig"); - _ = @import("fmt.zig"); - _ = @import("fs.zig"); - _ = @import("hash.zig"); - _ = @import("heap.zig"); - _ = @import("http.zig"); - _ = @import("io.zig"); - _ = @import("json.zig"); - _ = @import("lazy_init.zig"); - _ = @import("macho.zig"); - _ = @import("math.zig"); - _ = @import("mem.zig"); - _ = @import("meta.zig"); - _ = @import("net.zig"); - _ = @import("os.zig"); - _ = @import("pdb.zig"); - _ = @import("process.zig"); - _ = @import("packed_int_array.zig"); - _ = @import("priority_queue.zig"); - _ = @import("rand.zig"); - _ = @import("rb.zig"); - _ = @import("sort.zig"); - _ = @import("testing.zig"); - _ = @import("thread.zig"); - _ = @import("time.zig"); - _ = @import("unicode.zig"); - _ = @import("valgrind.zig"); - _ = @import("zig.zig"); - - _ = @import("debug/leb128.zig"); +// This forces the start.zig file to be imported, and the comptime logic inside that +// file decides whether to export any appropriate start symbols. +comptime { + _ = start; +} + +test "" { + meta.refAllDecls(@This()); } diff --git a/lib/std/target.zig b/lib/std/target.zig new file mode 100644 index 000000000..22fea691c --- /dev/null +++ b/lib/std/target.zig @@ -0,0 +1,780 @@ +const std = @import("std.zig"); +const mem = std.mem; +const builtin = std.builtin; + +/// TODO Nearly all the functions in this namespace would be +/// better off if https://github.com/ziglang/zig/issues/425 +/// was solved. +pub const Target = union(enum) { + Native: void, + Cross: Cross, + + pub const Os = enum { + freestanding, + ananas, + cloudabi, + dragonfly, + freebsd, + fuchsia, + ios, + kfreebsd, + linux, + lv2, + macosx, + netbsd, + openbsd, + solaris, + windows, + haiku, + minix, + rtems, + nacl, + cnk, + aix, + cuda, + nvcl, + amdhsa, + ps4, + elfiamcu, + tvos, + watchos, + mesa3d, + contiki, + amdpal, + hermit, + hurd, + wasi, + emscripten, + uefi, + other, + }; + + pub const Arch = union(enum) { + arm: Arm32, + armeb: Arm32, + aarch64: Arm64, + aarch64_be: Arm64, + aarch64_32: Arm64, + arc, + avr, + bpfel, + bpfeb, + hexagon, + mips, + mipsel, + mips64, + mips64el, + msp430, + powerpc, + powerpc64, + powerpc64le, + r600, + amdgcn, + riscv32, + riscv64, + sparc, + sparcv9, + sparcel, + s390x, + tce, + tcele, + thumb: Arm32, + thumbeb: Arm32, + i386, + x86_64, + xcore, + nvptx, + nvptx64, + le32, + le64, + amdil, + amdil64, + hsail, + hsail64, + spir, + spir64, + kalimba: Kalimba, + shave, + lanai, + wasm32, + wasm64, + renderscript32, + renderscript64, + + pub const Arm32 = enum { + v8_5a, + v8_4a, + v8_3a, + v8_2a, + v8_1a, + v8, + v8r, + v8m_baseline, + v8m_mainline, + v8_1m_mainline, + v7, + v7em, + v7m, + v7s, + v7k, + v7ve, + v6, + v6m, + v6k, + v6t2, + v5, + v5te, + v4t, + }; + pub const Arm64 = enum { + v8_5a, + v8_4a, + v8_3a, + v8_2a, + v8_1a, + v8, + v8r, + v8m_baseline, + v8m_mainline, + }; + pub const Kalimba = enum { + v5, + v4, + v3, + }; + pub const Mips = enum { + r6, + }; + + pub fn toElfMachine(arch: Arch) std.elf.EM { + return switch (arch) { + .avr => ._AVR, + .msp430 => ._MSP430, + .arc => ._ARC, + .arm => ._ARM, + .armeb => ._ARM, + .hexagon => ._HEXAGON, + .le32 => ._NONE, + .mips => ._MIPS, + .mipsel => ._MIPS_RS3_LE, + .powerpc => ._PPC, + .r600 => ._NONE, + .riscv32 => ._RISCV, + .sparc => ._SPARC, + .sparcel => ._SPARC, + .tce => ._NONE, + .tcele => ._NONE, + .thumb => ._ARM, + .thumbeb => ._ARM, + .i386 => ._386, + .xcore => ._XCORE, + .nvptx => ._NONE, + .amdil => ._NONE, + .hsail => ._NONE, + .spir => ._NONE, + .kalimba => ._CSR_KALIMBA, + .shave => ._NONE, + .lanai => ._LANAI, + .wasm32 => ._NONE, + .renderscript32 => ._NONE, + .aarch64_32 => ._AARCH64, + .aarch64 => ._AARCH64, + .aarch64_be => ._AARCH64, + .mips64 => ._MIPS, + .mips64el => ._MIPS_RS3_LE, + .powerpc64 => ._PPC64, + .powerpc64le => ._PPC64, + .riscv64 => ._RISCV, + .x86_64 => ._X86_64, + .nvptx64 => ._NONE, + .le64 => ._NONE, + .amdil64 => ._NONE, + .hsail64 => ._NONE, + .spir64 => ._NONE, + .wasm64 => ._NONE, + .renderscript64 => ._NONE, + .amdgcn => ._NONE, + .bpfel => ._BPF, + .bpfeb => ._BPF, + .sparcv9 => ._SPARCV9, + .s390x => ._S390, + }; + } + + pub fn endian(arch: Arch) builtin.Endian { + return switch (arch) { + .avr, + .arm, + .aarch64_32, + .aarch64, + .amdgcn, + .amdil, + .amdil64, + .bpfel, + .hexagon, + .hsail, + .hsail64, + .kalimba, + .le32, + .le64, + .mipsel, + .mips64el, + .msp430, + .nvptx, + .nvptx64, + .sparcel, + .tcele, + .powerpc64le, + .r600, + .riscv32, + .riscv64, + .i386, + .x86_64, + .wasm32, + .wasm64, + .xcore, + .thumb, + .spir, + .spir64, + .renderscript32, + .renderscript64, + .shave, + => .Little, + + .arc, + .armeb, + .aarch64_be, + .bpfeb, + .mips, + .mips64, + .powerpc, + .powerpc64, + .thumbeb, + .sparc, + .sparcv9, + .tce, + .lanai, + .s390x, + => .Big, + }; + } + }; + + pub const Abi = enum { + none, + gnu, + gnuabin32, + gnuabi64, + gnueabi, + gnueabihf, + gnux32, + code16, + eabi, + eabihf, + elfv1, + elfv2, + android, + musl, + musleabi, + musleabihf, + msvc, + itanium, + cygnus, + coreclr, + simulator, + macabi, + }; + + pub const ObjectFormat = enum { + unknown, + coff, + elf, + macho, + wasm, + }; + + pub const SubSystem = enum { + Console, + Windows, + Posix, + Native, + EfiApplication, + EfiBootServiceDriver, + EfiRom, + EfiRuntimeDriver, + }; + + pub const Cross = struct { + arch: Arch, + os: Os, + abi: Abi, + }; + + pub const current = Target{ + .Cross = Cross{ + .arch = builtin.arch, + .os = builtin.os, + .abi = builtin.abi, + }, + }; + + pub const stack_align = 16; + + pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return std.fmt.allocPrint(allocator, "{}{}-{}-{}", .{ + @tagName(self.getArch()), + Target.archSubArchName(self.getArch()), + @tagName(self.getOs()), + @tagName(self.getAbi()), + }); + } + + /// Returned slice must be freed by the caller. + pub fn vcpkgTriplet(allocator: *mem.Allocator, target: Target, linkage: std.build.VcpkgLinkage) ![]const u8 { + const arch = switch (target.getArch()) { + .i386 => "x86", + .x86_64 => "x64", + + .arm, + .armeb, + .thumb, + .thumbeb, + .aarch64_32, + => "arm", + + .aarch64, + .aarch64_be, + => "arm64", + + else => return error.VcpkgNoSuchArchitecture, + }; + + const os = switch (target.getOs()) { + .windows => "windows", + .linux => "linux", + .macosx => "macos", + else => return error.VcpkgNoSuchOs, + }; + + if (linkage == .Static) { + return try mem.join(allocator, "-", &[_][]const u8{ arch, os, "static" }); + } else { + return try mem.join(allocator, "-", &[_][]const u8{ arch, os }); + } + } + + pub fn allocDescription(self: Target, allocator: *mem.Allocator) ![]u8 { + // TODO is there anything else worthy of the description that is not + // already captured in the triple? + return self.zigTriple(allocator); + } + + pub fn zigTripleNoSubArch(self: Target, allocator: *mem.Allocator) ![]u8 { + return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + @tagName(self.getArch()), + @tagName(self.getOs()), + @tagName(self.getAbi()), + }); + } + + pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { + return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + @tagName(self.getArch()), + @tagName(self.getOs()), + @tagName(self.getAbi()), + }); + } + + pub fn parse(text: []const u8) !Target { + var it = mem.separate(text, "-"); + const arch_name = it.next() orelse return error.MissingArchitecture; + const os_name = it.next() orelse return error.MissingOperatingSystem; + const abi_name = it.next(); + + var cross = Cross{ + .arch = try parseArchSub(arch_name), + .os = try parseOs(os_name), + .abi = undefined, + }; + cross.abi = if (abi_name) |n| try parseAbi(n) else defaultAbi(cross.arch, cross.os); + return Target{ .Cross = cross }; + } + + pub fn defaultAbi(arch: Arch, target_os: Os) Abi { + switch (arch) { + .wasm32, .wasm64 => return .musl, + else => {}, + } + switch (target_os) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .other, + => return .eabi, + .openbsd, + .macosx, + .freebsd, + .ios, + .tvos, + .watchos, + .fuchsia, + .kfreebsd, + .netbsd, + .hurd, + => return .gnu, + .windows, + .uefi, + => return .msvc, + .linux, + .wasi, + .emscripten, + => return .musl, + } + } + + pub const ParseArchSubError = error{ + UnknownArchitecture, + UnknownSubArchitecture, + }; + + pub fn parseArchSub(text: []const u8) ParseArchSubError!Arch { + const info = @typeInfo(Arch); + inline for (info.Union.fields) |field| { + if (mem.eql(u8, text, field.name)) { + if (field.field_type == void) { + return @as(Arch, @field(Arch, field.name)); + } else { + const sub_info = @typeInfo(field.field_type); + inline for (sub_info.Enum.fields) |sub_field| { + const combined = field.name ++ sub_field.name; + if (mem.eql(u8, text, combined)) { + return @unionInit(Arch, field.name, @field(field.field_type, sub_field.name)); + } + } + return error.UnknownSubArchitecture; + } + } + } + return error.UnknownArchitecture; + } + + pub fn parseOs(text: []const u8) !Os { + const info = @typeInfo(Os); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, text, field.name)) { + return @field(Os, field.name); + } + } + return error.UnknownOperatingSystem; + } + + pub fn parseAbi(text: []const u8) !Abi { + const info = @typeInfo(Abi); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, text, field.name)) { + return @field(Abi, field.name); + } + } + return error.UnknownApplicationBinaryInterface; + } + + fn archSubArchName(arch: Arch) []const u8 { + return switch (arch) { + .arm => |sub| @tagName(sub), + .armeb => |sub| @tagName(sub), + .thumb => |sub| @tagName(sub), + .thumbeb => |sub| @tagName(sub), + .aarch64 => |sub| @tagName(sub), + .aarch64_be => |sub| @tagName(sub), + .kalimba => |sub| @tagName(sub), + else => "", + }; + } + + pub fn subArchName(self: Target) []const u8 { + switch (self) { + .Native => return archSubArchName(builtin.arch), + .Cross => |cross| return archSubArchName(cross.arch), + } + } + + pub fn oFileExt(self: Target) []const u8 { + return switch (self.getAbi()) { + .msvc => ".obj", + else => ".o", + }; + } + + pub fn exeFileExt(self: Target) []const u8 { + if (self.isWindows()) { + return ".exe"; + } else if (self.isUefi()) { + return ".efi"; + } else if (self.isWasm()) { + return ".wasm"; + } else { + return ""; + } + } + + pub fn staticLibSuffix(self: Target) []const u8 { + if (self.isWasm()) { + return ".wasm"; + } + switch (self.getAbi()) { + .msvc => return ".lib", + else => return ".a", + } + } + + pub fn dynamicLibSuffix(self: Target) []const u8 { + if (self.isDarwin()) { + return ".dylib"; + } + switch (self.getOs()) { + .windows => return ".dll", + else => return ".so", + } + } + + pub fn libPrefix(self: Target) []const u8 { + if (self.isWasm()) { + return ""; + } + switch (self.getAbi()) { + .msvc => return "", + else => return "lib", + } + } + + pub fn getOs(self: Target) Os { + return switch (self) { + .Native => builtin.os, + .Cross => |t| t.os, + }; + } + + pub fn getArch(self: Target) Arch { + switch (self) { + .Native => return builtin.arch, + .Cross => |t| return t.arch, + } + } + + pub fn getAbi(self: Target) Abi { + switch (self) { + .Native => return builtin.abi, + .Cross => |t| return t.abi, + } + } + + pub fn isMinGW(self: Target) bool { + return self.isWindows() and self.isGnu(); + } + + pub fn isGnu(self: Target) bool { + return switch (self.getAbi()) { + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + else => false, + }; + } + + pub fn isMusl(self: Target) bool { + return switch (self.getAbi()) { + .musl, .musleabi, .musleabihf => true, + else => false, + }; + } + + pub fn isDarwin(self: Target) bool { + return switch (self.getOs()) { + .ios, .macosx, .watchos, .tvos => true, + else => false, + }; + } + + pub fn isWindows(self: Target) bool { + return switch (self.getOs()) { + .windows => true, + else => false, + }; + } + + pub fn isLinux(self: Target) bool { + return switch (self.getOs()) { + .linux => true, + else => false, + }; + } + + pub fn isUefi(self: Target) bool { + return switch (self.getOs()) { + .uefi => true, + else => false, + }; + } + + pub fn isWasm(self: Target) bool { + return switch (self.getArch()) { + .wasm32, .wasm64 => true, + else => false, + }; + } + + pub fn isFreeBSD(self: Target) bool { + return switch (self.getOs()) { + .freebsd => true, + else => false, + }; + } + + pub fn isNetBSD(self: Target) bool { + return switch (self.getOs()) { + .netbsd => true, + else => false, + }; + } + + pub fn wantSharedLibSymLinks(self: Target) bool { + return !self.isWindows(); + } + + pub fn osRequiresLibC(self: Target) bool { + return self.isDarwin() or self.isFreeBSD() or self.isNetBSD(); + } + + pub fn getArchPtrBitWidth(self: Target) u32 { + switch (self.getArch()) { + .avr, + .msp430, + => return 16, + + .arc, + .arm, + .armeb, + .hexagon, + .le32, + .mips, + .mipsel, + .powerpc, + .r600, + .riscv32, + .sparc, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .i386, + .xcore, + .nvptx, + .amdil, + .hsail, + .spir, + .kalimba, + .shave, + .lanai, + .wasm32, + .renderscript32, + .aarch64_32, + => return 32, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .x86_64, + .nvptx64, + .le64, + .amdil64, + .hsail64, + .spir64, + .wasm64, + .renderscript64, + .amdgcn, + .bpfel, + .bpfeb, + .sparcv9, + .s390x, + => return 64, + } + } + + pub fn supportsNewStackCall(self: Target) bool { + return !self.isWasm(); + } + + pub const Executor = union(enum) { + native, + qemu: []const u8, + wine: []const u8, + wasmtime: []const u8, + unavailable, + }; + + pub fn getExternalExecutor(self: Target) Executor { + if (@as(@TagType(Target), self) == .Native) return .native; + + // If the target OS matches the host OS, we can use QEMU to emulate a foreign architecture. + if (self.getOs() == builtin.os) { + return switch (self.getArch()) { + .aarch64 => Executor{ .qemu = "qemu-aarch64" }, + .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, + .arm => Executor{ .qemu = "qemu-arm" }, + .armeb => Executor{ .qemu = "qemu-armeb" }, + .i386 => Executor{ .qemu = "qemu-i386" }, + .mips => Executor{ .qemu = "qemu-mips" }, + .mipsel => Executor{ .qemu = "qemu-mipsel" }, + .mips64 => Executor{ .qemu = "qemu-mips64" }, + .mips64el => Executor{ .qemu = "qemu-mips64el" }, + .powerpc => Executor{ .qemu = "qemu-ppc" }, + .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, + .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, + .riscv32 => Executor{ .qemu = "qemu-riscv32" }, + .riscv64 => Executor{ .qemu = "qemu-riscv64" }, + .s390x => Executor{ .qemu = "qemu-s390x" }, + .sparc => Executor{ .qemu = "qemu-sparc" }, + .x86_64 => Executor{ .qemu = "qemu-x86_64" }, + else => return .unavailable, + }; + } + + if (self.isWindows()) { + switch (self.getArchPtrBitWidth()) { + 32 => return Executor{ .wine = "wine" }, + 64 => return Executor{ .wine = "wine64" }, + else => return .unavailable, + } + } + + if (self.getOs() == .wasi) { + switch (self.getArchPtrBitWidth()) { + 32 => return Executor{ .wasmtime = "wasmtime" }, + else => return .unavailable, + } + } + + return .unavailable; + } +}; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 7f347b0c2..7a261e075 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -6,15 +6,13 @@ const std = @import("std.zig"); /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: var) void { if (actual_error_union) |actual_payload| { - // TODO remove workaround here for https://github.com/ziglang/zig/issues/557 - if (@sizeOf(@typeOf(actual_payload)) == 0) { - std.debug.panic("expected error.{}, found {} value", @errorName(expected_error), @typeName(@typeOf(actual_payload))); - } else { - std.debug.panic("expected error.{}, found {}", @errorName(expected_error), actual_payload); - } + std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload }); } else |actual_error| { if (expected_error != actual_error) { - std.debug.panic("expected error.{}, found error.{}", @errorName(expected_error), @errorName(actual_error)); + std.debug.panic("expected error.{}, found error.{}", .{ + @errorName(expected_error), + @errorName(actual_error), + }); } } } @@ -23,15 +21,14 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void { /// equal, prints diagnostics to stderr to show exactly how they are not equal, /// then aborts. /// The types must match exactly. -pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { - switch (@typeInfo(@typeOf(actual))) { +pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void { + switch (@typeInfo(@TypeOf(actual))) { .NoReturn, .BoundFn, - .ArgTuple, .Opaque, .Frame, .AnyFrame, - => @compileError("value of type " ++ @typeName(@typeOf(actual)) ++ " encountered"), + => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"), .Undefined, .Null, @@ -51,7 +48,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { .ErrorSet, => { if (actual != expected) { - std.debug.panic("expected {}, found {}", expected, actual); + std.debug.panic("expected {}, found {}", .{ expected, actual }); } }, @@ -62,16 +59,16 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { builtin.TypeInfo.Pointer.Size.C, => { if (actual != expected) { - std.debug.panic("expected {}, found {}", expected, actual); + std.debug.panic("expected {*}, found {*}", .{ expected, actual }); } }, builtin.TypeInfo.Pointer.Size.Slice => { if (actual.ptr != expected.ptr) { - std.debug.panic("expected slice ptr {}, found {}", expected.ptr, actual.ptr); + std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); } if (actual.len != expected.len) { - std.debug.panic("expected slice len {}, found {}", expected.len, actual.len); + std.debug.panic("expected slice len {}, found {}", .{ expected.len, actual.len }); } }, } @@ -89,7 +86,26 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { if (union_info.tag_type == null) { @compileError("Unable to compare untagged union values"); } - @compileError("TODO implement testing.expectEqual for tagged unions"); + + const TagType = @TagType(@TypeOf(expected)); + + const expectedTag = @as(TagType, expected); + const actualTag = @as(TagType, actual); + + expectEqual(expectedTag, actualTag); + + // we only reach this loop if the tags are equal + inline for (std.meta.fields(@TypeOf(actual))) |fld| { + if (std.mem.eql(u8, fld.name, @tagName(actualTag))) { + expectEqual(@field(expected, fld.name), @field(actual, fld.name)); + return; + } + } + + // we iterate over *all* union fields + // => we should never get here as the loop above is + // including all possible values. + unreachable; }, .Optional => { @@ -97,11 +113,11 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else { - std.debug.panic("expected {}, found null", expected_payload); + std.debug.panic("expected {}, found null", .{expected_payload}); } } else { if (actual) |actual_payload| { - std.debug.panic("expected null, found {}", actual_payload); + std.debug.panic("expected null, found {}", .{actual_payload}); } } }, @@ -111,11 +127,11 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else |actual_err| { - std.debug.panic("expected {}, found {}", expected_payload, actual_err); + std.debug.panic("expected {}, found {}", .{ expected_payload, actual_err }); } } else |expected_err| { if (actual) |actual_payload| { - std.debug.panic("expected {}, found {}", expected_err, actual_payload); + std.debug.panic("expected {}, found {}", .{ expected_err, actual_payload }); } else |actual_err| { expectEqual(expected_err, actual_err); } @@ -124,6 +140,18 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { } } +test "expectEqual.union(enum)" { + const T = union(enum) { + a: i32, + b: f32, + }; + + const a10 = T{ .a = 10 }; + const a20 = T{ .a = 20 }; + + expectEqual(a10, a10); +} + /// This function is intended to be used only in tests. When the two slices are not /// equal, prints diagnostics to stderr to show exactly how they are not equal, /// then aborts. @@ -133,12 +161,12 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const // If the child type is u8 and no weird bytes, we could print it as strings // Even for the length difference, it would be useful to see the values of the slices probably. if (expected.len != actual.len) { - std.debug.panic("slice lengths differ. expected {}, found {}", expected.len, actual.len); + std.debug.panic("slice lengths differ. expected {}, found {}", .{ expected.len, actual.len }); } var i: usize = 0; while (i < expected.len) : (i += 1) { - if (expected[i] != actual[i]) { - std.debug.panic("index {} incorrect. expected {}, found {}", i, expected[i], actual[i]); + if (!std.meta.eql(expected[i], actual[i])) { + std.debug.panic("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] }); } } } @@ -148,3 +176,17 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const pub fn expect(ok: bool) void { if (!ok) @panic("test failure"); } + +test "expectEqual nested array" { + const a = [2][2]f32{ + [_]f32{ 1.0, 0.0 }, + [_]f32{ 0.0, 1.0 }, + }; + + const b = [2][2]f32{ + [_]f32{ 1.0, 0.0 }, + [_]f32{ 0.0, 1.0 }, + }; + + expectEqual(a, b); +} diff --git a/lib/std/thread.zig b/lib/std/thread.zig index 278fcc827..c482405c4 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -9,7 +9,7 @@ const assert = std.debug.assert; pub const Thread = struct { data: Data, - pub const use_pthreads = !windows.is_the_target and builtin.link_libc; + pub const use_pthreads = builtin.os != .windows and builtin.link_libc; /// Represents a kernel thread handle. /// May be an integer or a pointer depending on the platform. @@ -19,7 +19,7 @@ pub const Thread = struct { else switch (builtin.os) { .linux => i32, .windows => windows.HANDLE, - else => @compileError("Unsupported OS"), + else => void, }; /// Represents a unique ID per thread. @@ -45,7 +45,7 @@ pub const Thread = struct { alloc_start: *c_void, heap_handle: windows.HANDLE, }, - else => @compileError("Unsupported OS"), + else => struct {}, }; /// Returns the ID of the calling thread. @@ -99,7 +99,7 @@ pub const Thread = struct { os.munmap(self.data.memory); }, .windows => { - windows.WaitForSingleObject(self.data.handle, windows.INFINITE) catch unreachable; + windows.WaitForSingleObjectEx(self.data.handle, windows.INFINITE, false) catch unreachable; windows.CloseHandle(self.data.handle); windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start); }, @@ -138,7 +138,7 @@ pub const Thread = struct { }; /// caller must call wait on the returned thread - /// fn startFn(@typeOf(context)) T + /// fn startFn(@TypeOf(context)) T /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread pub fn spawn(context: var, comptime startFn: var) SpawnError!*Thread { @@ -147,8 +147,8 @@ pub const Thread = struct { // https://github.com/ziglang/zig/issues/157 const default_stack_size = 16 * 1024 * 1024; - const Context = @typeOf(context); - comptime assert(@ArgType(@typeOf(startFn), 0) == Context); + const Context = @TypeOf(context); + comptime assert(@ArgType(@TypeOf(startFn), 0) == Context); if (builtin.os == builtin.Os.windows) { const WinThread = struct { @@ -156,9 +156,9 @@ pub const Thread = struct { thread: Thread, inner: Context, }; - extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { + fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD { const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; - switch (@typeId(@typeOf(startFn).ReturnType)) { + switch (@typeId(@TypeOf(startFn).ReturnType)) { .Int => { return startFn(arg); }, @@ -198,10 +198,10 @@ pub const Thread = struct { } const MainFuncs = struct { - extern fn linuxThreadMain(ctx_addr: usize) u8 { + fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 { const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; - switch (@typeId(@typeOf(startFn).ReturnType)) { + switch (@typeId(@TypeOf(startFn).ReturnType)) { .Int => { return startFn(arg); }, @@ -212,7 +212,7 @@ pub const Thread = struct { else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), } } - extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { + fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void { if (@sizeOf(Context) == 0) { _ = startFn({}); return null; @@ -309,16 +309,43 @@ pub const Thread = struct { os.EINVAL => unreachable, else => return os.unexpectedErrno(@intCast(usize, err)), } - } else if (os.linux.is_the_target) { + } else if (builtin.os == .linux) { var flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_DETACHED; var newtls: usize = undefined; + // This structure is only needed when targeting i386 + var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined; + if (os.linux.tls.tls_image) |tls_img| { - newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset); + if (builtin.arch == .i386) { + user_desc = os.linux.user_desc{ + .entry_number = tls_img.gdt_entry_number, + .base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset), + .limit = 0xfffff, + .seg_32bit = 1, + .contents = 0, // Data + .read_exec_only = 0, + .limit_in_pages = 1, + .seg_not_present = 0, + .useable = 1, + }; + newtls = @ptrToInt(&user_desc); + } else { + newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset); + } flags |= os.CLONE_SETTLS; } - const rc = os.linux.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); + + const rc = os.linux.clone( + MainFuncs.linuxThreadMain, + mmap_addr + stack_end_offset, + flags, + arg, + &thread_ptr.data.handle, + newtls, + &thread_ptr.data.handle, + ); switch (os.errno(rc)) { 0 => return thread_ptr, os.EAGAIN => return error.ThreadQuotaExceeded, @@ -342,18 +369,18 @@ pub const Thread = struct { }; pub fn cpuCount() CpuCountError!usize { - if (os.linux.is_the_target) { + if (builtin.os == .linux) { const cpu_set = try os.sched_getaffinity(0); - return usize(os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast + return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast } - if (os.windows.is_the_target) { + if (builtin.os == .windows) { var system_info: windows.SYSTEM_INFO = undefined; windows.kernel32.GetSystemInfo(&system_info); return @intCast(usize, system_info.dwNumberOfProcessors); } var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); - const name = if (os.darwin.is_the_target) c"hw.logicalcpu" else c"hw.ncpu"; + const name = if (comptime std.Target.current.isDarwin()) "hw.logicalcpu" else "hw.ncpu"; os.sysctlbynameC(name, &count, &count_len, null, 0) catch |err| switch (err) { error.NameTooLong => unreachable, else => |e| return e, diff --git a/lib/std/time.zig b/lib/std/time.zig index caf6c31b3..716c99957 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -9,7 +9,7 @@ pub const epoch = @import("time/epoch.zig"); /// Spurious wakeups are possible and no precision of timing is guaranteed. pub fn sleep(nanoseconds: u64) void { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { const ns_per_ms = ns_per_s / ms_per_s; const big_ms_from_ns = nanoseconds / ns_per_ms; const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD); @@ -30,7 +30,7 @@ pub fn timestamp() u64 { /// Get the posix timestamp, UTC, in milliseconds /// TODO audit this function. is it possible to return an error? pub fn milliTimestamp() u64 { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { //FileTime has a granularity of 100 nanoseconds // and uses the NTFS/Windows epoch var ft: os.windows.FILETIME = undefined; @@ -38,10 +38,10 @@ pub fn milliTimestamp() u64 { const hns_per_ms = (ns_per_s / 100) / ms_per_s; const epoch_adj = epoch.windows * ms_per_s; - const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; return @divFloor(ft64, hns_per_ms) - -epoch_adj; } - if (os.wasi.is_the_target and !builtin.link_libc) { + if (builtin.os == .wasi and !builtin.link_libc) { var ns: os.wasi.timestamp_t = undefined; // TODO: Verify that precision is ignored @@ -51,7 +51,7 @@ pub fn milliTimestamp() u64 { const ns_per_ms = 1000; return @divFloor(ns, ns_per_ms); } - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { var tv: os.darwin.timeval = undefined; var err = os.darwin.gettimeofday(&tv, null); assert(err == 0); @@ -126,11 +126,11 @@ pub const Timer = struct { pub fn start() Error!Timer { var self: Timer = undefined; - if (os.windows.is_the_target) { + if (builtin.os == .windows) { self.frequency = os.windows.QueryPerformanceFrequency(); self.resolution = @divFloor(ns_per_s, self.frequency); self.start_time = os.windows.QueryPerformanceCounter(); - } else if (os.darwin.is_the_target) { + } else if (comptime std.Target.current.isDarwin()) { os.darwin.mach_timebase_info(&self.frequency); self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); self.start_time = os.darwin.mach_absolute_time(); @@ -142,10 +142,10 @@ pub const Timer = struct { // seccomp is going to block us it will at least do so consistently var ts: os.timespec = undefined; os.clock_getres(monotonic_clock_id, &ts) catch return error.TimerUnsupported; - self.resolution = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + self.resolution = @intCast(u64, ts.tv_sec) * @as(u64, ns_per_s) + @intCast(u64, ts.tv_nsec); os.clock_gettime(monotonic_clock_id, &ts) catch return error.TimerUnsupported; - self.start_time = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + self.start_time = @intCast(u64, ts.tv_sec) * @as(u64, ns_per_s) + @intCast(u64, ts.tv_nsec); } return self; @@ -154,10 +154,10 @@ pub const Timer = struct { /// Reads the timer value since start or the last reset in nanoseconds pub fn read(self: *Timer) u64 { var clock = clockNative() - self.start_time; - if (os.windows.is_the_target) { + if (builtin.os == .windows) { return @divFloor(clock * ns_per_s, self.frequency); } - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { return @divFloor(clock * self.frequency.numer, self.frequency.denom); } return clock; @@ -177,15 +177,15 @@ pub const Timer = struct { } fn clockNative() u64 { - if (os.windows.is_the_target) { + if (builtin.os == .windows) { return os.windows.QueryPerformanceCounter(); } - if (os.darwin.is_the_target) { + if (comptime std.Target.current.isDarwin()) { return os.darwin.mach_absolute_time(); } var ts: os.timespec = undefined; os.clock_gettime(monotonic_clock_id, &ts) catch unreachable; - return @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); + return @intCast(u64, ts.tv_sec) * @as(u64, ns_per_s) + @intCast(u64, ts.tv_nsec); } }; diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 2e9614716..3d1674844 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -6,11 +6,11 @@ const mem = std.mem; /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. -pub fn utf8CodepointSequenceLength(c: u32) !u3 { - if (c < 0x80) return u3(1); - if (c < 0x800) return u3(2); - if (c < 0x10000) return u3(3); - if (c < 0x110000) return u3(4); +pub fn utf8CodepointSequenceLength(c: u21) !u3 { + if (c < 0x80) return @as(u3, 1); + if (c < 0x800) return @as(u3, 2); + if (c < 0x10000) return @as(u3, 3); + if (c < 0x110000) return @as(u3, 4); return error.CodepointTooLarge; } @@ -18,11 +18,13 @@ pub fn utf8CodepointSequenceLength(c: u32) !u3 { /// returns a number 1-4 indicating the total length of the codepoint in bytes. /// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte. pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { - if (first_byte < 0b10000000) return u3(1); - if (first_byte & 0b11100000 == 0b11000000) return u3(2); - if (first_byte & 0b11110000 == 0b11100000) return u3(3); - if (first_byte & 0b11111000 == 0b11110000) return u3(4); - return error.Utf8InvalidStartByte; + return switch (@clz(u8, ~first_byte)) { + 0 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + else => error.Utf8InvalidStartByte, + }; } /// Encodes the given codepoint into a UTF-8 byte sequence. @@ -30,7 +32,7 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { /// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c). /// Errors: if c cannot be encoded in UTF-8. /// Returns: the number of bytes written to out. -pub fn utf8Encode(c: u32, out: []u8) !u3 { +pub fn utf8Encode(c: u21, out: []u8) !u3 { const length = try utf8CodepointSequenceLength(c); assert(out.len >= length); switch (length) { @@ -66,9 +68,9 @@ const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. /// If you already know the length at comptime, you can call one of /// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function. -pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 { +pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u21 { return switch (bytes.len) { - 1 => u32(bytes[0]), + 1 => @as(u21, bytes[0]), 2 => utf8Decode2(bytes), 3 => utf8Decode3(bytes), 4 => utf8Decode4(bytes), @@ -80,10 +82,10 @@ const Utf8Decode2Error = error{ Utf8ExpectedContinuation, Utf8OverlongEncoding, }; -pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 { +pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u21 { assert(bytes.len == 2); assert(bytes[0] & 0b11100000 == 0b11000000); - var value: u32 = bytes[0] & 0b00011111; + var value: u21 = bytes[0] & 0b00011111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; value <<= 6; @@ -99,10 +101,10 @@ const Utf8Decode3Error = error{ Utf8OverlongEncoding, Utf8EncodesSurrogateHalf, }; -pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 { +pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u21 { assert(bytes.len == 3); assert(bytes[0] & 0b11110000 == 0b11100000); - var value: u32 = bytes[0] & 0b00001111; + var value: u21 = bytes[0] & 0b00001111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; value <<= 6; @@ -123,10 +125,10 @@ const Utf8Decode4Error = error{ Utf8OverlongEncoding, Utf8CodepointTooLarge, }; -pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 { +pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u21 { assert(bytes.len == 4); assert(bytes[0] & 0b11111000 == 0b11110000); - var value: u32 = bytes[0] & 0b00000111; + var value: u21 = bytes[0] & 0b00000111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; value <<= 6; @@ -170,7 +172,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool { /// ``` /// var utf8 = (try std.unicode.Utf8View.init("hi there")).iterator(); /// while (utf8.nextCodepointSlice()) |codepoint| { -/// std.debug.warn("got codepoint {}\n", codepoint); +/// std.debug.warn("got codepoint {}\n", .{codepoint}); /// } /// ``` pub const Utf8View = struct { @@ -222,11 +224,11 @@ pub const Utf8Iterator = struct { return it.bytes[it.i - cp_len .. it.i]; } - pub fn nextCodepoint(it: *Utf8Iterator) ?u32 { + pub fn nextCodepoint(it: *Utf8Iterator) ?u21 { const slice = it.nextCodepointSlice() orelse return null; switch (slice.len) { - 1 => return u32(slice[0]), + 1 => return @as(u21, slice[0]), 2 => return utf8Decode2(slice) catch unreachable, 3 => return utf8Decode3(slice) catch unreachable, 4 => return utf8Decode4(slice) catch unreachable, @@ -246,19 +248,19 @@ pub const Utf16LeIterator = struct { }; } - pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 { + pub fn nextCodepoint(it: *Utf16LeIterator) !?u21 { assert(it.i <= it.bytes.len); if (it.i == it.bytes.len) return null; - const c0: u32 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); - if (c0 & ~u32(0x03ff) == 0xd800) { + const c0: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + if (c0 & ~@as(u21, 0x03ff) == 0xd800) { // surrogate pair it.i += 2; if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf; - const c1: u32 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); - if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; + const c1: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; it.i += 2; return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); - } else if (c0 & ~u32(0x03ff) == 0xdc00) { + } else if (c0 & ~@as(u21, 0x03ff) == 0xdc00) { return error.UnexpectedSecondSurrogateHalf; } else { it.i += 2; @@ -302,10 +304,10 @@ fn testUtf8EncodeError() void { testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf); testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf); testErrorEncode(0x110000, array[0..], error.CodepointTooLarge); - testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge); + testErrorEncode(0x1fffff, array[0..], error.CodepointTooLarge); } -fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: anyerror) void { +fn testErrorEncode(codePoint: u21, array: []u8, expectedErr: anyerror) void { testing.expectError(expectedErr, utf8Encode(codePoint, array)); } @@ -453,11 +455,11 @@ fn testError(bytes: []const u8, expected_err: anyerror) void { testing.expectError(expected_err, testDecode(bytes)); } -fn testValid(bytes: []const u8, expected_codepoint: u32) void { +fn testValid(bytes: []const u8, expected_codepoint: u21) void { testing.expect((testDecode(bytes) catch unreachable) == expected_codepoint); } -fn testDecode(bytes: []const u8) !u32 { +fn testDecode(bytes: []const u8) !u21 { const length = try utf8ByteSequenceLength(bytes[0]); if (bytes.len < length) return error.UnexpectedEof; testing.expect(bytes.len == length); @@ -499,14 +501,14 @@ test "utf16leToUtf8" { { mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 'A'); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 'a'); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "Aa")); } { mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0x80); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xffff); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); } @@ -514,7 +516,7 @@ test "utf16leToUtf8" { // the values just outside the surrogate half range mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0xd7ff); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xe000); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); } @@ -522,7 +524,7 @@ test "utf16leToUtf8" { // smallest surrogate pair mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0xd800); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xdc00); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); } @@ -530,21 +532,19 @@ test "utf16leToUtf8" { // largest surrogate pair mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0xdbff); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xdfff); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); } { mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0xdbff); mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xdc00); - const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le); + const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, &utf16le); testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); } } -/// TODO support codepoints bigger than 16 bits -/// TODO type for null terminated pointer -pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 { +pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![:0]u16 { var result = std.ArrayList(u16).init(allocator); // optimistically guess that it will not require surrogate pairs try result.ensureCapacity(utf8.len + 1); @@ -552,42 +552,73 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 const view = try Utf8View.init(utf8); var it = view.iterator(); while (it.nextCodepoint()) |codepoint| { - try result.append(@intCast(u16, codepoint)); // TODO surrogate pairs + if (codepoint < 0x10000) { + const short = @intCast(u16, codepoint); + try result.append(mem.nativeToLittle(u16, short)); + } else { + const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; + const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; + var out: [2]u16 = undefined; + out[0] = mem.nativeToLittle(u16, high); + out[1] = mem.nativeToLittle(u16, low); + try result.appendSlice(out[0..]); + } } try result.append(0); - return result.toOwnedSlice(); + return result.toOwnedSlice()[0..:0]; } /// Returns index of next character. If exact fit, returned index equals output slice length. /// Assumes there is enough space for the output. -/// TODO support codepoints bigger than 16 bits pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { var dest_i: usize = 0; var src_i: usize = 0; while (src_i < utf8.len) { - const byte = utf8[src_i]; - const n = @clz(u8, ~byte); - switch (n) { - 0 => { - utf16le[dest_i] = byte; - dest_i += 1; - src_i += 1; - continue; - }, - 2, 3, 4 => { - const next_src_i = src_i + n; - const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; - const short = @intCast(u16, codepoint); // TODO surrogate pairs - utf16le[dest_i] = switch (builtin.endian) { - .Little => short, - .Big => @byteSwap(u16, short), - }; - dest_i += 1; - src_i = next_src_i; - }, - else => return error.InvalidUtf8, + const n = utf8ByteSequenceLength(utf8[src_i]) catch return error.InvalidUtf8; + const next_src_i = src_i + n; + const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; + if (codepoint < 0x10000) { + const short = @intCast(u16, codepoint); + utf16le[dest_i] = mem.nativeToLittle(u16, short); + dest_i += 1; + } else { + const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; + const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; + utf16le[dest_i] = mem.nativeToLittle(u16, high); + utf16le[dest_i + 1] = mem.nativeToLittle(u16, low); + dest_i += 2; } + src_i = next_src_i; } return dest_i; } + +test "utf8ToUtf16Le" { + var utf16le: [2]u16 = [_]u16{0} ** 2; + { + const length = try utf8ToUtf16Le(utf16le[0..], "๐ท"); + testing.expectEqual(@as(usize, 2), length); + testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", @sliceToBytes(utf16le[0..])); + } + { + const length = try utf8ToUtf16Le(utf16le[0..], "\u{10FFFF}"); + testing.expectEqual(@as(usize, 2), length); + testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", @sliceToBytes(utf16le[0..])); + } +} + +test "utf8ToUtf16LeWithNull" { + { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + const utf16 = try utf8ToUtf16LeWithNull(allocator, "๐ท"); + testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc\x00\x00", @sliceToBytes(utf16[0..])); + } + { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + const utf16 = try utf8ToUtf16LeWithNull(allocator, "\u{10FFFF}"); + testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf\x00\x00", @sliceToBytes(utf16[0..])); + } +} diff --git a/lib/std/unicode/throughput_test.zig b/lib/std/unicode/throughput_test.zig index f8b18af73..7265361b3 100644 --- a/lib/std/unicode/throughput_test.zig +++ b/lib/std/unicode/throughput_test.zig @@ -2,32 +2,39 @@ const builtin = @import("builtin"); const std = @import("std"); pub fn main() !void { - var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = stdout_file.outStream(); - const stdout = &stdout_out_stream.stream; + const stdout = &std.io.getStdOut().outStream().stream; - const args = try std.process.argsAlloc(std.heap.direct_allocator); + const args = try std.process.argsAlloc(std.heap.page_allocator); + + // Warm up runs + var buffer0: [32767]u16 align(4096) = undefined; + _ = try std.unicode.utf8ToUtf16Le(&buffer0, args[1]); + _ = try std.unicode.utf8ToUtf16Le_better(&buffer0, args[1]); @fence(.SeqCst); var timer = try std.time.Timer.start(); @fence(.SeqCst); - var buffer1: [32767]u16 = undefined; + var buffer1: [32767]u16 align(4096) = undefined; _ = try std.unicode.utf8ToUtf16Le(&buffer1, args[1]); @fence(.SeqCst); const elapsed_ns_orig = timer.lap(); @fence(.SeqCst); - var buffer2: [32767]u16 = undefined; + var buffer2: [32767]u16 align(4096) = undefined; _ = try std.unicode.utf8ToUtf16Le_better(&buffer2, args[1]); @fence(.SeqCst); const elapsed_ns_better = timer.lap(); @fence(.SeqCst); - std.debug.warn("original utf8ToUtf16Le: elapsed: {} ns ({} ms)\n", elapsed_ns_orig, elapsed_ns_orig / 1000000); - std.debug.warn("new utf8ToUtf16Le: elapsed: {} ns ({} ms)\n", elapsed_ns_better, elapsed_ns_better / 1000000); + std.debug.warn("original utf8ToUtf16Le: elapsed: {} ns ({} ms)\n", .{ + elapsed_ns_orig, elapsed_ns_orig / 1000000, + }); + std.debug.warn("new utf8ToUtf16Le: elapsed: {} ns ({} ms)\n", .{ + elapsed_ns_better, elapsed_ns_better / 1000000, + }); asm volatile ("nop" : : [a] "r" (&buffer1), diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig index 0d7f79dfa..fe1eb65b7 100644 --- a/lib/std/valgrind.zig +++ b/lib/std/valgrind.zig @@ -1,5 +1,6 @@ const builtin = @import("builtin"); -const math = @import("index.zig").math; +const std = @import("std.zig"); +const math = std.math; pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { if (!builtin.valgrind_support) { @@ -13,7 +14,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: \\ roll $29, %%edi ; roll $19, %%edi \\ xchgl %%ebx,%%ebx : [_] "={edx}" (-> usize) - : [_] "{eax}" (&[]usize{ request, a1, a2, a3, a4, a5 }), + : [_] "{eax}" (&[_]usize{ request, a1, a2, a3, a4, a5 }), [_] "0" (default) : "cc", "memory" ); @@ -24,7 +25,7 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: \\ rolq $61, %%rdi ; rolq $51, %%rdi \\ xchgq %%rbx,%%rbx : [_] "={rdx}" (-> usize) - : [_] "{rax}" (&[]usize{ request, a1, a2, a3, a4, a5 }), + : [_] "{rax}" (&[_]usize{ request, a1, a2, a3, a4, a5 }), [_] "0" (default) : "cc", "memory" ); @@ -76,7 +77,7 @@ pub const ClientRequest = extern enum { InnerThreads = 6402, }; pub fn ToolBase(base: [2]u8) u32 { - return (u32(base[0] & 0xff) << 24) | (u32(base[1] & 0xff) << 16); + return (@as(u32, base[0] & 0xff) << 24) | (@as(u32, base[1] & 0xff) << 16); } pub fn IsTool(base: [2]u8, code: usize) bool { return ToolBase(base) == (code & 0xffff0000); @@ -95,48 +96,38 @@ fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, /// running under Valgrind which is running under another Valgrind, /// etc. pub fn runningOnValgrind() usize { - return doClientRequestExpr(0, ClientRequest.RunningOnValgrind, 0, 0, 0, 0, 0); + return doClientRequestExpr(0, .RunningOnValgrind, 0, 0, 0, 0, 0); +} + +test "works whether running on valgrind or not" { + _ = runningOnValgrind(); } /// Discard translation of code in the slice qzz. Useful if you are debugging /// a JITter or some such, since it provides a way to make sure valgrind will /// retranslate the invalidated area. Returns no value. pub fn discardTranslations(qzz: []const u8) void { - doClientRequestStmt(ClientRequest.DiscardTranslations, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); + doClientRequestStmt(.DiscardTranslations, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); } pub fn innerThreads(qzz: [*]u8) void { - doClientRequestStmt(ClientRequest.InnerThreads, qzz, 0, 0, 0, 0); + doClientRequestStmt(.InnerThreads, qzz, 0, 0, 0, 0); } -//pub fn printf(format: [*]const u8, args: ...) usize { -// return doClientRequestExpr(0, -// ClientRequest.PrintfValistByRef, -// @ptrToInt(format), @ptrToInt(args), -// 0, 0, 0); -//} - -//pub fn printfBacktrace(format: [*]const u8, args: ...) usize { -// return doClientRequestExpr(0, -// ClientRequest.PrintfBacktraceValistByRef, -// @ptrToInt(format), @ptrToInt(args), -// 0, 0, 0); -//} - pub fn nonSIMDCall0(func: fn (usize) usize) usize { - return doClientRequestExpr(0, ClientRequest.ClientCall0, @ptrToInt(func), 0, 0, 0, 0); + return doClientRequestExpr(0, .ClientCall0, @ptrToInt(func), 0, 0, 0, 0); } pub fn nonSIMDCall1(func: fn (usize, usize) usize, a1: usize) usize { - return doClientRequestExpr(0, ClientRequest.ClientCall1, @ptrToInt(func), a1, 0, 0, 0); + return doClientRequestExpr(0, .ClientCall1, @ptrToInt(func), a1, 0, 0, 0); } pub fn nonSIMDCall2(func: fn (usize, usize, usize) usize, a1: usize, a2: usize) usize { - return doClientRequestExpr(0, ClientRequest.ClientCall2, @ptrToInt(func), a1, a2, 0, 0); + return doClientRequestExpr(0, .ClientCall2, @ptrToInt(func), a1, a2, 0, 0); } pub fn nonSIMDCall3(func: fn (usize, usize, usize, usize) usize, a1: usize, a2: usize, a3: usize) usize { - return doClientRequestExpr(0, ClientRequest.ClientCall3, @ptrToInt(func), a1, a2, a3, 0); + return doClientRequestExpr(0, .ClientCall3, @ptrToInt(func), a1, a2, a3, 0); } /// Counts the number of errors that have been recorded by a tool. Nb: @@ -144,19 +135,19 @@ pub fn nonSIMDCall3(func: fn (usize, usize, usize, usize) usize, a1: usize, a2: /// VG_(unique_error)() for them to be counted. pub fn countErrors() usize { return doClientRequestExpr(0, // default return - ClientRequest.CountErrors, 0, 0, 0, 0, 0); + .CountErrors, 0, 0, 0, 0, 0); } pub fn mallocLikeBlock(mem: []u8, rzB: usize, is_zeroed: bool) void { - doClientRequestStmt(ClientRequest.MalloclikeBlock, @ptrToInt(mem.ptr), mem.len, rzB, @boolToInt(is_zeroed), 0); + doClientRequestStmt(.MalloclikeBlock, @ptrToInt(mem.ptr), mem.len, rzB, @boolToInt(is_zeroed), 0); } pub fn resizeInPlaceBlock(oldmem: []u8, newsize: usize, rzB: usize) void { - doClientRequestStmt(ClientRequest.ResizeinplaceBlock, @ptrToInt(oldmem.ptr), oldmem.len, newsize, rzB, 0); + doClientRequestStmt(.ResizeinplaceBlock, @ptrToInt(oldmem.ptr), oldmem.len, newsize, rzB, 0); } pub fn freeLikeBlock(addr: [*]u8, rzB: usize) void { - doClientRequestStmt(ClientRequest.FreelikeBlock, @ptrToInt(addr), rzB, 0, 0, 0); + doClientRequestStmt(.FreelikeBlock, @ptrToInt(addr), rzB, 0, 0, 0); } /// Create a memory pool. @@ -165,66 +156,66 @@ pub const MempoolFlags = extern enum { MetaPool = 2, }; pub fn createMempool(pool: [*]u8, rzB: usize, is_zeroed: bool, flags: usize) void { - doClientRequestStmt(ClientRequest.CreateMempool, @ptrToInt(pool), rzB, @boolToInt(is_zeroed), flags, 0); + doClientRequestStmt(.CreateMempool, @ptrToInt(pool), rzB, @boolToInt(is_zeroed), flags, 0); } /// Destroy a memory pool. pub fn destroyMempool(pool: [*]u8) void { - doClientRequestStmt(ClientRequest.DestroyMempool, pool, 0, 0, 0, 0); + doClientRequestStmt(.DestroyMempool, pool, 0, 0, 0, 0); } /// Associate a piece of memory with a memory pool. pub fn mempoolAlloc(pool: [*]u8, mem: []u8) void { - doClientRequestStmt(ClientRequest.MempoolAlloc, @ptrToInt(pool), @ptrToInt(mem.ptr), mem.len, 0, 0); + doClientRequestStmt(.MempoolAlloc, @ptrToInt(pool), @ptrToInt(mem.ptr), mem.len, 0, 0); } /// Disassociate a piece of memory from a memory pool. pub fn mempoolFree(pool: [*]u8, addr: [*]u8) void { - doClientRequestStmt(ClientRequest.MempoolFree, @ptrToInt(pool), @ptrToInt(addr), 0, 0, 0); + doClientRequestStmt(.MempoolFree, @ptrToInt(pool), @ptrToInt(addr), 0, 0, 0); } /// Disassociate any pieces outside a particular range. pub fn mempoolTrim(pool: [*]u8, mem: []u8) void { - doClientRequestStmt(ClientRequest.MempoolTrim, @ptrToInt(pool), @ptrToInt(mem.ptr), mem.len, 0, 0); + doClientRequestStmt(.MempoolTrim, @ptrToInt(pool), @ptrToInt(mem.ptr), mem.len, 0, 0); } /// Resize and/or move a piece associated with a memory pool. pub fn moveMempool(poolA: [*]u8, poolB: [*]u8) void { - doClientRequestStmt(ClientRequest.MoveMempool, @ptrToInt(poolA), @ptrToInt(poolB), 0, 0, 0); + doClientRequestStmt(.MoveMempool, @ptrToInt(poolA), @ptrToInt(poolB), 0, 0, 0); } /// Resize and/or move a piece associated with a memory pool. pub fn mempoolChange(pool: [*]u8, addrA: [*]u8, mem: []u8) void { - doClientRequestStmt(ClientRequest.MempoolChange, @ptrToInt(pool), @ptrToInt(addrA), @ptrToInt(mem.ptr), mem.len, 0); + doClientRequestStmt(.MempoolChange, @ptrToInt(pool), @ptrToInt(addrA), @ptrToInt(mem.ptr), mem.len, 0); } /// Return if a mempool exists. pub fn mempoolExists(pool: [*]u8) bool { - return doClientRequestExpr(0, ClientRequest.MempoolExists, @ptrToInt(pool), 0, 0, 0, 0) != 0; + return doClientRequestExpr(0, .MempoolExists, @ptrToInt(pool), 0, 0, 0, 0) != 0; } /// Mark a piece of memory as being a stack. Returns a stack id. /// start is the lowest addressable stack byte, end is the highest /// addressable stack byte. pub fn stackRegister(stack: []u8) usize { - return doClientRequestExpr(0, ClientRequest.StackRegister, @ptrToInt(stack.ptr), @ptrToInt(stack.ptr) + stack.len, 0, 0, 0); + return doClientRequestExpr(0, .StackRegister, @ptrToInt(stack.ptr), @ptrToInt(stack.ptr) + stack.len, 0, 0, 0); } /// Unmark the piece of memory associated with a stack id as being a stack. pub fn stackDeregister(id: usize) void { - doClientRequestStmt(ClientRequest.StackDeregister, id, 0, 0, 0, 0); + doClientRequestStmt(.StackDeregister, id, 0, 0, 0, 0); } /// Change the start and end address of the stack id. /// start is the new lowest addressable stack byte, end is the new highest /// addressable stack byte. pub fn stackChange(id: usize, newstack: []u8) void { - doClientRequestStmt(ClientRequest.StackChange, id, @ptrToInt(newstack.ptr), @ptrToInt(newstack.ptr) + newstack.len, 0, 0); + doClientRequestStmt(.StackChange, id, @ptrToInt(newstack.ptr), @ptrToInt(newstack.ptr) + newstack.len, 0, 0); } // Load PDB debug info for Wine PE image_map. // pub fn loadPdbDebuginfo(fd, ptr, total_size, delta) void { -// doClientRequestStmt(ClientRequest.LoadPdbDebuginfo, +// doClientRequestStmt(.LoadPdbDebuginfo, // fd, ptr, total_size, delta, // 0); // } @@ -234,7 +225,7 @@ pub fn stackChange(id: usize, newstack: []u8) void { /// result will be dumped in there and is guaranteed to be zero /// terminated. If no info is found, the first byte is set to zero. pub fn mapIpToSrcloc(addr: *const u8, buf64: [64]u8) usize { - return doClientRequestExpr(0, ClientRequest.MapIpToSrcloc, @ptrToInt(addr), @ptrToInt(&buf64[0]), 0, 0, 0); + return doClientRequestExpr(0, .MapIpToSrcloc, @ptrToInt(addr), @ptrToInt(&buf64[0]), 0, 0, 0); } /// Disable error reporting for this thread. Behaves in a stack like @@ -246,12 +237,12 @@ pub fn mapIpToSrcloc(addr: *const u8, buf64: [64]u8) usize { /// reporting. Child threads do not inherit this setting from their /// parents -- they are always created with reporting enabled. pub fn disableErrorReporting() void { - doClientRequestStmt(ClientRequest.ChangeErrDisablement, 1, 0, 0, 0, 0); + doClientRequestStmt(.ChangeErrDisablement, 1, 0, 0, 0, 0); } /// Re-enable error reporting, (see disableErrorReporting()) pub fn enableErrorReporting() void { - doClientRequestStmt(ClientRequest.ChangeErrDisablement, math.maxInt(usize), 0, 0, 0, 0); + doClientRequestStmt(.ChangeErrDisablement, math.maxInt(usize), 0, 0, 0, 0); } /// Execute a monitor command from the client program. @@ -260,8 +251,13 @@ pub fn enableErrorReporting() void { /// If no connection is opened, output will go to the log output. /// Returns 1 if command not recognised, 0 otherwise. pub fn monitorCommand(command: [*]u8) bool { - return doClientRequestExpr(0, ClientRequest.GdbMonitorCommand, @ptrToInt(command.ptr), 0, 0, 0, 0) != 0; + return doClientRequestExpr(0, .GdbMonitorCommand, @ptrToInt(command.ptr), 0, 0, 0, 0) != 0; } -pub const memcheck = @import("memcheck.zig"); -pub const callgrind = @import("callgrind.zig"); +pub const memcheck = @import("valgrind/memcheck.zig"); +pub const callgrind = @import("valgrind/callgrind.zig"); + +test "" { + _ = @import("valgrind/memcheck.zig"); + _ = @import("valgrind/callgrind.zig"); +} diff --git a/lib/std/valgrind/callgrind.zig b/lib/std/valgrind/callgrind.zig index d00829487..82725e910 100644 --- a/lib/std/valgrind/callgrind.zig +++ b/lib/std/valgrind/callgrind.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const valgrind = std.valgrind; pub const CallgrindClientRequest = extern enum { @@ -20,7 +20,7 @@ fn doCallgrindClientRequestStmt(request: CallgrindClientRequest, a1: usize, a2: /// Dump current state of cost centers, and zero them afterwards pub fn dumpStats() void { - doCallgrindClientRequestStmt(CallgrindClientRequest.DumpStats, 0, 0, 0, 0, 0); + doCallgrindClientRequestStmt(.DumpStats, 0, 0, 0, 0, 0); } /// Dump current state of cost centers, and zero them afterwards. @@ -28,12 +28,12 @@ pub fn dumpStats() void { /// the dump. This string is written as a description field into the /// profile data dump. pub fn dumpStatsAt(pos_str: [*]u8) void { - doCallgrindClientRequestStmt(CallgrindClientRequest.DumpStatsAt, @ptrToInt(pos_str), 0, 0, 0, 0); + doCallgrindClientRequestStmt(.DumpStatsAt, @ptrToInt(pos_str), 0, 0, 0, 0); } /// Zero cost centers pub fn zeroStats() void { - doCallgrindClientRequestStmt(CallgrindClientRequest.ZeroStats, 0, 0, 0, 0, 0); + doCallgrindClientRequestStmt(.ZeroStats, 0, 0, 0, 0, 0); } /// Toggles collection state. @@ -41,7 +41,7 @@ pub fn zeroStats() void { /// should be noted or if they are to be ignored. Events are noted /// by increment of counters in a cost center pub fn toggleCollect() void { - doCallgrindClientRequestStmt(CallgrindClientRequest.ToggleCollect, 0, 0, 0, 0, 0); + doCallgrindClientRequestStmt(.ToggleCollect, 0, 0, 0, 0, 0); } /// Start full callgrind instrumentation if not already switched on. @@ -49,7 +49,7 @@ pub fn toggleCollect() void { /// this will lead to an artificial cache warmup phase afterwards with /// cache misses which would not have happened in reality. pub fn startInstrumentation() void { - doCallgrindClientRequestStmt(CallgrindClientRequest.StartInstrumentation, 0, 0, 0, 0, 0); + doCallgrindClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0); } /// Stop full callgrind instrumentation if not already switched off. @@ -60,5 +60,5 @@ pub fn startInstrumentation() void { /// To start Callgrind in this mode to ignore the setup phase, use /// the option "--instr-atstart=no". pub fn stopInstrumentation() void { - doCallgrindClientRequestStmt(CallgrindClientRequest.StopInstrumentation, 0, 0, 0, 0, 0); + doCallgrindClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0); } diff --git a/lib/std/valgrind/memcheck.zig b/lib/std/valgrind/memcheck.zig index 2830f58db..e0aefa7d5 100644 --- a/lib/std/valgrind/memcheck.zig +++ b/lib/std/valgrind/memcheck.zig @@ -1,8 +1,9 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); +const testing = std.testing; const valgrind = std.valgrind; pub const MemCheckClientRequest = extern enum { - MakeMemNoAccess = valgrind.ToolBase("MC"), + MakeMemNoAccess = valgrind.ToolBase("MC".*), MakeMemUndefined, MakeMemDefined, Discard, @@ -31,7 +32,7 @@ fn doMemCheckClientRequestStmt(request: MemCheckClientRequest, a1: usize, a2: us /// This returns -1 when run on Valgrind and 0 otherwise. pub fn makeMemNoAccess(qzz: []u8) i1 { return @intCast(i1, doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.MakeMemNoAccess, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); + .MakeMemNoAccess, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); } /// Similarly, mark memory at qzz.ptr as addressable but undefined @@ -39,7 +40,7 @@ pub fn makeMemNoAccess(qzz: []u8) i1 { /// This returns -1 when run on Valgrind and 0 otherwise. pub fn makeMemUndefined(qzz: []u8) i1 { return @intCast(i1, doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); + .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); } /// Similarly, mark memory at qzz.ptr as addressable and defined @@ -47,7 +48,7 @@ pub fn makeMemUndefined(qzz: []u8) i1 { pub fn makeMemDefined(qzz: []u8) i1 { // This returns -1 when run on Valgrind and 0 otherwise. return @intCast(i1, doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.MakeMemDefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); + .MakeMemDefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); } /// Similar to makeMemDefined except that addressability is @@ -56,7 +57,7 @@ pub fn makeMemDefined(qzz: []u8) i1 { /// This returns -1 when run on Valgrind and 0 otherwise. pub fn makeMemDefinedIfAddressable(qzz: []u8) i1 { return @intCast(i1, doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.MakeMemDefinedIfAddressable, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); + .MakeMemDefinedIfAddressable, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); } /// Create a block-description handle. The description is an ascii @@ -65,14 +66,14 @@ pub fn makeMemDefinedIfAddressable(qzz: []u8) i1 { /// properties of the memory range. pub fn createBlock(qzz: []u8, desc: [*]u8) usize { return doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.CreateBlock, @ptrToInt(qzz.ptr), qzz.len, @ptrToInt(desc), 0, 0); + .CreateBlock, @ptrToInt(qzz.ptr), qzz.len, @ptrToInt(desc), 0, 0); } /// Discard a block-description-handle. Returns 1 for an /// invalid handle, 0 for a valid handle. pub fn discard(blkindex) bool { return doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.Discard, 0, blkindex, 0, 0, 0) != 0; + .Discard, 0, blkindex, 0, 0, 0) != 0; } /// Check that memory at qzz.ptr is addressable for qzz.len bytes. @@ -80,7 +81,7 @@ pub fn discard(blkindex) bool { /// error message and returns the address of the first offending byte. /// Otherwise it returns zero. pub fn checkMemIsAddressable(qzz: []u8) usize { - return doMemCheckClientRequestExpr(0, MemCheckClientRequest.CheckMemIsAddressable, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); + return doMemCheckClientRequestExpr(0, .CheckMemIsAddressable, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); } /// Check that memory at qzz.ptr is addressable and defined for @@ -88,31 +89,31 @@ pub fn checkMemIsAddressable(qzz: []u8) usize { /// established, Valgrind prints an error message and returns the /// address of the first offending byte. Otherwise it returns zero. pub fn checkMemIsDefined(qzz: []u8) usize { - return doMemCheckClientRequestExpr(0, MemCheckClientRequest.CheckMemIsDefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); + return doMemCheckClientRequestExpr(0, .CheckMemIsDefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); } /// Do a full memory leak check (like --leak-check=full) mid-execution. pub fn doLeakCheck() void { - doMemCheckClientRequestStmt(MemCheckClientRequest.DO_LEAK_CHECK, 0, 0, 0, 0, 0); + doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 0, 0, 0, 0); } /// Same as doLeakCheck() but only showing the entries for /// which there was an increase in leaked bytes or leaked nr of blocks /// since the previous leak search. pub fn doAddedLeakCheck() void { - doMemCheckClientRequestStmt(MemCheckClientRequest.DO_LEAK_CHECK, 0, 1, 0, 0, 0); + doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 1, 0, 0, 0); } /// Same as doAddedLeakCheck() but showing entries with /// increased or decreased leaked bytes/blocks since previous leak /// search. pub fn doChangedLeakCheck() void { - doMemCheckClientRequestStmt(MemCheckClientRequest.DO_LEAK_CHECK, 0, 2, 0, 0, 0); + doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 2, 0, 0, 0); } /// Do a summary memory leak check (like --leak-check=summary) mid-execution. pub fn doQuickLeakCheck() void { - doMemCheckClientRequestStmt(MemCheckClientRequest.DO_LEAK_CHECK, 1, 0, 0, 0, 0); + doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 1, 0, 0, 0, 0); } /// Return number of leaked, dubious, reachable and suppressed bytes found by @@ -125,27 +126,65 @@ const CountResult = struct { }; pub fn countLeaks() CountResult { - var res = CountResult{ + var res: CountResult = .{ .leaked = 0, .dubious = 0, .reachable = 0, .suppressed = 0, }; - doMemCheckClientRequestStmt(MemCheckClientRequest.CountLeaks, &res.leaked, &res.dubious, &res.reachable, &res.suppressed, 0); + doMemCheckClientRequestStmt( + .CountLeaks, + @ptrToInt(&res.leaked), + @ptrToInt(&res.dubious), + @ptrToInt(&res.reachable), + @ptrToInt(&res.suppressed), + 0, + ); return res; } +test "countLeaks" { + testing.expectEqual( + @as(CountResult, .{ + .leaked = 0, + .dubious = 0, + .reachable = 0, + .suppressed = 0, + }), + countLeaks(), + ); +} + pub fn countLeakBlocks() CountResult { - var res = CountResult{ + var res: CountResult = .{ .leaked = 0, .dubious = 0, .reachable = 0, .suppressed = 0, }; - doMemCheckClientRequestStmt(MemCheckClientRequest.CountLeakBlocks, &res.leaked, &res.dubious, &res.reachable, &res.suppressed, 0); + doMemCheckClientRequestStmt( + .CountLeakBlocks, + @ptrToInt(&res.leaked), + @ptrToInt(&res.dubious), + @ptrToInt(&res.reachable), + @ptrToInt(&res.suppressed), + 0, + ); return res; } +test "countLeakBlocks" { + testing.expectEqual( + @as(CountResult, .{ + .leaked = 0, + .dubious = 0, + .reachable = 0, + .suppressed = 0, + }), + countLeakBlocks(), + ); +} + /// Get the validity data for addresses zza and copy it /// into the provided zzvbits array. Return values: /// 0 if not running on valgrind @@ -156,7 +195,7 @@ pub fn countLeakBlocks() CountResult { /// impossible to segfault your system by using this call. pub fn getVbits(zza: []u8, zzvbits: []u8) u2 { std.debug.assert(zzvbits.len >= zza.len / 8); - return @intCast(u2, doMemCheckClientRequestExpr(0, MemCheckClientRequest.GetVbits, @ptrToInt(zza.ptr), @ptrToInt(zzvbits), zza.len, 0, 0)); + return @intCast(u2, doMemCheckClientRequestExpr(0, .GetVbits, @ptrToInt(zza.ptr), @ptrToInt(zzvbits), zza.len, 0, 0)); } /// Set the validity data for addresses zza, copying it @@ -169,17 +208,17 @@ pub fn getVbits(zza: []u8, zzvbits: []u8) u2 { /// impossible to segfault your system by using this call. pub fn setVbits(zzvbits: []u8, zza: []u8) u2 { std.debug.assert(zzvbits.len >= zza.len / 8); - return @intCast(u2, doMemCheckClientRequestExpr(0, MemCheckClientRequest.SetVbits, @ptrToInt(zza.ptr), @ptrToInt(zzvbits), zza.len, 0, 0)); + return @intCast(u2, doMemCheckClientRequestExpr(0, .SetVbits, @ptrToInt(zza.ptr), @ptrToInt(zzvbits), zza.len, 0, 0)); } /// Disable and re-enable reporting of addressing errors in the /// specified address range. pub fn disableAddrErrorReportingInRange(qzz: []u8) usize { return doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.DisableAddrErrorReportingInRange, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); + .DisableAddrErrorReportingInRange, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); } pub fn enableAddrErrorReportingInRange(qzz: []u8) usize { return doMemCheckClientRequestExpr(0, // default return - MemCheckClientRequest.EnableAddrErrorReportingInRange, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); + .EnableAddrErrorReportingInRange, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0); } diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 79e664714..dc9a6bc7c 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -10,9 +10,11 @@ pub const TokenIndex = usize; pub const Tree = struct { source: []const u8, tokens: TokenList, + /// undefined on parse error (errors not empty) root_node: *Node.Root, arena_allocator: std.heap.ArenaAllocator, errors: ErrorList, + generated: bool = false, pub const TokenList = SegmentedList(Token, 64); pub const ErrorList = SegmentedList(Error, 0); @@ -58,6 +60,8 @@ pub const Tree = struct { .line_start = start_index, .line_end = self.source.len, }; + if (self.generated) + return loc; const token_start = token.start; for (self.source[start_index..]) |c, i| { if (i + start_index == token_start) { @@ -290,7 +294,7 @@ pub const Error = union(enum) { pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'"); pub const ExpectedParamType = SimpleError("Expected parameter type"); - pub const ExpectedPubItem = SimpleError("Pub must be followed by fn decl, var decl, or container member"); + pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); @@ -301,7 +305,9 @@ pub const Error = union(enum) { node: *Node, pub fn render(self: *const ExpectedCall, tokens: *Tree.TokenList, stream: var) !void { - return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", .{ + @tagName(self.node.id), + }); } }; @@ -309,7 +315,8 @@ pub const Error = union(enum) { node: *Node, pub fn render(self: *const ExpectedCallOrFnProto, tokens: *Tree.TokenList, stream: var) !void { - return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ + @tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)}); } }; @@ -321,14 +328,14 @@ pub const Error = union(enum) { const found_token = tokens.at(self.token); switch (found_token.id) { .Invalid_ampersands => { - return stream.print("`&&` is invalid. Note that `and` is boolean AND."); + return stream.print("`&&` is invalid. Note that `and` is boolean AND.", .{}); }, .Invalid => { - return stream.print("expected '{}', found invalid bytes", self.expected_id.symbol()); + return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); }, else => { const token_name = found_token.id.symbol(); - return stream.print("expected '{}', found '{}'", self.expected_id.symbol(), token_name); + return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); }, } } @@ -340,7 +347,10 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedCommaOrEnd, tokens: *Tree.TokenList, stream: var) !void { const actual_token = tokens.at(self.token); - return stream.print("expected ',' or '{}', found '{}'", self.end_id.symbol(), actual_token.id.symbol()); + return stream.print("expected ',' or '{}', found '{}'", .{ + self.end_id.symbol(), + actual_token.id.symbol(), + }); } }; @@ -352,7 +362,7 @@ pub const Error = union(enum) { pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void { const actual_token = tokens.at(self.token); - return stream.print(msg, actual_token.id.symbol()); + return stream.print(msg, .{actual_token.id.symbol()}); } }; } @@ -563,10 +573,10 @@ pub const Node = struct { { var i: usize = 0; while (i < indent) : (i += 1) { - std.debug.warn(" "); + std.debug.warn(" ", .{}); } } - std.debug.warn("{}\n", @tagName(self.id)); + std.debug.warn("{}\n", .{@tagName(self.id)}); var child_i: usize = 0; while (self.iterate(child_i)) |child| : (child_i += 1) { @@ -575,8 +585,7 @@ pub const Node = struct { } pub const Root = struct { - base: Node, - doc_comments: ?*DocComment, + base: Node = Node{ .id = .Root }, decls: DeclList, eof_token: TokenIndex, @@ -599,12 +608,12 @@ pub const Node = struct { }; pub const VarDecl = struct { - base: Node, + base: Node = Node{ .id = .VarDecl }, doc_comments: ?*DocComment, visib_token: ?TokenIndex, thread_local_token: ?TokenIndex, name_token: TokenIndex, - eq_token: TokenIndex, + eq_token: ?TokenIndex, mut_token: TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, @@ -656,7 +665,7 @@ pub const Node = struct { }; pub const Use = struct { - base: Node, + base: Node = Node{ .id = .Use }, doc_comments: ?*DocComment, visib_token: ?TokenIndex, use_token: TokenIndex, @@ -683,7 +692,7 @@ pub const Node = struct { }; pub const ErrorSetDecl = struct { - base: Node, + base: Node = Node{ .id = .ErrorSetDecl }, error_token: TokenIndex, decls: DeclList, rbrace_token: TokenIndex, @@ -709,7 +718,7 @@ pub const Node = struct { }; pub const ContainerDecl = struct { - base: Node, + base: Node = Node{ .id = .ContainerDecl }, layout_token: ?TokenIndex, kind_token: TokenIndex, init_arg_expr: InitArg, @@ -755,9 +764,9 @@ pub const Node = struct { }; pub const ContainerField = struct { - base: Node, + base: Node = Node{ .id = .ContainerField }, doc_comments: ?*DocComment, - visib_token: ?TokenIndex, + comptime_token: ?TokenIndex, name_token: TokenIndex, type_expr: ?*Node, value_expr: ?*Node, @@ -780,8 +789,7 @@ pub const Node = struct { } pub fn firstToken(self: *const ContainerField) TokenIndex { - if (self.visib_token) |visib_token| return visib_token; - return self.name_token; + return self.comptime_token orelse self.name_token; } pub fn lastToken(self: *const ContainerField) TokenIndex { @@ -797,7 +805,7 @@ pub const Node = struct { }; pub const ErrorTag = struct { - base: Node, + base: Node = Node{ .id = .ErrorTag }, doc_comments: ?*DocComment, name_token: TokenIndex, @@ -822,7 +830,7 @@ pub const Node = struct { }; pub const Identifier = struct { - base: Node, + base: Node = Node{ .id = .Identifier }, token: TokenIndex, pub fn iterate(self: *Identifier, index: usize) ?*Node { @@ -839,7 +847,7 @@ pub const Node = struct { }; pub const FnProto = struct { - base: Node, + base: Node = Node{ .id = .FnProto }, doc_comments: ?*DocComment, visib_token: ?TokenIndex, fn_token: TokenIndex, @@ -853,6 +861,7 @@ pub const Node = struct { lib_name: ?*Node, // populated if this is an extern declaration align_expr: ?*Node, // populated if align(A) is present section_expr: ?*Node, // populated if linksection(A) is present + callconv_expr: ?*Node, // populated if callconv(A) is present pub const ParamList = SegmentedList(*Node, 2); @@ -921,7 +930,7 @@ pub const Node = struct { }; pub const AnyFrameType = struct { - base: Node, + base: Node = Node{ .id = .AnyFrameType }, anyframe_token: TokenIndex, result: ?Result, @@ -952,7 +961,7 @@ pub const Node = struct { }; pub const ParamDecl = struct { - base: Node, + base: Node = Node{ .id = .ParamDecl }, doc_comments: ?*DocComment, comptime_token: ?TokenIndex, noalias_token: ?TokenIndex, @@ -963,7 +972,9 @@ pub const Node = struct { pub fn iterate(self: *ParamDecl, index: usize) ?*Node { var i = index; - if (i < 1) return self.type_node; + if (i < 1) { + return if (self.var_args_token == null) self.type_node else null; + } i -= 1; return null; @@ -983,7 +994,7 @@ pub const Node = struct { }; pub const Block = struct { - base: Node, + base: Node = Node{ .id = .Block }, label: ?TokenIndex, lbrace: TokenIndex, statements: StatementList, @@ -1014,7 +1025,7 @@ pub const Node = struct { }; pub const Defer = struct { - base: Node, + base: Node = Node{ .id = .Defer }, defer_token: TokenIndex, expr: *Node, @@ -1037,7 +1048,7 @@ pub const Node = struct { }; pub const Comptime = struct { - base: Node, + base: Node = Node{ .id = .Comptime }, doc_comments: ?*DocComment, comptime_token: TokenIndex, expr: *Node, @@ -1061,7 +1072,7 @@ pub const Node = struct { }; pub const Payload = struct { - base: Node, + base: Node = Node{ .id = .Payload }, lpipe: TokenIndex, error_symbol: *Node, rpipe: TokenIndex, @@ -1085,7 +1096,7 @@ pub const Node = struct { }; pub const PointerPayload = struct { - base: Node, + base: Node = Node{ .id = .PointerPayload }, lpipe: TokenIndex, ptr_token: ?TokenIndex, value_symbol: *Node, @@ -1110,7 +1121,7 @@ pub const Node = struct { }; pub const PointerIndexPayload = struct { - base: Node, + base: Node = Node{ .id = .PointerIndexPayload }, lpipe: TokenIndex, ptr_token: ?TokenIndex, value_symbol: *Node, @@ -1141,7 +1152,7 @@ pub const Node = struct { }; pub const Else = struct { - base: Node, + base: Node = Node{ .id = .Else }, else_token: TokenIndex, payload: ?*Node, body: *Node, @@ -1170,7 +1181,7 @@ pub const Node = struct { }; pub const Switch = struct { - base: Node, + base: Node = Node{ .id = .Switch }, switch_token: TokenIndex, expr: *Node, @@ -1202,7 +1213,7 @@ pub const Node = struct { }; pub const SwitchCase = struct { - base: Node, + base: Node = Node{ .id = .SwitchCase }, items: ItemList, arrow_token: TokenIndex, payload: ?*Node, @@ -1237,7 +1248,7 @@ pub const Node = struct { }; pub const SwitchElse = struct { - base: Node, + base: Node = Node{ .id = .SwitchElse }, token: TokenIndex, pub fn iterate(self: *SwitchElse, index: usize) ?*Node { @@ -1254,7 +1265,7 @@ pub const Node = struct { }; pub const While = struct { - base: Node, + base: Node = Node{ .id = .While }, label: ?TokenIndex, inline_token: ?TokenIndex, while_token: TokenIndex, @@ -1313,7 +1324,7 @@ pub const Node = struct { }; pub const For = struct { - base: Node, + base: Node = Node{ .id = .For }, label: ?TokenIndex, inline_token: ?TokenIndex, for_token: TokenIndex, @@ -1364,7 +1375,7 @@ pub const Node = struct { }; pub const If = struct { - base: Node, + base: Node = Node{ .id = .If }, if_token: TokenIndex, condition: *Node, payload: ?*Node, @@ -1407,7 +1418,7 @@ pub const Node = struct { }; pub const InfixOp = struct { - base: Node, + base: Node = Node{ .id = .InfixOp }, op_token: TokenIndex, lhs: *Node, op: Op, @@ -1425,13 +1436,13 @@ pub const Node = struct { AssignBitShiftRight, AssignBitXor, AssignDiv, - AssignMinus, - AssignMinusWrap, + AssignSub, + AssignSubWrap, AssignMod, - AssignPlus, - AssignPlusWrap, - AssignTimes, - AssignTimesWarp, + AssignAdd, + AssignAddWrap, + AssignMul, + AssignMulWrap, BangEqual, BitAnd, BitOr, @@ -1450,8 +1461,8 @@ pub const Node = struct { LessThan, MergeErrorSets, Mod, - Mult, - MultWrap, + Mul, + MulWrap, Period, Range, Sub, @@ -1484,13 +1495,13 @@ pub const Node = struct { Op.AssignBitShiftRight, Op.AssignBitXor, Op.AssignDiv, - Op.AssignMinus, - Op.AssignMinusWrap, + Op.AssignSub, + Op.AssignSubWrap, Op.AssignMod, - Op.AssignPlus, - Op.AssignPlusWrap, - Op.AssignTimes, - Op.AssignTimesWarp, + Op.AssignAdd, + Op.AssignAddWrap, + Op.AssignMul, + Op.AssignMulWrap, Op.BangEqual, Op.BitAnd, Op.BitOr, @@ -1508,8 +1519,8 @@ pub const Node = struct { Op.LessThan, Op.MergeErrorSets, Op.Mod, - Op.Mult, - Op.MultWrap, + Op.Mul, + Op.MulWrap, Op.Period, Op.Range, Op.Sub, @@ -1534,14 +1545,14 @@ pub const Node = struct { }; pub const PrefixOp = struct { - base: Node, + base: Node = Node{ .id = .PrefixOp }, op_token: TokenIndex, op: Op, rhs: *Node, pub const Op = union(enum) { AddressOf, - ArrayType: *Node, + ArrayType: ArrayInfo, Await, BitNot, BoolNot, @@ -1555,11 +1566,17 @@ pub const Node = struct { Try, }; + pub const ArrayInfo = struct { + len_expr: *Node, + sentinel: ?*Node, + }; + pub const PtrInfo = struct { - allowzero_token: ?TokenIndex, - align_info: ?Align, - const_token: ?TokenIndex, - volatile_token: ?TokenIndex, + allowzero_token: ?TokenIndex = null, + align_info: ?Align = null, + const_token: ?TokenIndex = null, + volatile_token: ?TokenIndex = null, + sentinel: ?*Node = null, pub const Align = struct { node: *Node, @@ -1578,6 +1595,11 @@ pub const Node = struct { switch (self.op) { // TODO https://github.com/ziglang/zig/issues/1107 Op.SliceType => |addr_of_info| { + if (addr_of_info.sentinel) |sentinel| { + if (i < 1) return sentinel; + i -= 1; + } + if (addr_of_info.align_info) |align_info| { if (i < 1) return align_info.node; i -= 1; @@ -1591,9 +1613,13 @@ pub const Node = struct { } }, - Op.ArrayType => |size_expr| { - if (i < 1) return size_expr; + Op.ArrayType => |array_info| { + if (i < 1) return array_info.len_expr; i -= 1; + if (array_info.sentinel) |sentinel| { + if (i < 1) return sentinel; + i -= 1; + } }, Op.AddressOf, @@ -1625,7 +1651,7 @@ pub const Node = struct { }; pub const FieldInitializer = struct { - base: Node, + base: Node = Node{ .id = .FieldInitializer }, period_token: TokenIndex, name_token: TokenIndex, expr: *Node, @@ -1649,11 +1675,16 @@ pub const Node = struct { }; pub const SuffixOp = struct { - base: Node, - lhs: *Node, + base: Node = Node{ .id = .SuffixOp }, + lhs: Lhs, op: Op, rtoken: TokenIndex, + pub const Lhs = union(enum) { + node: *Node, + dot: TokenIndex, + }; + pub const Op = union(enum) { Call: Call, ArrayAccess: *Node, @@ -1675,14 +1706,20 @@ pub const Node = struct { pub const Slice = struct { start: *Node, end: ?*Node, + sentinel: ?*Node, }; }; pub fn iterate(self: *SuffixOp, index: usize) ?*Node { var i = index; - if (i < 1) return self.lhs; - i -= 1; + switch (self.lhs) { + .node => |node| { + if (i == 0) return node; + i -= 1; + }, + .dot => {}, + } switch (self.op) { .Call => |*call_info| { @@ -1701,6 +1738,10 @@ pub const Node = struct { if (i < 1) return end; i -= 1; } + if (range.sentinel) |sentinel| { + if (i < 1) return sentinel; + i -= 1; + } }, .ArrayInitializer => |*exprs| { if (i < exprs.len) return exprs.at(i).*; @@ -1723,7 +1764,10 @@ pub const Node = struct { .Call => |*call_info| if (call_info.async_token) |async_token| return async_token, else => {}, } - return self.lhs.firstToken(); + switch (self.lhs) { + .node => |node| return node.firstToken(), + .dot => |dot| return dot, + } } pub fn lastToken(self: *const SuffixOp) TokenIndex { @@ -1732,7 +1776,7 @@ pub const Node = struct { }; pub const GroupedExpression = struct { - base: Node, + base: Node = Node{ .id = .GroupedExpression }, lparen: TokenIndex, expr: *Node, rparen: TokenIndex, @@ -1756,7 +1800,7 @@ pub const Node = struct { }; pub const ControlFlowExpression = struct { - base: Node, + base: Node = Node{ .id = .ControlFlowExpression }, ltoken: TokenIndex, kind: Kind, rhs: ?*Node, @@ -1822,7 +1866,7 @@ pub const Node = struct { }; pub const Suspend = struct { - base: Node, + base: Node = Node{ .id = .Suspend }, suspend_token: TokenIndex, body: ?*Node, @@ -1851,7 +1895,7 @@ pub const Node = struct { }; pub const IntegerLiteral = struct { - base: Node, + base: Node = Node{ .id = .IntegerLiteral }, token: TokenIndex, pub fn iterate(self: *IntegerLiteral, index: usize) ?*Node { @@ -1868,7 +1912,7 @@ pub const Node = struct { }; pub const EnumLiteral = struct { - base: Node, + base: Node = Node{ .id = .EnumLiteral }, dot: TokenIndex, name: TokenIndex, @@ -1886,7 +1930,7 @@ pub const Node = struct { }; pub const FloatLiteral = struct { - base: Node, + base: Node = Node{ .id = .FloatLiteral }, token: TokenIndex, pub fn iterate(self: *FloatLiteral, index: usize) ?*Node { @@ -1903,7 +1947,7 @@ pub const Node = struct { }; pub const BuiltinCall = struct { - base: Node, + base: Node = Node{ .id = .BuiltinCall }, builtin_token: TokenIndex, params: ParamList, rparen_token: TokenIndex, @@ -1929,7 +1973,7 @@ pub const Node = struct { }; pub const StringLiteral = struct { - base: Node, + base: Node = Node{ .id = .StringLiteral }, token: TokenIndex, pub fn iterate(self: *StringLiteral, index: usize) ?*Node { @@ -1946,7 +1990,7 @@ pub const Node = struct { }; pub const MultilineStringLiteral = struct { - base: Node, + base: Node = Node{ .id = .MultilineStringLiteral }, lines: LineList, pub const LineList = SegmentedList(TokenIndex, 4); @@ -1965,7 +2009,7 @@ pub const Node = struct { }; pub const CharLiteral = struct { - base: Node, + base: Node = Node{ .id = .CharLiteral }, token: TokenIndex, pub fn iterate(self: *CharLiteral, index: usize) ?*Node { @@ -1982,7 +2026,7 @@ pub const Node = struct { }; pub const BoolLiteral = struct { - base: Node, + base: Node = Node{ .id = .BoolLiteral }, token: TokenIndex, pub fn iterate(self: *BoolLiteral, index: usize) ?*Node { @@ -1999,7 +2043,7 @@ pub const Node = struct { }; pub const NullLiteral = struct { - base: Node, + base: Node = Node{ .id = .NullLiteral }, token: TokenIndex, pub fn iterate(self: *NullLiteral, index: usize) ?*Node { @@ -2016,7 +2060,7 @@ pub const Node = struct { }; pub const UndefinedLiteral = struct { - base: Node, + base: Node = Node{ .id = .UndefinedLiteral }, token: TokenIndex, pub fn iterate(self: *UndefinedLiteral, index: usize) ?*Node { @@ -2033,7 +2077,7 @@ pub const Node = struct { }; pub const AsmOutput = struct { - base: Node, + base: Node = Node{ .id = .AsmOutput }, lbracket: TokenIndex, symbolic_name: *Node, constraint: *Node, @@ -2078,7 +2122,7 @@ pub const Node = struct { }; pub const AsmInput = struct { - base: Node, + base: Node = Node{ .id = .AsmInput }, lbracket: TokenIndex, symbolic_name: *Node, constraint: *Node, @@ -2110,7 +2154,7 @@ pub const Node = struct { }; pub const Asm = struct { - base: Node, + base: Node = Node{ .id = .Asm }, asm_token: TokenIndex, volatile_token: ?TokenIndex, template: *Node, @@ -2145,7 +2189,7 @@ pub const Node = struct { }; pub const Unreachable = struct { - base: Node, + base: Node = Node{ .id = .Unreachable }, token: TokenIndex, pub fn iterate(self: *Unreachable, index: usize) ?*Node { @@ -2162,7 +2206,7 @@ pub const Node = struct { }; pub const ErrorType = struct { - base: Node, + base: Node = Node{ .id = .ErrorType }, token: TokenIndex, pub fn iterate(self: *ErrorType, index: usize) ?*Node { @@ -2179,7 +2223,7 @@ pub const Node = struct { }; pub const VarType = struct { - base: Node, + base: Node = Node{ .id = .VarType }, token: TokenIndex, pub fn iterate(self: *VarType, index: usize) ?*Node { @@ -2196,7 +2240,7 @@ pub const Node = struct { }; pub const DocComment = struct { - base: Node, + base: Node = Node{ .id = .DocComment }, lines: LineList, pub const LineList = SegmentedList(TokenIndex, 4); @@ -2215,7 +2259,7 @@ pub const Node = struct { }; pub const TestDecl = struct { - base: Node, + base: Node = Node{ .id = .TestDecl }, doc_comments: ?*DocComment, test_token: TokenIndex, name: *Node, @@ -2243,7 +2287,6 @@ pub const Node = struct { test "iterate" { var root = Node.Root{ .base = Node{ .id = Node.Id.Root }, - .doc_comments = null, .decls = Node.Root.DeclList.init(std.debug.global_allocator), .eof_token = 0, }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 151784284..1b6afc1c1 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -13,7 +13,7 @@ pub const Error = error{ParseError} || Allocator.Error; /// Result should be freed with tree.deinit() when there are /// no more references to any of the tokens or nodes. -pub fn parse(allocator: *Allocator, source: []const u8) !*Tree { +pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { const tree = blk: { // This block looks unnecessary, but is a "foot-shield" to prevent the SegmentedLists // from being initialized with a pointer to this `arena`, which is created on @@ -48,37 +48,32 @@ pub fn parse(allocator: *Allocator, source: []const u8) !*Tree { while (it.peek().?.id == .LineComment) _ = it.next(); - tree.root_node = try parseRoot(arena, &it, tree); + tree.root_node = parseRoot(arena, &it, tree) catch |err| blk: { + switch (err) { + error.ParseError => { + assert(tree.errors.len != 0); + break :blk undefined; + }, + error.OutOfMemory => { + return error.OutOfMemory; + }, + } + }; + return tree; } /// Root <- skip ContainerMembers eof -fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node.Root { +fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Root { const node = try arena.create(Node.Root); node.* = Node.Root{ - .base = Node{ .id = .Root }, - .decls = undefined, - // TODO: Because zig fmt collapses consecutive comments separated by blank lines into - // a single multi-line comment, it is currently impossible to have a container-level - // doc comment and NO doc comment on the first decl. For now, simply - // ignore the problem and assume that there will be no container-level - // doc comments. - // See: https://github.com/ziglang/zig/issues/2288 - .doc_comments = null, - .eof_token = undefined, - }; - node.decls = parseContainerMembers(arena, it, tree) catch |err| { - // TODO: Switch on the error type - // https://github.com/ziglang/zig/issues/2473 - if (err == error.ParseError) return node; - assert(err == Allocator.Error.OutOfMemory); - return Allocator.Error.OutOfMemory; - }; - node.eof_token = eatToken(it, .Eof) orelse { - try tree.errors.push(AstError{ - .ExpectedContainerMembers = AstError.ExpectedContainerMembers{ .token = it.index }, - }); - return node; + .decls = try parseContainerMembers(arena, it, tree), + .eof_token = eatToken(it, .Eof) orelse { + try tree.errors.push(AstError{ + .ExpectedContainerMembers = .{ .token = it.index }, + }); + return error.ParseError; + }, }; return node; } @@ -94,6 +89,11 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No var list = Node.Root.DeclList.init(arena); while (true) { + if (try parseContainerDocComments(arena, it, tree)) |node| { + try list.push(node); + continue; + } + const doc_comments = try parseDocComment(arena, it, tree); if (try parseTestDecl(arena, it, tree)) |node| { @@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No continue; } + if (visib_token != null) { + try tree.errors.push(AstError{ + .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index }, + }); + return error.ParseError; + } + if (try parseContainerField(arena, it, tree)) |node| { const field = node.cast(Node.ContainerField).?; - field.visib_token = visib_token; field.doc_comments = doc_comments; try list.push(node); const comma = eatToken(it, .Comma) orelse break; @@ -149,23 +155,38 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No continue; } - // Dangling pub - if (visib_token != null) { + // Dangling doc comment + if (doc_comments != null) { try tree.errors.push(AstError{ - .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index }, + .UnattachedDocComment = AstError.UnattachedDocComment{ .token = doc_comments.?.firstToken() }, }); } - break; } return list; } -/// TestDecl <- KEYWORD_test STRINGLITERAL Block +/// Eat a multiline container doc comment +fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { + var lines = Node.DocComment.LineList.init(arena); + while (eatToken(it, .ContainerDocComment)) |line| { + try lines.push(line); + } + + if (lines.len == 0) return null; + + const node = try arena.create(Node.DocComment); + node.* = Node.DocComment{ + .lines = lines, + }; + return &node.base; +} + +/// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const test_token = eatToken(it, .Keyword_test) orelse return null; - const name_node = try expectNode(arena, it, tree, parseStringLiteral, AstError{ + const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, AstError{ .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index }, }); const block_node = try expectNode(arena, it, tree, parseBlock, AstError{ @@ -174,7 +195,6 @@ fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const test_node = try arena.create(Node.TestDecl); test_node.* = Node.TestDecl{ - .base = Node{ .id = .TestDecl }, .doc_comments = null, .test_token = test_token, .name = name_node, @@ -186,13 +206,17 @@ fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// TopLevelComptime <- KEYWORD_comptime BlockExpr fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const tok = eatToken(it, .Keyword_comptime) orelse return null; + const lbrace = eatToken(it, .LBrace) orelse { + putBackToken(it, tok); + return null; + }; + putBackToken(it, lbrace); const block_node = try expectNode(arena, it, tree, parseBlockExpr, AstError{ .ExpectedLabelOrLBrace = AstError.ExpectedLabelOrLBrace{ .token = it.index }, }); const comptime_node = try arena.create(Node.Comptime); comptime_node.* = Node.Comptime{ - .base = Node{ .id = .Comptime }, .doc_comments = null, .comptime_token = tok, .expr = block_node, @@ -201,15 +225,15 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?* } /// TopLevelDecl -/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) -/// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl +/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) +/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl /// / KEYWORD_usingnamespace Expr SEMICOLON fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { var lib_name: ?*Node = null; const extern_export_inline_token = blk: { if (eatToken(it, .Keyword_export)) |token| break :blk token; if (eatToken(it, .Keyword_extern)) |token| { - lib_name = try parseStringLiteral(arena, it, tree); + lib_name = try parseStringLiteralSingle(arena, it, tree); break :blk token; } if (eatToken(it, .Keyword_inline)) |token| break :blk token; @@ -282,7 +306,17 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const cc = parseFnCC(arena, it, tree); const fn_token = eatToken(it, .Keyword_fn) orelse { - if (cc == null) return null else return error.ParseError; + if (cc) |fnCC| { + if (fnCC == .Extern) { + putBackToken(it, fnCC.Extern); // 'extern' is also used in ContainerDecl + } else { + try tree.errors.push(AstError{ + .ExpectedToken = .{ .token = it.index, .expected_id = .Keyword_fn }, + }); + return error.ParseError; + } + } + return null; }; const name_token = eatToken(it, .Identifier); const lparen = try expectToken(it, tree, .LParen); @@ -290,6 +324,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const rparen = try expectToken(it, tree, .RParen); const align_expr = try parseByteAlign(arena, it, tree); const section_expr = try parseLinkSection(arena, it, tree); + const callconv_expr = try parseCallconv(arena, it, tree); const exclamation_token = eatToken(it, .Bang); const return_type_expr = (try parseVarType(arena, it, tree)) orelse @@ -313,7 +348,6 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const fn_proto_node = try arena.create(Node.FnProto); fn_proto_node.* = Node.FnProto{ - .base = Node{ .id = .FnProto }, .doc_comments = null, .visib_token = null, .fn_token = fn_token, @@ -327,6 +361,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { .lib_name = null, .align_expr = align_expr, .section_expr = section_expr, + .callconv_expr = callconv_expr, }; if (cc) |kind| { @@ -364,12 +399,11 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.VarDecl); node.* = Node.VarDecl{ - .base = Node{ .id = .VarDecl }, .doc_comments = null, .visib_token = null, .thread_local_token = null, .name_token = name_token, - .eq_token = eq_token orelse undefined, + .eq_token = eq_token, .mut_token = mut_token, .comptime_token = null, .extern_export_token = null, @@ -383,17 +417,27 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { return &node.base; } -/// ContainerField <- IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? +/// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const name_token = eatToken(it, .Identifier) orelse return null; + const comptime_token = eatToken(it, .Keyword_comptime); + const name_token = eatToken(it, .Identifier) orelse { + if (comptime_token) |t| putBackToken(it, t); + return null; + }; var align_expr: ?*Node = null; var type_expr: ?*Node = null; if (eatToken(it, .Colon)) |_| { - type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, - }); - align_expr = try parseByteAlign(arena, it, tree); + if (eatToken(it, .Keyword_var)) |var_tok| { + const node = try arena.create(ast.Node.VarType); + node.* = .{ .token = var_tok }; + type_expr = &node.base; + } else { + type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ + .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + }); + align_expr = try parseByteAlign(arena, it, tree); + } } const value_expr = if (eatToken(it, .Equal)) |_| @@ -405,9 +449,8 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const node = try arena.create(Node.ContainerField); node.* = Node.ContainerField{ - .base = Node{ .id = .ContainerField }, .doc_comments = null, - .visib_token = null, + .comptime_token = comptime_token, .name_token = name_token, .type_expr = type_expr, .value_expr = value_expr, @@ -443,7 +486,6 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No const node = try arena.create(Node.Comptime); node.* = Node.Comptime{ - .base = Node{ .id = .Comptime }, .doc_comments = null, .comptime_token = token, .expr = block_expr, @@ -462,7 +504,6 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No const node = try arena.create(Node.Suspend); node.* = Node.Suspend{ - .base = Node{ .id = .Suspend }, .suspend_token = suspend_token, .body = body_node, }; @@ -476,7 +517,6 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No }); const node = try arena.create(Node.Defer); node.* = Node.Defer{ - .base = Node{ .id = .Defer }, .defer_token = token, .expr = expr_node, }; @@ -524,7 +564,6 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = try arena.create(Node.Else); node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = payload, .body = else_body, @@ -557,7 +596,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node /// LabeledStatement <- BlockLabel? (Block / LoopStatement) fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const label_token = parseBlockLabel(arena, it, tree); + var colon: TokenIndex = undefined; + const label_token = parseBlockLabel(arena, it, tree, &colon); if (try parseBlock(arena, it, tree)) |node| { node.cast(Node.Block).?.label = label_token; @@ -617,7 +657,6 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = null, .body = statement_node, @@ -642,7 +681,6 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = null, .body = statement_node, @@ -679,7 +717,6 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = payload, .body = statement_node, @@ -706,7 +743,6 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = payload, .body = statement_node, @@ -738,7 +774,8 @@ fn parseBlockExprStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) ! /// BlockExpr <- BlockLabel? Block fn parseBlockExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { - const label_token = parseBlockLabel(arena, it, tree); + var colon: TokenIndex = undefined; + const label_token = parseBlockLabel(arena, it, tree, &colon); const block_node = (try parseBlock(arena, it, tree)) orelse { if (label_token) |label| { putBackToken(it, label + 1); // ":" @@ -834,7 +871,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const expr_node = try parseExpr(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); node.* = Node.ControlFlowExpression{ - .base = Node{ .id = .ControlFlowExpression }, .ltoken = token, .kind = Node.ControlFlowExpression.Kind{ .Break = label }, .rhs = expr_node, @@ -848,7 +884,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node }); const node = try arena.create(Node.Comptime); node.* = Node.Comptime{ - .base = Node{ .id = .Comptime }, .doc_comments = null, .comptime_token = token, .expr = expr_node, @@ -860,7 +895,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const label = try parseBreakLabel(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); node.* = Node.ControlFlowExpression{ - .base = Node{ .id = .ControlFlowExpression }, .ltoken = token, .kind = Node.ControlFlowExpression.Kind{ .Continue = label }, .rhs = null, @@ -874,7 +908,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node }); const node = try arena.create(Node.PrefixOp); node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, .op_token = token, .op = Node.PrefixOp.Op.Resume, .rhs = expr_node, @@ -886,7 +919,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const expr_node = try parseExpr(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); node.* = Node.ControlFlowExpression{ - .base = Node{ .id = .ControlFlowExpression }, .ltoken = token, .kind = Node.ControlFlowExpression.Kind.Return, .rhs = expr_node, @@ -894,7 +926,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return &node.base; } - const label = parseBlockLabel(arena, it, tree); + var colon: TokenIndex = undefined; + const label = parseBlockLabel(arena, it, tree, &colon); if (try parseLoopExpr(arena, it, tree)) |node| { if (node.cast(Node.For)) |for_node| { for_node.label = label; @@ -933,7 +966,6 @@ fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const block_node = try arena.create(Node.Block); block_node.* = Node.Block{ - .base = Node{ .id = .Block }, .label = null, .lbrace = lbrace, .statements = statements, @@ -983,7 +1015,6 @@ fn parseForExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = null, .body = body, @@ -1013,7 +1044,6 @@ fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = payload, .body = body, @@ -1028,16 +1058,16 @@ fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// CurlySuffixExpr <- TypeExpr InitList? fn parseCurlySuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const type_expr = (try parseTypeExpr(arena, it, tree)) orelse return null; - const init_list = (try parseInitList(arena, it, tree)) orelse return type_expr; - init_list.cast(Node.SuffixOp).?.lhs = type_expr; - return init_list; + const suffix_op = (try parseInitList(arena, it, tree)) orelse return type_expr; + suffix_op.lhs.node = type_expr; + return &suffix_op.base; } /// InitList /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE /// / LBRACE RBRACE -fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { +fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.SuffixOp { const lbrace = eatToken(it, .LBrace) orelse return null; var init_list = Node.SuffixOp.Op.InitList.init(arena); @@ -1065,12 +1095,11 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.SuffixOp); node.* = Node.SuffixOp{ - .base = Node{ .id = .SuffixOp }, - .lhs = undefined, // set by caller + .lhs = .{ .node = undefined }, // set by caller .op = op, .rtoken = try expectToken(it, tree, .RBrace), }; - return &node.base; + return node; } /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr @@ -1119,7 +1148,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { while (try parseSuffixOp(arena, it, tree)) |node| { switch (node.id) { - .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res, + .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, .InfixOp => node.cast(Node.InfixOp).?.lhs = res, else => unreachable, } @@ -1134,8 +1163,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; const node = try arena.create(Node.SuffixOp); node.* = Node.SuffixOp{ - .base = Node{ .id = .SuffixOp }, - .lhs = res, + .lhs = .{ .node = res }, .op = Node.SuffixOp.Op{ .Call = Node.SuffixOp.Op.Call{ .params = params.list, @@ -1152,7 +1180,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { while (true) { if (try parseSuffixOp(arena, it, tree)) |node| { switch (node.id) { - .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res, + .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, .InfixOp => node.cast(Node.InfixOp).?.lhs = res, else => unreachable, } @@ -1162,8 +1190,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (try parseFnCallArguments(arena, it, tree)) |params| { const call = try arena.create(Node.SuffixOp); call.* = Node.SuffixOp{ - .base = Node{ .id = .SuffixOp }, - .lhs = res, + .lhs = .{ .node = res }, .op = Node.SuffixOp.Op{ .Call = Node.SuffixOp.Op.Call{ .params = params.list, @@ -1211,13 +1238,12 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N if (eatToken(it, .CharLiteral)) |token| { const node = try arena.create(Node.CharLiteral); node.* = Node.CharLiteral{ - .base = Node{ .id = .CharLiteral }, .token = token, }; return &node.base; } if (try parseContainerDecl(arena, it, tree)) |node| return node; - if (try parseEnumLiteral(arena, it, tree)) |node| return node; + if (try parseAnonLiteral(arena, it, tree)) |node| return node; if (try parseErrorSetDecl(arena, it, tree)) |node| return node; if (try parseFloatLiteral(arena, it, tree)) |node| return node; if (try parseFnProto(arena, it, tree)) |node| return node; @@ -1230,7 +1256,6 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N const expr = (try parseTypeExpr(arena, it, tree)) orelse return null; const node = try arena.create(Node.Comptime); node.* = Node.Comptime{ - .base = Node{ .id = .Comptime }, .doc_comments = null, .comptime_token = token, .expr = expr, @@ -1245,7 +1270,6 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N const global_error_set = try createLiteral(arena, Node.ErrorType, token); const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = period, .lhs = global_error_set, .op = Node.InfixOp.Op.Period, @@ -1258,7 +1282,6 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N if (eatToken(it, .Keyword_anyframe)) |token| { const node = try arena.create(Node.AnyFrameType); node.* = Node.AnyFrameType{ - .base = Node{ .id = .AnyFrameType }, .anyframe_token = token, .result = null, }; @@ -1300,7 +1323,6 @@ fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = try arena.create(Node.ErrorSetDecl); node.* = Node.ErrorSetDecl{ - .base = Node{ .id = .ErrorSetDecl }, .error_token = error_token, .decls = decls, .rbrace_token = rbrace, @@ -1318,7 +1340,6 @@ fn parseGroupedExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = try arena.create(Node.GroupedExpression); node.* = Node.GroupedExpression{ - .base = Node{ .id = .GroupedExpression }, .lparen = lparen, .expr = expr, .rparen = rparen, @@ -1335,7 +1356,8 @@ fn parseIfTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// <- BlockLabel Block /// / BlockLabel? LoopTypeExpr fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const label = parseBlockLabel(arena, it, tree); + var colon: TokenIndex = undefined; + const label = parseBlockLabel(arena, it, tree, &colon); if (label) |token| { if (try parseBlock(arena, it, tree)) |node| { @@ -1353,12 +1375,9 @@ fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N return node; } - if (label != null) { - // If we saw a label, there should have been a block next - try tree.errors.push(AstError{ - .ExpectedLBrace = AstError.ExpectedLBrace{ .token = it.index }, - }); - return error.ParseError; + if (label) |token| { + putBackToken(it, colon); + putBackToken(it, token); } return null; } @@ -1403,7 +1422,6 @@ fn parseForTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = null, .body = else_expr, @@ -1434,7 +1452,6 @@ fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = null, .body = else_expr, @@ -1460,7 +1477,6 @@ fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.Switch); node.* = Node.Switch{ - .base = Node{ .id = .Switch }, .switch_token = switch_token, .expr = expr_node, .cases = cases, @@ -1469,18 +1485,17 @@ fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { return &node.base; } -/// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN +/// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const asm_token = eatToken(it, .Keyword_asm) orelse return null; const volatile_token = eatToken(it, .Keyword_volatile); _ = try expectToken(it, tree, .LParen); - const template = try expectNode(arena, it, tree, parseStringLiteral, AstError{ - .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index }, + const template = try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, }); const node = try arena.create(Node.Asm); node.* = Node.Asm{ - .base = Node{ .id = .Asm }, .asm_token = asm_token, .volatile_token = volatile_token, .template = template, @@ -1496,16 +1511,27 @@ fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } /// DOT IDENTIFIER -fn parseEnumLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { +fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const dot = eatToken(it, .Period) orelse return null; - const name = try expectToken(it, tree, .Identifier); - const node = try arena.create(Node.EnumLiteral); - node.* = Node.EnumLiteral{ - .base = Node{ .id = .EnumLiteral }, - .dot = dot, - .name = name, - }; - return &node.base; + + // anon enum literal + if (eatToken(it, .Identifier)) |name| { + const node = try arena.create(Node.EnumLiteral); + node.* = Node.EnumLiteral{ + .dot = dot, + .name = name, + }; + return &node.base; + } + + // anon container literal + if (try parseInitList(arena, it, tree)) |node| { + node.lhs = .{ .dot = dot }; + return &node.base; + } + + putBackToken(it, dot); + return null; } /// AsmOutput <- COLON AsmOutputList AsmInput? @@ -1544,7 +1570,6 @@ fn parseAsmOutputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod const node = try arena.create(Node.AsmOutput); node.* = Node.AsmOutput{ - .base = Node{ .id = .AsmOutput }, .lbracket = lbracket, .symbolic_name = name, .constraint = constraint, @@ -1581,7 +1606,6 @@ fn parseAsmInputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = try arena.create(Node.AsmInput); node.* = Node.AsmInput{ - .base = Node{ .id = .AsmInput }, .lbracket = lbracket, .symbolic_name = name, .constraint = constraint, @@ -1610,9 +1634,12 @@ fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } /// BlockLabel <- IDENTIFIER COLON -fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?TokenIndex { +fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree, colon_token: *TokenIndex) ?TokenIndex { const identifier = eatToken(it, .Identifier) orelse return null; - if (eatToken(it, .Colon) != null) return identifier; + if (eatToken(it, .Colon)) |colon| { + colon_token.* = colon; + return identifier; + } putBackToken(it, identifier); return null; } @@ -1620,7 +1647,11 @@ fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?TokenInd /// FieldInit <- DOT IDENTIFIER EQUAL Expr fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const period_token = eatToken(it, .Period) orelse return null; - const name_token = try expectToken(it, tree, .Identifier); + const name_token = eatToken(it, .Identifier) orelse { + // Because of anon literals `.{` is also valid. + putBackToken(it, period_token); + return null; + }; const eq_token = eatToken(it, .Equal) orelse { // `.Name` may also be an enum literal, which is a later rule. putBackToken(it, name_token); @@ -1633,7 +1664,6 @@ fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.FieldInitializer); node.* = Node.FieldInitializer{ - .base = Node{ .id = .FieldInitializer }, .period_token = period_token, .name_token = name_token, .expr = expr_node, @@ -1663,6 +1693,17 @@ fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return expr_node; } +/// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN +fn parseCallconv(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { + _ = eatToken(it, .Keyword_callconv) orelse return null; + _ = try expectToken(it, tree, .LParen); + const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + }); + _ = try expectToken(it, tree, .RParen); + return expr_node; +} + /// FnCC /// <- KEYWORD_nakedcc /// / KEYWORD_stdcallcc @@ -1706,7 +1747,6 @@ fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const param_decl = try arena.create(Node.ParamDecl); param_decl.* = Node.ParamDecl{ - .base = Node{ .id = .ParamDecl }, .doc_comments = doc_comments, .comptime_token = comptime_token, .noalias_token = noalias_token, @@ -1753,7 +1793,6 @@ fn parseIfPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.If); node.* = Node.If{ - .base = Node{ .id = .If }, .if_token = if_token, .condition = condition, .payload = payload, @@ -1778,7 +1817,6 @@ fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = try arena.create(Node.While); node.* = Node.While{ - .base = Node{ .id = .While }, .label = null, .inline_token = null, .while_token = while_token, @@ -1807,7 +1845,6 @@ fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.For); node.* = Node.For{ - .base = Node{ .id = .For }, .label = null, .inline_token = null, .for_token = for_token, @@ -1829,7 +1866,6 @@ fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.Payload); node.* = Node.Payload{ - .base = Node{ .id = .Payload }, .lpipe = lpipe, .error_symbol = identifier, .rpipe = rpipe, @@ -1848,7 +1884,6 @@ fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.PointerPayload); node.* = Node.PointerPayload{ - .base = Node{ .id = .PointerPayload }, .lpipe = lpipe, .ptr_token = asterisk, .value_symbol = identifier, @@ -1876,7 +1911,6 @@ fn parsePtrIndexPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N const node = try arena.create(Node.PointerIndexPayload); node.* = Node.PointerIndexPayload{ - .base = Node{ .id = .PointerIndexPayload }, .lpipe = lpipe, .ptr_token = asterisk, .value_symbol = identifier, @@ -1918,7 +1952,6 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } else if (eatToken(it, .Keyword_else)) |else_token| { const else_node = try arena.create(Node.SwitchElse); else_node.* = Node.SwitchElse{ - .base = Node{ .id = .SwitchElse }, .token = else_token, }; try list.push(&else_node.base); @@ -1926,7 +1959,6 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.SwitchCase); node.* = Node.SwitchCase{ - .base = Node{ .id = .SwitchCase }, .items = list, .arrow_token = undefined, // set by caller .payload = null, @@ -1945,7 +1977,6 @@ fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = token, .lhs = expr, .op = Node.InfixOp.Op{ .Range = {} }, @@ -1976,19 +2007,19 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = nextToken(it); const op = switch (token.ptr.id) { - .AsteriskEqual => Op{ .AssignTimes = {} }, + .AsteriskEqual => Op{ .AssignMul = {} }, .SlashEqual => Op{ .AssignDiv = {} }, .PercentEqual => Op{ .AssignMod = {} }, - .PlusEqual => Op{ .AssignPlus = {} }, - .MinusEqual => Op{ .AssignMinus = {} }, + .PlusEqual => Op{ .AssignAdd = {} }, + .MinusEqual => Op{ .AssignSub = {} }, .AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} }, .AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} }, .AmpersandEqual => Op{ .AssignBitAnd = {} }, .CaretEqual => Op{ .AssignBitXor = {} }, .PipeEqual => Op{ .AssignBitOr = {} }, - .AsteriskPercentEqual => Op{ .AssignTimesWarp = {} }, - .PlusPercentEqual => Op{ .AssignPlusWrap = {} }, - .MinusPercentEqual => Op{ .AssignMinusWrap = {} }, + .AsteriskPercentEqual => Op{ .AssignMulWrap = {} }, + .PlusPercentEqual => Op{ .AssignAddWrap = {} }, + .MinusPercentEqual => Op{ .AssignSubWrap = {} }, .Equal => Op{ .Assign = {} }, else => { putBackToken(it, token.index); @@ -1998,7 +2029,6 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = token.index, .lhs = undefined, // set by caller .op = op, @@ -2116,11 +2146,11 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = nextToken(it); const op = switch (token.ptr.id) { .PipePipe => ops{ .BoolOr = {} }, - .Asterisk => ops{ .Mult = {} }, + .Asterisk => ops{ .Mul = {} }, .Slash => ops{ .Div = {} }, .Percent => ops{ .Mod = {} }, .AsteriskAsterisk => ops{ .ArrayMult = {} }, - .AsteriskPercent => ops{ .MultWrap = {} }, + .AsteriskPercent => ops{ .MulWrap = {} }, else => { putBackToken(it, token.index); return null; @@ -2158,10 +2188,9 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.PrefixOp); node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, .op_token = token.index, .op = op, - .rhs = undefined, + .rhs = undefined, // set by caller }; return &node.base; } @@ -2182,7 +2211,6 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if (eatToken(it, .QuestionMark)) |token| { const node = try arena.create(Node.PrefixOp); node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, .op_token = token, .op = Node.PrefixOp.Op.OptionalType, .rhs = undefined, // set by caller @@ -2201,7 +2229,6 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node }; const node = try arena.create(Node.AnyFrameType); node.* = Node.AnyFrameType{ - .base = Node{ .id = .AnyFrameType }, .anyframe_token = token, .result = Node.AnyFrameType.Result{ .arrow_token = arrow, @@ -2211,63 +2238,6 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return &node.base; } - if (try parseArrayTypeStart(arena, it, tree)) |node| { - switch (node.cast(Node.PrefixOp).?.op) { - .ArrayType => {}, - .SliceType => |*slice_type| { - // Collect pointer qualifiers in any order, but disallow duplicates - while (true) { - if (try parseByteAlign(arena, it, tree)) |align_expr| { - if (slice_type.align_info != null) { - try tree.errors.push(AstError{ - .ExtraAlignQualifier = AstError.ExtraAlignQualifier{ .token = it.index }, - }); - return error.ParseError; - } - slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ - .node = align_expr, - .bit_range = null, - }; - continue; - } - if (eatToken(it, .Keyword_const)) |const_token| { - if (slice_type.const_token != null) { - try tree.errors.push(AstError{ - .ExtraConstQualifier = AstError.ExtraConstQualifier{ .token = it.index }, - }); - return error.ParseError; - } - slice_type.const_token = const_token; - continue; - } - if (eatToken(it, .Keyword_volatile)) |volatile_token| { - if (slice_type.volatile_token != null) { - try tree.errors.push(AstError{ - .ExtraVolatileQualifier = AstError.ExtraVolatileQualifier{ .token = it.index }, - }); - return error.ParseError; - } - slice_type.volatile_token = volatile_token; - continue; - } - if (eatToken(it, .Keyword_allowzero)) |allowzero_token| { - if (slice_type.allowzero_token != null) { - try tree.errors.push(AstError{ - .ExtraAllowZeroQualifier = AstError.ExtraAllowZeroQualifier{ .token = it.index }, - }); - return error.ParseError; - } - slice_type.allowzero_token = allowzero_token; - continue; - } - break; - } - }, - else => unreachable, - } - return node; - } - if (try parsePtrTypeStart(arena, it, tree)) |node| { // If the token encountered was **, there will be two nodes instead of one. // The attributes should be applied to the rightmost operator. @@ -2326,11 +2296,68 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return node; } + if (try parseArrayTypeStart(arena, it, tree)) |node| { + switch (node.cast(Node.PrefixOp).?.op) { + .ArrayType => {}, + .SliceType => |*slice_type| { + // Collect pointer qualifiers in any order, but disallow duplicates + while (true) { + if (try parseByteAlign(arena, it, tree)) |align_expr| { + if (slice_type.align_info != null) { + try tree.errors.push(AstError{ + .ExtraAlignQualifier = AstError.ExtraAlignQualifier{ .token = it.index }, + }); + return error.ParseError; + } + slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ + .node = align_expr, + .bit_range = null, + }; + continue; + } + if (eatToken(it, .Keyword_const)) |const_token| { + if (slice_type.const_token != null) { + try tree.errors.push(AstError{ + .ExtraConstQualifier = AstError.ExtraConstQualifier{ .token = it.index }, + }); + return error.ParseError; + } + slice_type.const_token = const_token; + continue; + } + if (eatToken(it, .Keyword_volatile)) |volatile_token| { + if (slice_type.volatile_token != null) { + try tree.errors.push(AstError{ + .ExtraVolatileQualifier = AstError.ExtraVolatileQualifier{ .token = it.index }, + }); + return error.ParseError; + } + slice_type.volatile_token = volatile_token; + continue; + } + if (eatToken(it, .Keyword_allowzero)) |allowzero_token| { + if (slice_type.allowzero_token != null) { + try tree.errors.push(AstError{ + .ExtraAllowZeroQualifier = AstError.ExtraAllowZeroQualifier{ .token = it.index }, + }); + return error.ParseError; + } + slice_type.allowzero_token = allowzero_token; + continue; + } + break; + } + }, + else => unreachable, + } + return node; + } + return null; } /// SuffixOp -/// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET +/// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET /// / DOT IDENTIFIER /// / DOTASTERISK /// / DOTQUESTIONMARK @@ -2348,11 +2375,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (eatToken(it, .Ellipsis2) != null) { const end_expr = try parseExpr(arena, it, tree); + const sentinel: ?*ast.Node = if (eatToken(it, .Colon) != null) + try parseExpr(arena, it, tree) + else + null; break :blk OpAndToken{ .op = Op{ .Slice = Op.Slice{ .start = index_expr, .end = end_expr, + .sentinel = sentinel, }, }, .token = try expectToken(it, tree, .RBracket), @@ -2365,6 +2397,10 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; } + if (eatToken(it, .PeriodAsterisk)) |period_asterisk| { + break :blk OpAndToken{ .op = Op{ .Deref = {} }, .token = period_asterisk }; + } + if (eatToken(it, .Period)) |period| { if (try parseIdentifier(arena, it, tree)) |identifier| { // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser. @@ -2372,7 +2408,6 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { // this grammar rule be altered? const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = period, .lhs = undefined, // set by caller .op = Node.InfixOp.Op.Period, @@ -2380,9 +2415,6 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; return &node.base; } - if (eatToken(it, .Asterisk)) |asterisk| { - break :blk OpAndToken{ .op = Op{ .Deref = {} }, .token = asterisk }; - } if (eatToken(it, .QuestionMark)) |question_mark| { break :blk OpAndToken{ .op = Op{ .UnwrapOptional = {} }, .token = question_mark }; } @@ -2397,7 +2429,6 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.SuffixOp); node.* = Node.SuffixOp{ - .base = Node{ .id = .SuffixOp }, .lhs = undefined, // set by caller .op = op_and_token.op, .rtoken = op_and_token.token, @@ -2423,10 +2454,21 @@ const AnnotatedParamList = struct { fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const lbracket = eatToken(it, .LBracket) orelse return null; const expr = try parseExpr(arena, it, tree); + const sentinel = if (eatToken(it, .Colon)) |_| + try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = .{ .token = it.index }, + }) + else + null; const rbracket = try expectToken(it, tree, .RBracket); - const op = if (expr) |element_type| - Node.PrefixOp.Op{ .ArrayType = element_type } + const op = if (expr) |len_expr| + Node.PrefixOp.Op{ + .ArrayType = .{ + .len_expr = len_expr, + .sentinel = sentinel, + }, + } else Node.PrefixOp.Op{ .SliceType = Node.PrefixOp.PtrInfo{ @@ -2434,12 +2476,12 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No .align_info = null, .const_token = null, .volatile_token = null, + .sentinel = sentinel, }, }; const node = try arena.create(Node.PrefixOp); node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, .op_token = lbracket, .op = op, .rhs = undefined, // set by caller @@ -2453,47 +2495,76 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No /// / PTRUNKNOWN /// / PTRC fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const token = eatAnnotatedToken(it, .Asterisk) orelse - eatAnnotatedToken(it, .AsteriskAsterisk) orelse - eatAnnotatedToken(it, .BracketStarBracket) orelse - eatAnnotatedToken(it, .BracketStarCBracket) orelse - return null; + if (eatToken(it, .Asterisk)) |asterisk| { + const sentinel = if (eatToken(it, .Colon)) |_| + try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = .{ .token = it.index }, + }) + else + null; + const node = try arena.create(Node.PrefixOp); + node.* = .{ + .op_token = asterisk, + .op = .{ .PtrType = .{ .sentinel = sentinel } }, + .rhs = undefined, // set by caller + }; + return &node.base; + } - const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, - .op_token = token.index, - .op = Node.PrefixOp.Op{ - .PtrType = Node.PrefixOp.PtrInfo{ - .allowzero_token = null, - .align_info = null, - .const_token = null, - .volatile_token = null, - }, - }, - .rhs = undefined, // set by caller - }; + if (eatToken(it, .AsteriskAsterisk)) |double_asterisk| { + const node = try arena.create(Node.PrefixOp); + node.* = Node.PrefixOp{ + .op_token = double_asterisk, + .op = Node.PrefixOp.Op{ .PtrType = .{} }, + .rhs = undefined, // set by caller + }; - // Special case for **, which is its own token - if (token.ptr.id == .AsteriskAsterisk) { + // Special case for **, which is its own token const child = try arena.create(Node.PrefixOp); child.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, - .op_token = token.index, - .op = Node.PrefixOp.Op{ - .PtrType = Node.PrefixOp.PtrInfo{ - .allowzero_token = null, - .align_info = null, - .const_token = null, - .volatile_token = null, - }, - }, + .op_token = double_asterisk, + .op = Node.PrefixOp.Op{ .PtrType = .{} }, .rhs = undefined, // set by caller }; node.rhs = &child.base; - } - return &node.base; + return &node.base; + } + if (eatToken(it, .LBracket)) |lbracket| { + const asterisk = eatToken(it, .Asterisk) orelse { + putBackToken(it, lbracket); + return null; + }; + if (eatToken(it, .Identifier)) |ident| { + if (!std.mem.eql(u8, tree.tokenSlice(ident), "c")) { + putBackToken(it, ident); + } else { + _ = try expectToken(it, tree, .RBracket); + const node = try arena.create(Node.PrefixOp); + node.* = .{ + .op_token = lbracket, + .op = .{ .PtrType = .{} }, + .rhs = undefined, // set by caller + }; + return &node.base; + } + } + const sentinel = if (eatToken(it, .Colon)) |_| + try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = .{ .token = it.index }, + }) + else + null; + _ = try expectToken(it, tree, .RBracket); + const node = try arena.create(Node.PrefixOp); + node.* = .{ + .op_token = lbracket, + .op = .{ .PtrType = .{ .sentinel = sentinel } }, + .rhs = undefined, // set by caller + }; + return &node.base; + } + return null; } /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE @@ -2560,7 +2631,6 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? const node = try arena.create(Node.ContainerDecl); node.* = Node.ContainerDecl{ - .base = Node{ .id = .ContainerDecl }, .layout_token = null, .kind_token = kind_token.index, .init_arg_expr = init_arg_expr, @@ -2633,7 +2703,6 @@ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) No const op_token = eatToken(it, token) orelse return null; const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = op_token, .lhs = undefined, // set by caller .op = op, @@ -2656,7 +2725,6 @@ fn parseBuiltinCall(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node }; const node = try arena.create(Node.BuiltinCall); node.* = Node.BuiltinCall{ - .base = Node{ .id = .BuiltinCall }, .builtin_token = token, .params = params.list, .rparen_token = params.rparen, @@ -2670,7 +2738,6 @@ fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = try arena.create(Node.ErrorTag); node.* = Node.ErrorTag{ - .base = Node{ .id = .ErrorTag }, .doc_comments = doc_comments, .name_token = token, }; @@ -2681,7 +2748,6 @@ fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Identifier) orelse return null; const node = try arena.create(Node.Identifier); node.* = Node.Identifier{ - .base = Node{ .id = .Identifier }, .token = token, }; return &node.base; @@ -2691,7 +2757,6 @@ fn parseVarType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_var) orelse return null; const node = try arena.create(Node.VarType); node.* = Node.VarType{ - .base = Node{ .id = .VarType }, .token = token, }; return &node.base; @@ -2706,21 +2771,24 @@ fn createLiteral(arena: *Allocator, comptime T: type, token: TokenIndex) !*Node return &result.base; } -// string literal or multiline string literal -fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { +fn parseStringLiteralSingle(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (eatToken(it, .StringLiteral)) |token| { const node = try arena.create(Node.StringLiteral); node.* = Node.StringLiteral{ - .base = Node{ .id = .StringLiteral }, .token = token, }; return &node.base; } + return null; +} + +// string literal or multiline string literal +fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { + if (try parseStringLiteralSingle(arena, it, tree)) |node| return node; if (eatToken(it, .MultilineStringLiteralLine)) |first_line| { const node = try arena.create(Node.MultilineStringLiteral); node.* = Node.MultilineStringLiteral{ - .base = Node{ .id = .MultilineStringLiteral }, .lines = Node.MultilineStringLiteral.LineList.init(arena), }; try node.lines.push(first_line); @@ -2737,7 +2805,6 @@ fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const token = eatToken(it, .IntegerLiteral) orelse return null; const node = try arena.create(Node.IntegerLiteral); node.* = Node.IntegerLiteral{ - .base = Node{ .id = .IntegerLiteral }, .token = token, }; return &node.base; @@ -2747,7 +2814,6 @@ fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const token = eatToken(it, .FloatLiteral) orelse return null; const node = try arena.create(Node.FloatLiteral); node.* = Node.FloatLiteral{ - .base = Node{ .id = .FloatLiteral }, .token = token, }; return &node.base; @@ -2757,7 +2823,6 @@ fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_try) orelse return null; const node = try arena.create(Node.PrefixOp); node.* = Node.PrefixOp{ - .base = Node{ .id = .PrefixOp }, .op_token = token, .op = Node.PrefixOp.Op.Try, .rhs = undefined, // set by caller @@ -2769,12 +2834,11 @@ fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_usingnamespace) orelse return null; const node = try arena.create(Node.Use); node.* = Node.Use{ - .base = Node{ .id = .Use }, .doc_comments = null, .visib_token = null, .use_token = token, - .expr = undefined, - .semicolon_token = undefined, + .expr = undefined, // set by caller + .semicolon_token = undefined, // set by caller }; return &node.base; } @@ -2795,7 +2859,6 @@ fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: Node }); const else_node = try arena.create(Node.Else); else_node.* = Node.Else{ - .base = Node{ .id = .Else }, .else_token = else_token, .payload = payload, .body = else_expr, @@ -2816,7 +2879,6 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D const node = try arena.create(Node.DocComment); node.* = Node.DocComment{ - .base = Node{ .id = .DocComment }, .lines = lines, }; return node; @@ -2828,7 +2890,6 @@ fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, a if (tree.tokensOnSameLine(after_token, comment_token)) { const node = try arena.create(Node.DocComment); node.* = Node.DocComment{ - .base = Node{ .id = .DocComment }, .lines = Node.DocComment.LineList.init(arena), }; try node.lines.push(comment_token); @@ -2935,11 +2996,10 @@ fn parseBinOpExpr( fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*Node { const node = try arena.create(Node.InfixOp); node.* = Node.InfixOp{ - .base = Node{ .id = .InfixOp }, .op_token = index, - .lhs = undefined, + .lhs = undefined, // set by caller .op = op, - .rhs = undefined, + .rhs = undefined, // set by caller }; return &node.base; } diff --git a/lib/std/zig/parse_string_literal.zig b/lib/std/zig/parse_string_literal.zig index acae0b64c..a6bdff4a0 100644 --- a/lib/std/zig/parse_string_literal.zig +++ b/lib/std/zig/parse_string_literal.zig @@ -19,7 +19,7 @@ pub fn parseStringLiteral( bytes: []const u8, bad_index: *usize, // populated if error.InvalidCharacter is returned ) ParseStringLiteralError![]u8 { - const first_index = if (bytes[0] == 'c') usize(2) else usize(1); + const first_index = if (bytes[0] == 'c') @as(usize, 2) else @as(usize, 1); assert(bytes[bytes.len - 1] == '"'); var list = std.ArrayList(u8).init(allocator); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2c7978ba8..c57540ade 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1,9 +1,134 @@ -// TODO remove `use` keyword eventually: https://github.com/ziglang/zig/issues/2591 -test "zig fmt: change use to usingnamespace" { +// TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348 +test "zig fmt: change @typeOf to @TypeOf" { try testTransform( - \\use @import("std"); + \\const a = @typeOf(@as(usize, 10)); + \\ , - \\usingnamespace @import("std"); + \\const a = @TypeOf(@as(usize, 10)); + \\ + ); +} + +// TODO: Remove nakedcc/stdcallcc once zig 0.6.0 is released. See https://github.com/ziglang/zig/pull/3977 +test "zig fmt: convert extern/nakedcc/stdcallcc into callconv(...)" { + try testTransform( + \\nakedcc fn foo1() void {} + \\stdcallcc fn foo2() void {} + \\extern fn foo3() void {} + , + \\fn foo1() callconv(.Naked) void {} + \\fn foo2() callconv(.Stdcall) void {} + \\fn foo3() callconv(.C) void {} + \\ + ); +} + +test "zig fmt: comptime struct field" { + try testCanonical( + \\const Foo = struct { + \\ a: i32, + \\ comptime b: i32 = 1234, + \\}; + \\ + ); +} + +test "zig fmt: c pointer type" { + try testCanonical( + \\pub extern fn repro() [*c]const u8; + \\ + ); +} + +test "zig fmt: builtin call with trailing comma" { + try testCanonical( + \\pub fn main() void { + \\ @breakpoint(); + \\ _ = @boolToInt(a); + \\ _ = @call( + \\ a, + \\ b, + \\ c, + \\ ); + \\} + \\ + ); +} + +test "zig fmt: asm expression with comptime content" { + try testCanonical( + \\comptime { + \\ asm ("foo" ++ "bar"); + \\} + \\pub fn main() void { + \\ asm volatile ("foo" ++ "bar"); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ ); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ : [_] "" (y) + \\ ); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ : [_] "" (y) + \\ : "h", "e", "l", "l", "o" + \\ ); + \\} + \\ + ); +} + +test "zig fmt: var struct field" { + try testCanonical( + \\pub const Pointer = struct { + \\ sentinel: var, + \\}; + \\ + ); +} + +test "zig fmt: sentinel-terminated array type" { + try testCanonical( + \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { + \\ return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); + \\} + \\ + ); +} + +test "zig fmt: sentinel-terminated slice type" { + try testCanonical( + \\pub fn toSlice(self: Buffer) [:0]u8 { + \\ return self.list.toSlice()[0..self.len()]; + \\} + \\ + ); +} + +test "zig fmt: anon literal in array" { + try testCanonical( + \\var arr: [2]Foo = .{ + \\ .{ .a = 2 }, + \\ .{ .b = 3 }, + \\}; + \\ + ); +} + +test "zig fmt: anon struct literal syntax" { + try testCanonical( + \\const x = .{ + \\ .a = b, + \\ .c = d, + \\}; + \\ + ); +} + +test "zig fmt: anon list literal syntax" { + try testCanonical( + \\const x = .{ a, b, c }; \\ ); } @@ -37,7 +162,7 @@ test "zig fmt: while else err prong with no block" { \\test "" { \\ const result = while (returnError()) |value| { \\ break value; - \\ } else |err| i32(2); + \\ } else |err| @as(i32, 2); \\ expect(result == 2); \\} \\ @@ -123,7 +248,7 @@ test "zig fmt: threadlocal" { test "zig fmt: linksection" { try testCanonical( \\export var aoeu: u64 linksection(".text.derp") = 1234; - \\export nakedcc fn _start() linksection(".text.boot") noreturn {} + \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {} \\ ); } @@ -173,6 +298,12 @@ test "zig fmt: aligned struct field" { \\}; \\ ); + try testCanonical( + \\pub const S = struct { + \\ f: i32 align(32) = 1, + \\}; + \\ + ); } test "zig fmt: preserve space between async fn definitions" { @@ -317,10 +448,13 @@ test "zig fmt: pointer of unknown length" { test "zig fmt: spaces around slice operator" { try testCanonical( \\var a = b[c..d]; + \\var a = b[c..d :0]; \\var a = b[c + 1 .. d]; \\var a = b[c + 1 ..]; \\var a = b[c .. d + 1]; + \\var a = b[c .. d + 1 :0]; \\var a = b[c.a..d.e]; + \\var a = b[c.a..d.e :0]; \\ ); } @@ -551,15 +685,6 @@ test "zig fmt: fn decl with trailing comma" { ); } -test "zig fmt: var_args with trailing comma" { - try testCanonical( - \\pub fn add( - \\ a: ..., - \\) void {} - \\ - ); -} - test "zig fmt: enum decl with no trailing comma" { try testTransform( \\const StrLitKind = enum {Normal, C}; @@ -978,7 +1103,7 @@ test "zig fmt: line comment after doc comment" { test "zig fmt: float literal with exponent" { try testCanonical( \\test "bit field alignment" { - \\ assert(@typeOf(&blah.b) == *align(1:3:6) const u3); + \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3); \\} \\ ); @@ -1438,11 +1563,11 @@ test "zig fmt: preserve spacing" { \\const std = @import("std"); \\ \\pub fn main() !void { - \\ var stdout_file = try std.io.getStdOut; - \\ var stdout_file = try std.io.getStdOut; + \\ var stdout_file = std.io.getStdOut; + \\ var stdout_file = std.io.getStdOut; \\ - \\ var stdout_file = try std.io.getStdOut; - \\ var stdout_file = try std.io.getStdOut; + \\ var stdout_file = std.io.getStdOut; + \\ var stdout_file = std.io.getStdOut; \\} \\ ); @@ -1529,6 +1654,7 @@ test "zig fmt: pointer attributes" { \\extern fn f2(s: **align(1) *const *volatile u8) c_int; \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; \\extern fn f4(s: *align(1) const volatile u8) c_int; + \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int; \\ ); } @@ -1539,6 +1665,7 @@ test "zig fmt: slice attributes" { \\extern fn f2(s: **align(1) *const *volatile u8) c_int; \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; \\extern fn f4(s: *align(1) const volatile u8) c_int; + \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int; \\ ); } @@ -1657,13 +1784,6 @@ test "zig fmt: call expression" { ); } -test "zig fmt: var args" { - try testCanonical( - \\fn print(args: ...) void {} - \\ - ); -} - test "zig fmt: var type" { try testCanonical( \\fn print(args: var) var {} @@ -1700,11 +1820,6 @@ test "zig fmt: multiline string" { \\ \\two) \\ \\three \\ ; - \\ const s2 = - \\ c\\one - \\ c\\two) - \\ c\\three - \\ ; \\ const s3 = // hi \\ \\one \\ \\two) @@ -1721,7 +1836,6 @@ test "zig fmt: values" { \\ 1; \\ 1.0; \\ "string"; - \\ c"cstring"; \\ 'c'; \\ true; \\ false; @@ -1760,7 +1874,7 @@ test "zig fmt: struct declaration" { \\const S = struct { \\ const Self = @This(); \\ f1: u8, - \\ pub f3: u8, + \\ f3: u8, \\ \\ fn method(self: *Self) Self { \\ return self.*; @@ -1771,14 +1885,14 @@ test "zig fmt: struct declaration" { \\ \\const Ps = packed struct { \\ a: u8, - \\ pub b: u8, + \\ b: u8, \\ \\ c: u8, \\}; \\ \\const Es = extern struct { \\ a: u8, - \\ pub b: u8, + \\ b: u8, \\ \\ c: u8, \\}; @@ -1866,6 +1980,7 @@ test "zig fmt: arrays" { \\ 2, \\ }; \\ const a: [0]u8 = []u8{}; + \\ const x: [4:0]u8 = undefined; \\} \\ ); @@ -2225,7 +2340,7 @@ test "zig fmt: fn type" { \\ \\const a: fn (u8) u8 = undefined; \\const b: extern fn (u8) u8 = undefined; - \\const c: nakedcc fn (u8) u8 = undefined; + \\const c: fn (u8) callconv(.Naked) u8 = undefined; \\const ap: fn (u8) u8 = a; \\ ); @@ -2521,7 +2636,7 @@ test "zig fmt: comments at several places in struct init" { try testTransform( \\var bar = Bar{ \\ .x = 10, // test - \\ .y = "test" + \\ .y = "test" \\ // test \\}; \\ @@ -2543,6 +2658,69 @@ test "zig fmt: comments at several places in struct init" { ); } +test "zig fmt: top level doc comments" { + try testCanonical( + \\//! tld 1 + \\//! tld 2 + \\//! tld 3 + \\ + \\// comment + \\ + \\/// A doc + \\const A = struct { + \\ //! A tld 1 + \\ //! A tld 2 + \\ //! A tld 3 + \\}; + \\ + \\/// B doc + \\const B = struct { + \\ //! B tld 1 + \\ //! B tld 2 + \\ //! B tld 3 + \\ + \\ /// b doc + \\ b: u32, + \\}; + \\ + \\/// C doc + \\const C = struct { + \\ //! C tld 1 + \\ //! C tld 2 + \\ //! C tld 3 + \\ + \\ /// c1 doc + \\ c1: u32, + \\ + \\ //! C tld 4 + \\ //! C tld 5 + \\ //! C tld 6 + \\ + \\ /// c2 doc + \\ c2: u32, + \\}; + \\ + ); + try testCanonical( + \\//! Top-level documentation. + \\ + \\/// This is A + \\pub const A = usize; + \\ + ); + try testCanonical( + \\//! Nothing here + \\ + ); +} + +test "zig fmt: extern without container keyword returns error" { + try testError( + \\const container = extern {}; + \\ + ); +} + const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; @@ -2552,8 +2730,7 @@ const maxInt = std.math.maxInt; var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { - var stderr_file = try io.getStdErr(); - var stderr = &stderr_file.outStream().stream; + const stderr = &io.getStdErr().outStream().stream; const tree = try std.zig.parse(allocator, source); defer tree.deinit(); @@ -2562,9 +2739,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b while (error_it.next()) |parse_error| { const token = tree.tokens.at(parse_error.loc()); const loc = tree.tokenLocation(0, parse_error.loc()); - try stderr.print("(memory buffer):{}:{}: error: ", loc.line + 1, loc.column + 1); + try stderr.print("(memory buffer):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 }); try tree.renderError(parse_error, stderr); - try stderr.print("\n{}\n", source[loc.line_start..loc.line_end]); + try stderr.print("\n{}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { @@ -2600,16 +2777,16 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { var anything_changed: bool = undefined; const result_source = try testParse(source, &failing_allocator.allocator, &anything_changed); if (!mem.eql(u8, result_source, expected_source)) { - warn("\n====== expected this output: =========\n"); - warn("{}", expected_source); - warn("\n======== instead found this: =========\n"); - warn("{}", result_source); - warn("\n======================================\n"); + warn("\n====== expected this output: =========\n", .{}); + warn("{}", .{expected_source}); + warn("\n======== instead found this: =========\n", .{}); + warn("{}", .{result_source}); + warn("\n======================================\n", .{}); return error.TestFailed; } const changes_expected = source.ptr != expected_source.ptr; if (anything_changed != changes_expected) { - warn("std.zig.render returned {} instead of {}\n", anything_changed, changes_expected); + warn("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); return error.TestFailed; } std.testing.expect(anything_changed == changes_expected); @@ -2629,12 +2806,14 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { warn( "\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", - fail_index, - needed_alloc_count, - failing_allocator.allocated_bytes, - failing_allocator.freed_bytes, - failing_allocator.allocations, - failing_allocator.deallocations, + .{ + fail_index, + needed_alloc_count, + failing_allocator.allocated_bytes, + failing_allocator.freed_bytes, + failing_allocator.allocations, + failing_allocator.deallocations, + }, ); return error.MemoryLeakDetected; } @@ -2648,3 +2827,11 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { fn testCanonical(source: []const u8) !void { return testTransform(source, source); } + +fn testError(source: []const u8) !void { + var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + const tree = try std.zig.parse(&fixed_allocator.allocator, source); + defer tree.deinit(); + + std.testing.expect(tree.errors.len != 0); +} diff --git a/lib/std/zig/perf_test.zig b/lib/std/zig/perf_test.zig index e0723d851..fe9d35fa4 100644 --- a/lib/std/zig/perf_test.zig +++ b/lib/std/zig/perf_test.zig @@ -23,9 +23,9 @@ pub fn main() !void { const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s; const mb_per_sec = bytes_per_sec / (1024 * 1024); - var stdout_file = try std.io.getStdOut(); + var stdout_file = std.io.getStdOut(); const stdout = &stdout_file.outStream().stream; - try stdout.print("{:.3} MiB/s, {} KiB used \n", mb_per_sec, memory_used / 1024); + try stdout.print("{:.3} MiB/s, {} KiB used \n", .{mb_per_sec, memory_used / 1024}); } fn testOnce() usize { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 6268a056f..f2c64a0e4 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -13,19 +13,19 @@ pub const Error = error{ }; /// Returns whether anything changed -pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { - comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); +pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Child.Error || Error)!bool { + comptime assert(@typeId(@TypeOf(stream)) == builtin.TypeId.Pointer); var anything_changed: bool = false; // make a passthrough stream that checks whether something changed const MyStream = struct { const MyStream = @This(); - const StreamError = @typeOf(stream).Child.Error; + const StreamError = @TypeOf(stream).Child.Error; const Stream = std.io.OutStream(StreamError); anything_changed_ptr: *bool, - child_stream: @typeOf(stream), + child_stream: @TypeOf(stream), stream: Stream, source_index: usize, source: []const u8, @@ -70,13 +70,13 @@ fn renderRoot( allocator: *mem.Allocator, stream: var, tree: *ast.Tree, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { var tok_it = tree.tokens.iterator(0); // render all the line comments at the beginning of the file while (tok_it.next()) |token| { - if (token.id != Token.Id.LineComment) break; - try stream.print("{}\n", mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); + if (token.id != .LineComment) break; + try stream.print("{}\n", .{mem.trimRight(u8, tree.tokenSlicePtr(token), " ")}); if (tok_it.peek()) |next_token| { const loc = tree.tokenLocationPtr(token.end, next_token); if (loc.line >= 2) { @@ -109,8 +109,8 @@ fn renderRoot( token_index -= 1; const token = tree.tokens.at(token_index); switch (token.id) { - Token.Id.LineComment => {}, - Token.Id.DocComment => { + .LineComment => {}, + .DocComment => { copy_start_token_index = token_index; continue; }, @@ -149,8 +149,8 @@ fn renderRoot( while (token_index < decl_first_token_index) : (token_index += 1) { const token = tree.tokens.at(token_index); switch (token.id) { - Token.Id.LineComment => {}, - Token.Id.Eof => unreachable, + .LineComment => {}, + .Eof => unreachable, else => continue, } if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) { @@ -169,8 +169,8 @@ fn renderRoot( token_index -= 1; const token = tree.tokens.at(token_index); switch (token.id) { - Token.Id.LineComment => {}, - Token.Id.DocComment => { + .LineComment => {}, + .DocComment => { copy_end_token_index = token_index; continue; }, @@ -190,10 +190,11 @@ fn renderRoot( } } -fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @typeOf(stream).Child.Error!void { +fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Child.Error!void { const first_token = node.firstToken(); var prev_token = first_token; - while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) { + if (prev_token == 0) return; + while (tree.tokens.at(prev_token - 1).id == .DocComment) { prev_token -= 1; } const prev_token_end = tree.tokens.at(prev_token - 1).end; @@ -204,9 +205,9 @@ fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *as } } -fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Child.Error || Error)!void { switch (decl.id) { - ast.Node.Id.FnProto => { + .FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); try renderDocComments(tree, stream, fn_proto, indent, start_col); @@ -220,27 +221,25 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i } }, - ast.Node.Id.Use => { + .Use => { const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); if (use_decl.visib_token) |visib_token| { try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } - // TODO after depracating use, go back to this: - //try renderToken(tree, stream, use_decl.use_token, indent, start_col, Space.Space); // usingnamespace - try stream.write("usingnamespace "); + try renderToken(tree, stream, use_decl.use_token, indent, start_col, Space.Space); // usingnamespace try renderExpression(allocator, stream, tree, indent, start_col, use_decl.expr, Space.None); try renderToken(tree, stream, use_decl.semicolon_token, indent, start_col, Space.Newline); // ; }, - ast.Node.Id.VarDecl => { + .VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); try renderDocComments(tree, stream, var_decl, indent, start_col); try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl); }, - ast.Node.Id.TestDecl => { + .TestDecl => { const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); try renderDocComments(tree, stream, test_decl, indent, start_col); @@ -249,13 +248,12 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, Space.Newline); }, - ast.Node.Id.ContainerField => { + .ContainerField => { const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl); try renderDocComments(tree, stream, field, indent, start_col); - - if (field.visib_token) |visib_token| { - try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub + if (field.comptime_token) |t| { + try renderToken(tree, stream, t, indent, start_col, Space.Space); // comptime } if (field.type_expr == null and field.value_expr == null) { @@ -272,7 +270,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, Space.None); // alignment - try renderToken(tree, stream, rparen_token, indent, start_col, Space.Comma); // ) + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Comma); // ), } else { try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, Space.Comma); // type, } @@ -283,16 +281,39 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i } else { try renderToken(tree, stream, field.name_token, indent, start_col, Space.None); // name try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, Space.Space); // : - try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, Space.Space); // type - try renderToken(tree, stream, tree.nextToken(field.type_expr.?.lastToken()), indent, start_col, Space.Space); // = + + if (field.align_expr) |align_value_expr| { + try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, Space.Space); // type + const lparen_token = tree.prevToken(align_value_expr.firstToken()); + const align_kw = tree.prevToken(lparen_token); + const rparen_token = tree.nextToken(align_value_expr.lastToken()); + try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, Space.None); // alignment + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) + } else { + try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, Space.Space); // type + } + try renderToken(tree, stream, tree.prevToken(field.value_expr.?.firstToken()), indent, start_col, Space.Space); // = return renderExpression(allocator, stream, tree, indent, start_col, field.value_expr.?, Space.Comma); // value, } }, - ast.Node.Id.Comptime => { + .Comptime => { assert(!decl.requireSemiColon()); try renderExpression(allocator, stream, tree, indent, start_col, decl, Space.Newline); }, + + .DocComment => { + const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl); + var it = comment.lines.iterator(0); + while (it.next()) |line_token_index| { + try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.Newline); + if (it.peek()) |_| { + try stream.writeByteNTimes(' ', indent); + } + } + }, else => unreachable, } } @@ -305,13 +326,13 @@ fn renderExpression( start_col: *usize, base: *ast.Node, space: Space, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { switch (base.id) { - ast.Node.Id.Identifier => { + .Identifier => { const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); return renderToken(tree, stream, identifier.token, indent, start_col, space); }, - ast.Node.Id.Block => { + .Block => { const block = @fieldParentPtr(ast.Node.Block, "base", base); if (block.label) |label| { @@ -340,20 +361,20 @@ fn renderExpression( return renderToken(tree, stream, block.rbrace, indent, start_col, space); } }, - ast.Node.Id.Defer => { + .Defer => { const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space); }, - ast.Node.Id.Comptime => { + .Comptime => { const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space); }, - ast.Node.Id.Suspend => { + .Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); if (suspend_node.body) |body| { @@ -364,7 +385,7 @@ fn renderExpression( } }, - ast.Node.Id.InfixOp => { + .InfixOp => { const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); const op_space = switch (infix_op_node.op) { @@ -380,7 +401,7 @@ fn renderExpression( try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space); if (after_op_space == Space.Newline and - tree.tokens.at(tree.nextToken(infix_op_node.op_token)).id != Token.Id.MultilineStringLiteralLine) + tree.tokens.at(tree.nextToken(infix_op_node.op_token)).id != .MultilineStringLiteralLine) { try stream.writeByteNTimes(' ', indent + indent_delta); start_col.* = indent + indent_delta; @@ -396,16 +417,34 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space); }, - ast.Node.Id.PrefixOp => { + .PrefixOp => { const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.PtrType => |ptr_info| { - const star_offset = switch (tree.tokens.at(prefix_op_node.op_token).id) { - Token.Id.AsteriskAsterisk => usize(1), - else => usize(0), - }; - try renderTokenOffset(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None, star_offset); // * + .PtrType => |ptr_info| { + const op_tok_id = tree.tokens.at(prefix_op_node.op_token).id; + switch (op_tok_id) { + .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'), + .LBracket => if (tree.tokens.at(prefix_op_node.op_token + 2).id == .Identifier) + try stream.write("[*c") + else + try stream.write("[*"), + else => unreachable, + } + if (ptr_info.sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + const sentinel_space = switch (op_tok_id) { + .LBracket => Space.None, + else => Space.Space, + }; + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space); + } + switch (op_tok_id) { + .Asterisk, .AsteriskAsterisk => {}, + .LBracket => try stream.writeByte(']'), + else => unreachable, + } if (ptr_info.allowzero_token) |allowzero_token| { try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero } @@ -442,9 +481,16 @@ fn renderExpression( } }, - ast.Node.PrefixOp.Op.SliceType => |ptr_info| { + .SliceType => |ptr_info| { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ - try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] + if (ptr_info.sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); + try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ] + } else { + try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] + } if (ptr_info.allowzero_token) |allowzero_token| { try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero @@ -482,9 +528,12 @@ fn renderExpression( } }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { + .ArrayType => |array_info| { const lbracket = prefix_op_node.op_token; - const rbracket = tree.nextToken(array_index.lastToken()); + const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel| + sentinel.lastToken() + else + array_info.len_expr.lastToken()); try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ @@ -492,29 +541,34 @@ fn renderExpression( const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment; const new_indent = if (ends_with_comment) indent + indent_delta else indent; const new_space = if (ends_with_comment) Space.Newline else Space.None; - try renderExpression(allocator, stream, tree, new_indent, start_col, array_index, new_space); + try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space); if (starts_with_comment) { try stream.writeByte('\n'); } if (ends_with_comment or starts_with_comment) { try stream.writeByteNTimes(' ', indent); } + if (array_info.sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); + } try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] }, - ast.Node.PrefixOp.Op.BitNot, - ast.Node.PrefixOp.Op.BoolNot, - ast.Node.PrefixOp.Op.Negation, - ast.Node.PrefixOp.Op.NegationWrap, - ast.Node.PrefixOp.Op.OptionalType, - ast.Node.PrefixOp.Op.AddressOf, + .BitNot, + .BoolNot, + .Negation, + .NegationWrap, + .OptionalType, + .AddressOf, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); }, - ast.Node.PrefixOp.Op.Try, - ast.Node.PrefixOp.Op.Await, - ast.Node.PrefixOp.Op.Cancel, - ast.Node.PrefixOp.Op.Resume, + .Try, + .Await, + .Cancel, + .Resume, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); }, @@ -523,18 +577,18 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space); }, - ast.Node.Id.SuffixOp => { + .SuffixOp => { const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); switch (suffix_op.op) { - @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { + .Call => |*call_info| { if (call_info.async_token) |async_token| { try renderToken(tree, stream, async_token, indent, start_col, Space.Space); } - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None); - const lparen = tree.nextToken(suffix_op.lhs.lastToken()); + const lparen = tree.nextToken(suffix_op.lhs.node.lastToken()); if (call_info.params.len == 0) { try renderToken(tree, stream, lparen, indent, start_col, Space.None); @@ -543,7 +597,7 @@ fn renderExpression( const src_has_trailing_comma = blk: { const maybe_comma = tree.prevToken(suffix_op.rtoken); - break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + break :blk tree.tokens.at(maybe_comma).id == .Comma; }; if (src_has_trailing_comma) { @@ -554,7 +608,7 @@ fn renderExpression( while (true) { const param_node = it.next().?; - const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: { + const param_node_new_indent = if (param_node.*.id == .MultilineStringLiteral) blk: { break :blk indent; } else blk: { try stream.writeByteNTimes(' ', new_indent); @@ -588,11 +642,11 @@ fn renderExpression( return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); }, - ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - const lbracket = tree.prevToken(index_expr.firstToken()); + .ArrayAccess => |index_expr| { + const lbracket = tree.nextToken(suffix_op.lhs.node.lastToken()); const rbracket = tree.nextToken(index_expr.lastToken()); - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None); try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment; @@ -609,14 +663,19 @@ fn renderExpression( return renderToken(tree, stream, rbracket, indent, start_col, space); // ] }, - ast.Node.SuffixOp.Op.Deref, ast.Node.SuffixOp.Op.UnwrapOptional => { - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); - try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . - return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * or ? + .Deref => { + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .* }, - @TagType(ast.Node.SuffixOp.Op).Slice => |range| { - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + .UnwrapOptional => { + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None); + try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ? + }, + + .Slice => |range| { + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None); const lbracket = tree.prevToken(range.start.firstToken()); const dotdot = tree.nextToken(range.start.lastToken()); @@ -630,23 +689,35 @@ fn renderExpression( try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space); try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // .. if (range.end) |end| { - try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None); + const after_end_space = if (range.sentinel != null) Space.Space else Space.None; + try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space); + } + if (range.sentinel) |sentinel| { + const colon = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); } return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ] }, - ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { - const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); + .StructInitializer => |*field_inits| { + const lbrace = switch (suffix_op.lhs) { + .dot => |dot| tree.nextToken(dot), + .node => |node| tree.nextToken(node.lastToken()), + }; if (field_inits.len == 0) { - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } const src_has_trailing_comma = blk: { const maybe_comma = tree.prevToken(suffix_op.rtoken); - break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + break :blk tree.tokens.at(maybe_comma).id == .Comma; }; const src_same_line = blk: { @@ -670,7 +741,7 @@ fn renderExpression( const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?; if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| { - if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) { + if (nested_suffix_op.op == .StructInitializer) { break :blk; } } @@ -680,7 +751,10 @@ fn renderExpression( break :blk; } - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); @@ -688,7 +762,10 @@ fn renderExpression( if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) { // render all on one line, no trailing comma - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); var it = field_inits.iterator(0); @@ -708,7 +785,10 @@ fn renderExpression( const new_indent = indent + indent_delta; - try renderExpression(allocator, stream, tree, new_indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); var it = field_inits.iterator(0); @@ -731,24 +811,36 @@ fn renderExpression( return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); }, - ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { - const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); + .ArrayInitializer => |*exprs| { + const lbrace = switch (suffix_op.lhs) { + .dot => |dot| tree.nextToken(dot), + .node => |node| tree.nextToken(node.lastToken()), + }; if (exprs.len == 0) { - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, indent, start_col, Space.None); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } if (exprs.len == 1 and tree.tokens.at(exprs.at(0).*.lastToken() + 1).id == .RBrace) { const expr = exprs.at(0).*; - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } try renderToken(tree, stream, lbrace, indent, start_col, Space.None); try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } - try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + switch (suffix_op.lhs) { + .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None), + .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None), + } // scan to find row size const maybe_row_size: ?usize = blk: { @@ -768,7 +860,7 @@ fn renderExpression( // all on one line const src_has_trailing_comma = trailblk: { const maybe_comma = tree.prevToken(suffix_op.rtoken); - break :trailblk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + break :trailblk tree.tokens.at(maybe_comma).id == .Comma; }; if (src_has_trailing_comma) { break :blk 1; // force row size 1 @@ -809,7 +901,7 @@ fn renderExpression( var new_indent = indent + indent_delta; - if (tree.tokens.at(tree.nextToken(lbrace)).id != Token.Id.MultilineStringLiteralLine) { + if (tree.tokens.at(tree.nextToken(lbrace)).id != .MultilineStringLiteralLine) { try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); try stream.writeByteNTimes(' ', new_indent); } else { @@ -837,14 +929,14 @@ fn renderExpression( } col = 1; - if (tree.tokens.at(tree.nextToken(comma)).id != Token.Id.MultilineStringLiteralLine) { + if (tree.tokens.at(tree.nextToken(comma)).id != .MultilineStringLiteralLine) { try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // , } else { try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // , } try renderExtraNewline(tree, stream, start_col, next_expr.*); - if (next_expr.*.id != ast.Node.Id.MultilineStringLiteral) { + if (next_expr.*.id != .MultilineStringLiteral) { try stream.writeByteNTimes(' ', new_indent); } } else { @@ -852,7 +944,7 @@ fn renderExpression( } } const last_node = it.prev().?; - if (last_node.*.id != ast.Node.Id.MultilineStringLiteral) { + if (last_node.*.id != .MultilineStringLiteral) { try stream.writeByteNTimes(' ', indent); } return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); @@ -875,11 +967,11 @@ fn renderExpression( } }, - ast.Node.Id.ControlFlowExpression => { + .ControlFlowExpression => { const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); switch (flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { + .Break => |maybe_label| { if (maybe_label == null and flow_expr.rhs == null) { return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break } @@ -895,7 +987,7 @@ fn renderExpression( try renderExpression(allocator, stream, tree, indent, start_col, label, Space.Space); // label } }, - ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { + .Continue => |maybe_label| { assert(flow_expr.rhs == null); if (maybe_label == null and flow_expr.rhs == null) { @@ -910,7 +1002,7 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, label, space); } }, - ast.Node.ControlFlowExpression.Kind.Return => { + .Return => { if (flow_expr.rhs == null) { return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); } @@ -921,7 +1013,7 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space); }, - ast.Node.Id.Payload => { + .Payload => { const payload = @fieldParentPtr(ast.Node.Payload, "base", base); try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); @@ -929,7 +1021,7 @@ fn renderExpression( return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, - ast.Node.Id.PointerPayload => { + .PointerPayload => { const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); @@ -940,7 +1032,7 @@ fn renderExpression( return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, - ast.Node.Id.PointerIndexPayload => { + .PointerIndexPayload => { const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); @@ -959,7 +1051,7 @@ fn renderExpression( return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, - ast.Node.Id.GroupedExpression => { + .GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); try renderToken(tree, stream, grouped_expr.lparen, indent, start_col, Space.None); @@ -967,7 +1059,7 @@ fn renderExpression( return renderToken(tree, stream, grouped_expr.rparen, indent, start_col, space); }, - ast.Node.Id.FieldInitializer => { + .FieldInitializer => { const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); try renderToken(tree, stream, field_init.period_token, indent, start_col, Space.None); // . @@ -976,43 +1068,43 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space); }, - ast.Node.Id.IntegerLiteral => { + .IntegerLiteral => { const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); return renderToken(tree, stream, integer_literal.token, indent, start_col, space); }, - ast.Node.Id.FloatLiteral => { + .FloatLiteral => { const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); return renderToken(tree, stream, float_literal.token, indent, start_col, space); }, - ast.Node.Id.StringLiteral => { + .StringLiteral => { const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); return renderToken(tree, stream, string_literal.token, indent, start_col, space); }, - ast.Node.Id.CharLiteral => { + .CharLiteral => { const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); return renderToken(tree, stream, char_literal.token, indent, start_col, space); }, - ast.Node.Id.BoolLiteral => { + .BoolLiteral => { const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); return renderToken(tree, stream, bool_literal.token, indent, start_col, space); }, - ast.Node.Id.NullLiteral => { + .NullLiteral => { const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); return renderToken(tree, stream, null_literal.token, indent, start_col, space); }, - ast.Node.Id.Unreachable => { + .Unreachable => { const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); return renderToken(tree, stream, unreachable_node.token, indent, start_col, space); }, - ast.Node.Id.ErrorType => { + .ErrorType => { const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); return renderToken(tree, stream, error_type.token, indent, start_col, space); }, - ast.Node.Id.VarType => { + .VarType => { const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); return renderToken(tree, stream, var_type.token, indent, start_col, space); }, - ast.Node.Id.ContainerDecl => { + .ContainerDecl => { const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); if (container_decl.layout_token) |layout_token| { @@ -1077,7 +1169,7 @@ fn renderExpression( } }, - ast.Node.Id.ErrorSetDecl => { + .ErrorSetDecl => { const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); const lbrace = tree.nextToken(err_set_decl.error_token); @@ -1127,21 +1219,21 @@ fn renderExpression( return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } }, - ast.Node.Id.ErrorTag => { + .ErrorTag => { const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base); try renderDocComments(tree, stream, tag, indent, start_col); return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name }, - ast.Node.Id.MultilineStringLiteral => { + .MultilineStringLiteral => { // TODO: Don't indent in this function, but let the caller indent. // If this has been implemented, a lot of hacky solutions in i.e. ArrayInit and FunctionCall can be removed const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); var skip_first_indent = true; - if (tree.tokens.at(multiline_str_literal.firstToken() - 1).id != Token.Id.LineComment) { - try stream.print("\n"); + if (tree.tokens.at(multiline_str_literal.firstToken() - 1).id != .LineComment) { + try stream.print("\n", .{}); skip_first_indent = false; } @@ -1156,41 +1248,79 @@ fn renderExpression( } try stream.writeByteNTimes(' ', indent); }, - ast.Node.Id.UndefinedLiteral => { + .UndefinedLiteral => { const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); return renderToken(tree, stream, undefined_literal.token, indent, start_col, space); }, - ast.Node.Id.BuiltinCall => { + .BuiltinCall => { const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name - try renderToken(tree, stream, tree.nextToken(builtin_call.builtin_token), indent, start_col, Space.None); // ( - - var it = builtin_call.params.iterator(0); - while (it.next()) |param_node| { - try renderExpression(allocator, stream, tree, indent, start_col, param_node.*, Space.None); - - if (it.peek() != null) { - const comma_token = tree.nextToken(param_node.*.lastToken()); - try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // , - } + // TODO: Remove condition after deprecating 'typeOf'. See https://github.com/ziglang/zig/issues/1348 + if (mem.eql(u8, tree.tokenSlicePtr(tree.tokens.at(builtin_call.builtin_token)), "@typeOf")) { + try stream.write("@TypeOf"); + } else { + try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name } + + const src_params_trailing_comma = blk: { + if (builtin_call.params.len < 2) break :blk false; + const last_node = builtin_call.params.at(builtin_call.params.len - 1).*; + const maybe_comma = tree.nextToken(last_node.lastToken()); + break :blk tree.tokens.at(maybe_comma).id == .Comma; + }; + + const lparen = tree.nextToken(builtin_call.builtin_token); + + if (!src_params_trailing_comma) { + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( + + // render all on one line, no trailing comma + var it = builtin_call.params.iterator(0); + while (it.next()) |param_node| { + try renderExpression(allocator, stream, tree, indent, start_col, param_node.*, Space.None); + + if (it.peek() != null) { + const comma_token = tree.nextToken(param_node.*.lastToken()); + try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // , + } + } + } else { + // one param per line + const new_indent = indent + indent_delta; + try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // ( + + var it = builtin_call.params.iterator(0); + while (it.next()) |param_node| { + try stream.writeByteNTimes(' ', new_indent); + try renderExpression(allocator, stream, tree, indent, start_col, param_node.*, Space.Comma); + } + try stream.writeByteNTimes(' ', indent); + } + return renderToken(tree, stream, builtin_call.rparen_token, indent, start_col, space); // ) }, - ast.Node.Id.FnProto => { + .FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); if (fn_proto.visib_token) |visib_token_index| { const visib_token = tree.tokens.at(visib_token_index); - assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); + assert(visib_token.id == .Keyword_pub or visib_token.id == .Keyword_export); try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub } + // Some extra machinery is needed to rewrite the old-style cc + // notation to the new callconv one + var cc_rewrite_str: ?[*:0]const u8 = null; if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export + const tok = tree.tokens.at(extern_export_inline_token); + if (tok.id != .Keyword_extern or fn_proto.body_node == null) { + try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export + } else { + cc_rewrite_str = ".C"; + } } if (fn_proto.lib_name) |lib_name| { @@ -1198,7 +1328,12 @@ fn renderExpression( } if (fn_proto.cc_token) |cc_token| { - try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc + var str = tree.tokenSlicePtr(tree.tokens.at(cc_token)); + if (mem.eql(u8, str, "stdcallcc")) { + cc_rewrite_str = ".Stdcall"; + } else if (mem.eql(u8, str, "nakedcc")) { + cc_rewrite_str = ".Naked"; + } else try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc } const lparen = if (fn_proto.name_token) |name_token| blk: { @@ -1270,6 +1405,21 @@ fn renderExpression( try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // ) } + if (fn_proto.callconv_expr) |callconv_expr| { + const callconv_rparen = tree.nextToken(callconv_expr.lastToken()); + const callconv_lparen = tree.prevToken(callconv_expr.firstToken()); + const callconv_kw = tree.prevToken(callconv_lparen); + + try renderToken(tree, stream, callconv_kw, indent, start_col, Space.None); // callconv + try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None); + try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // ) + } else if (cc_rewrite_str) |str| { + try stream.write("callconv("); + try stream.write(mem.toSliceConst(u8, str)); + try stream.write(") "); + } + switch (fn_proto.return_type) { ast.Node.FnProto.ReturnType.Explicit => |node| { return renderExpression(allocator, stream, tree, indent, start_col, node, space); @@ -1281,7 +1431,7 @@ fn renderExpression( } }, - ast.Node.Id.AnyFrameType => { + .AnyFrameType => { const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base); if (anyframe_type.result) |result| { @@ -1293,9 +1443,9 @@ fn renderExpression( } }, - ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes + .DocComment => unreachable, // doc comments are attached to nodes - ast.Node.Id.Switch => { + .Switch => { const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); try renderToken(tree, stream, switch_node.switch_token, indent, start_col, Space.Space); // switch @@ -1332,14 +1482,14 @@ fn renderExpression( return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // } }, - ast.Node.Id.SwitchCase => { + .SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); assert(switch_case.items.len != 0); const src_has_trailing_comma = blk: { const last_node = switch_case.items.at(switch_case.items.len - 1).*; const maybe_comma = tree.nextToken(last_node.lastToken()); - break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + break :blk tree.tokens.at(maybe_comma).id == .Comma; }; if (switch_case.items.len == 1 or !src_has_trailing_comma) { @@ -1382,11 +1532,11 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, switch_case.expr, space); }, - ast.Node.Id.SwitchElse => { + .SwitchElse => { const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); return renderToken(tree, stream, switch_else.token, indent, start_col, space); }, - ast.Node.Id.Else => { + .Else => { const else_node = @fieldParentPtr(ast.Node.Else, "base", base); const body_is_block = nodeIsBlock(else_node.body); @@ -1409,7 +1559,7 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, else_node.body, space); }, - ast.Node.Id.While => { + .While => { const while_node = @fieldParentPtr(ast.Node.While, "base", base); if (while_node.label) |label| { @@ -1484,7 +1634,7 @@ fn renderExpression( } }, - ast.Node.Id.For => { + .For => { const for_node = @fieldParentPtr(ast.Node.For, "base", base); if (for_node.label) |label| { @@ -1502,7 +1652,7 @@ fn renderExpression( const rparen = tree.nextToken(for_node.array_expr.lastToken()); - const body_is_block = for_node.body.id == ast.Node.Id.Block; + const body_is_block = for_node.body.id == .Block; const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken()); const body_on_same_line = body_is_block or src_one_line_to_body; @@ -1534,10 +1684,10 @@ fn renderExpression( } }, - ast.Node.Id.If => { + .If => { const if_node = @fieldParentPtr(ast.Node.If, "base", base); - const lparen = tree.prevToken(if_node.condition.firstToken()); + const lparen = tree.nextToken(if_node.if_token); const rparen = tree.nextToken(if_node.condition.lastToken()); try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if @@ -1545,7 +1695,7 @@ fn renderExpression( try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition - const body_is_if_block = if_node.body.id == ast.Node.Id.If; + const body_is_if_block = if_node.body.id == .If; const body_is_block = nodeIsBlock(if_node.body); if (body_is_if_block) { @@ -1628,7 +1778,7 @@ fn renderExpression( } }, - ast.Node.Id.Asm => { + .Asm => { const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); try renderToken(tree, stream, asm_node.asm_token, indent, start_col, Space.Space); // asm @@ -1649,7 +1799,7 @@ fn renderExpression( const indent_once = indent + indent_delta; - if (asm_node.template.id == ast.Node.Id.MultilineStringLiteral) { + if (asm_node.template.id == .MultilineStringLiteral) { // After rendering a multiline string literal the cursor is // already offset by indent try stream.writeByteNTimes(' ', indent_delta); @@ -1691,7 +1841,7 @@ fn renderExpression( try stream.writeByteNTimes(' ', indent_once); const comma_or_colon = tree.nextToken(node.lastToken()); break :blk switch (tree.tokens.at(comma_or_colon).id) { - Token.Id.Comma => tree.nextToken(comma_or_colon), + .Comma => tree.nextToken(comma_or_colon), else => comma_or_colon, }; } @@ -1729,7 +1879,7 @@ fn renderExpression( try stream.writeByteNTimes(' ', indent_once); const comma_or_colon = tree.nextToken(node.lastToken()); break :blk switch (tree.tokens.at(comma_or_colon).id) { - Token.Id.Comma => tree.nextToken(comma_or_colon), + .Comma => tree.nextToken(comma_or_colon), else => comma_or_colon, }; } @@ -1754,7 +1904,7 @@ fn renderExpression( } }, - ast.Node.Id.AsmInput => { + .AsmInput => { const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); try stream.write("["); @@ -1766,7 +1916,7 @@ fn renderExpression( return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // ) }, - ast.Node.Id.AsmOutput => { + .AsmOutput => { const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); try stream.write("["); @@ -1788,19 +1938,19 @@ fn renderExpression( return renderToken(tree, stream, asm_output.lastToken(), indent, start_col, space); // ) }, - ast.Node.Id.EnumLiteral => { + .EnumLiteral => { const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base); try renderToken(tree, stream, enum_literal.dot, indent, start_col, Space.None); // . return renderToken(tree, stream, enum_literal.name, indent, start_col, space); // name }, - ast.Node.Id.ContainerField, - ast.Node.Id.Root, - ast.Node.Id.VarDecl, - ast.Node.Id.Use, - ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl, + .ContainerField, + .Root, + .VarDecl, + .Use, + .TestDecl, + .ParamDecl, => unreachable, } } @@ -1812,7 +1962,7 @@ fn renderVarDecl( indent: usize, start_col: *usize, var_decl: *ast.Node.VarDecl, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } @@ -1869,8 +2019,8 @@ fn renderVarDecl( } if (var_decl.init_node) |init_node| { - const s = if (init_node.id == ast.Node.Id.MultilineStringLiteral) Space.None else Space.Space; - try renderToken(tree, stream, var_decl.eq_token, indent, start_col, s); // = + const s = if (init_node.id == .MultilineStringLiteral) Space.None else Space.Space; + try renderToken(tree, stream, var_decl.eq_token.?, indent, start_col, s); // = try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None); } @@ -1885,7 +2035,7 @@ fn renderParamDecl( start_col: *usize, base: *ast.Node, space: Space, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); try renderDocComments(tree, stream, param_decl, indent, start_col); @@ -1914,9 +2064,9 @@ fn renderStatement( indent: usize, start_col: *usize, base: *ast.Node, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { switch (base.id) { - ast.Node.Id.VarDecl => { + .VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl); }, @@ -1925,7 +2075,7 @@ fn renderStatement( try renderExpression(allocator, stream, tree, indent, start_col, base, Space.None); const semicolon_index = tree.nextToken(base.lastToken()); - assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); + assert(tree.tokens.at(semicolon_index).id == .Semicolon); try renderToken(tree, stream, semicolon_index, indent, start_col, Space.Newline); } else { try renderExpression(allocator, stream, tree, indent, start_col, base, Space.Newline); @@ -1953,7 +2103,7 @@ fn renderTokenOffset( start_col: *usize, space: Space, token_skip_bytes: usize, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { if (space == Space.BlockStart) { if (start_col.* < indent + indent_delta) return renderToken(tree, stream, token_index, indent, start_col, Space.Space); @@ -1972,13 +2122,13 @@ fn renderTokenOffset( var next_token = tree.tokens.at(token_index + 1); if (space == Space.Comma) switch (next_token.id) { - Token.Id.Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline), - Token.Id.LineComment => { + .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline), + .LineComment => { try stream.write(", "); return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline); }, else => { - if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == Token.Id.MultilineStringLiteralLine) { + if (token_index + 2 < tree.tokens.len and tree.tokens.at(token_index + 2).id == .MultilineStringLiteralLine) { try stream.write(","); return; } else { @@ -1991,7 +2141,7 @@ fn renderTokenOffset( // Skip over same line doc comments var offset: usize = 1; - if (next_token.id == Token.Id.DocComment) { + if (next_token.id == .DocComment) { const loc = tree.tokenLocationPtr(token.end, next_token); if (loc.line == 0) { offset += 1; @@ -1999,11 +2149,11 @@ fn renderTokenOffset( } } - if (next_token.id != Token.Id.LineComment) blk: { + if (next_token.id != .LineComment) blk: { switch (space) { Space.None, Space.NoNewline => return, Space.Newline => { - if (next_token.id == Token.Id.MultilineStringLiteralLine) { + if (next_token.id == .MultilineStringLiteralLine) { return; } else { try stream.write("\n"); @@ -2012,7 +2162,7 @@ fn renderTokenOffset( } }, Space.Space, Space.SpaceOrOutdent => { - if (next_token.id == Token.Id.MultilineStringLiteralLine) + if (next_token.id == .MultilineStringLiteralLine) return; try stream.writeByte(' '); return; @@ -2044,17 +2194,17 @@ fn renderTokenOffset( var loc = tree.tokenLocationPtr(token.end, next_token); if (loc.line == 0) { - try stream.print(" {}", mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); + try stream.print(" {}", .{mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")}); offset = 2; token = next_token; next_token = tree.tokens.at(token_index + offset); - if (next_token.id != Token.Id.LineComment) { + if (next_token.id != .LineComment) { switch (space) { Space.None, Space.Space => { try stream.writeByte('\n'); const after_comment_token = tree.tokens.at(token_index + offset); const next_line_indent = switch (after_comment_token.id) { - Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent, + .RParen, .RBrace, .RBracket => indent, else => indent + indent_delta, }; try stream.writeByteNTimes(' ', next_line_indent); @@ -2066,7 +2216,7 @@ fn renderTokenOffset( start_col.* = indent; }, Space.Newline => { - if (next_token.id == Token.Id.MultilineStringLiteralLine) { + if (next_token.id == .MultilineStringLiteralLine) { return; } else { try stream.write("\n"); @@ -2084,7 +2234,7 @@ fn renderTokenOffset( while (true) { assert(loc.line != 0); - const newline_count = if (loc.line == 1) u8(1) else u8(2); + const newline_count = if (loc.line == 1) @as(u8, 1) else @as(u8, 2); try stream.writeByteNTimes('\n', newline_count); try stream.writeByteNTimes(' ', indent); try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); @@ -2092,10 +2242,10 @@ fn renderTokenOffset( offset += 1; token = next_token; next_token = tree.tokens.at(token_index + offset); - if (next_token.id != Token.Id.LineComment) { + if (next_token.id != .LineComment) { switch (space) { Space.Newline => { - if (next_token.id == Token.Id.MultilineStringLiteralLine) { + if (next_token.id == .MultilineStringLiteralLine) { return; } else { try stream.write("\n"); @@ -2108,7 +2258,7 @@ fn renderTokenOffset( const after_comment_token = tree.tokens.at(token_index + offset); const next_line_indent = switch (after_comment_token.id) { - Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => blk: { + .RParen, .RBrace, .RBracket => blk: { if (indent > indent_delta) { break :blk indent - indent_delta; } else { @@ -2141,7 +2291,7 @@ fn renderToken( indent: usize, start_col: *usize, space: Space, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0); } @@ -2151,7 +2301,7 @@ fn renderDocComments( node: var, indent: usize, start_col: *usize, -) (@typeOf(stream).Child.Error || Error)!void { +) (@TypeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments orelse return; var it = comment.lines.iterator(0); const first_token = node.firstToken(); @@ -2169,11 +2319,11 @@ fn renderDocComments( fn nodeIsBlock(base: *const ast.Node) bool { return switch (base.id) { - ast.Node.Id.Block, - ast.Node.Id.If, - ast.Node.Id.For, - ast.Node.Id.While, - ast.Node.Id.Switch, + .Block, + .If, + .For, + .While, + .Switch, => true, else => false, }; @@ -2194,8 +2344,8 @@ const FindByteOutStream = struct { pub const Error = error{}; pub const Stream = std.io.OutStream(Error); - pub stream: Stream, - pub byte_found: bool, + stream: Stream, + byte_found: bool, byte: u8, pub fn init(byte: u8) Self { @@ -2217,7 +2367,7 @@ const FindByteOutStream = struct { } }; -fn copyFixingWhitespace(stream: var, slice: []const u8) @typeOf(stream).Child.Error!void { +fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Child.Error!void { for (slice) |byte| switch (byte) { '\t' => try stream.write(" "), '\r' => {}, diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index b0a6cd112..327700f59 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -9,67 +9,78 @@ pub const Token = struct { pub const Keyword = struct { bytes: []const u8, id: Id, + hash: u32, + + fn init(bytes: []const u8, id: Id) Keyword { + @setEvalBranchQuota(2000); + return .{ + .bytes = bytes, + .id = id, + .hash = std.hash_map.hashString(bytes), + }; + } }; pub const keywords = [_]Keyword{ - Keyword{ .bytes = "align", .id = Id.Keyword_align }, - Keyword{ .bytes = "allowzero", .id = Id.Keyword_allowzero }, - Keyword{ .bytes = "and", .id = Id.Keyword_and }, - Keyword{ .bytes = "anyframe", .id = Id.Keyword_anyframe }, - Keyword{ .bytes = "asm", .id = Id.Keyword_asm }, - Keyword{ .bytes = "async", .id = Id.Keyword_async }, - Keyword{ .bytes = "await", .id = Id.Keyword_await }, - Keyword{ .bytes = "break", .id = Id.Keyword_break }, - Keyword{ .bytes = "catch", .id = Id.Keyword_catch }, - Keyword{ .bytes = "comptime", .id = Id.Keyword_comptime }, - Keyword{ .bytes = "const", .id = Id.Keyword_const }, - Keyword{ .bytes = "continue", .id = Id.Keyword_continue }, - Keyword{ .bytes = "defer", .id = Id.Keyword_defer }, - Keyword{ .bytes = "else", .id = Id.Keyword_else }, - Keyword{ .bytes = "enum", .id = Id.Keyword_enum }, - Keyword{ .bytes = "errdefer", .id = Id.Keyword_errdefer }, - Keyword{ .bytes = "error", .id = Id.Keyword_error }, - Keyword{ .bytes = "export", .id = Id.Keyword_export }, - Keyword{ .bytes = "extern", .id = Id.Keyword_extern }, - Keyword{ .bytes = "false", .id = Id.Keyword_false }, - Keyword{ .bytes = "fn", .id = Id.Keyword_fn }, - Keyword{ .bytes = "for", .id = Id.Keyword_for }, - Keyword{ .bytes = "if", .id = Id.Keyword_if }, - Keyword{ .bytes = "inline", .id = Id.Keyword_inline }, - Keyword{ .bytes = "nakedcc", .id = Id.Keyword_nakedcc }, - Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias }, - Keyword{ .bytes = "noasync", .id = Id.Keyword_noasync }, - Keyword{ .bytes = "noinline", .id = Id.Keyword_noinline }, - Keyword{ .bytes = "null", .id = Id.Keyword_null }, - Keyword{ .bytes = "or", .id = Id.Keyword_or }, - Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse }, - Keyword{ .bytes = "packed", .id = Id.Keyword_packed }, - Keyword{ .bytes = "pub", .id = Id.Keyword_pub }, - Keyword{ .bytes = "resume", .id = Id.Keyword_resume }, - Keyword{ .bytes = "return", .id = Id.Keyword_return }, - Keyword{ .bytes = "linksection", .id = Id.Keyword_linksection }, - Keyword{ .bytes = "stdcallcc", .id = Id.Keyword_stdcallcc }, - Keyword{ .bytes = "struct", .id = Id.Keyword_struct }, - Keyword{ .bytes = "suspend", .id = Id.Keyword_suspend }, - Keyword{ .bytes = "switch", .id = Id.Keyword_switch }, - Keyword{ .bytes = "test", .id = Id.Keyword_test }, - Keyword{ .bytes = "threadlocal", .id = Id.Keyword_threadlocal }, - Keyword{ .bytes = "true", .id = Id.Keyword_true }, - Keyword{ .bytes = "try", .id = Id.Keyword_try }, - Keyword{ .bytes = "undefined", .id = Id.Keyword_undefined }, - Keyword{ .bytes = "union", .id = Id.Keyword_union }, - Keyword{ .bytes = "unreachable", .id = Id.Keyword_unreachable }, - Keyword{ .bytes = "use", .id = Id.Keyword_usingnamespace }, - Keyword{ .bytes = "usingnamespace", .id = Id.Keyword_usingnamespace }, - Keyword{ .bytes = "var", .id = Id.Keyword_var }, - Keyword{ .bytes = "volatile", .id = Id.Keyword_volatile }, - Keyword{ .bytes = "while", .id = Id.Keyword_while }, + Keyword.init("align", .Keyword_align), + Keyword.init("allowzero", .Keyword_allowzero), + Keyword.init("and", .Keyword_and), + Keyword.init("anyframe", .Keyword_anyframe), + Keyword.init("asm", .Keyword_asm), + Keyword.init("async", .Keyword_async), + Keyword.init("await", .Keyword_await), + Keyword.init("break", .Keyword_break), + Keyword.init("callconv", .Keyword_callconv), + Keyword.init("catch", .Keyword_catch), + Keyword.init("comptime", .Keyword_comptime), + Keyword.init("const", .Keyword_const), + Keyword.init("continue", .Keyword_continue), + Keyword.init("defer", .Keyword_defer), + Keyword.init("else", .Keyword_else), + Keyword.init("enum", .Keyword_enum), + Keyword.init("errdefer", .Keyword_errdefer), + Keyword.init("error", .Keyword_error), + Keyword.init("export", .Keyword_export), + Keyword.init("extern", .Keyword_extern), + Keyword.init("false", .Keyword_false), + Keyword.init("fn", .Keyword_fn), + Keyword.init("for", .Keyword_for), + Keyword.init("if", .Keyword_if), + Keyword.init("inline", .Keyword_inline), + Keyword.init("nakedcc", .Keyword_nakedcc), + Keyword.init("noalias", .Keyword_noalias), + Keyword.init("noasync", .Keyword_noasync), + Keyword.init("noinline", .Keyword_noinline), + Keyword.init("null", .Keyword_null), + Keyword.init("or", .Keyword_or), + Keyword.init("orelse", .Keyword_orelse), + Keyword.init("packed", .Keyword_packed), + Keyword.init("pub", .Keyword_pub), + Keyword.init("resume", .Keyword_resume), + Keyword.init("return", .Keyword_return), + Keyword.init("linksection", .Keyword_linksection), + Keyword.init("stdcallcc", .Keyword_stdcallcc), + Keyword.init("struct", .Keyword_struct), + Keyword.init("suspend", .Keyword_suspend), + Keyword.init("switch", .Keyword_switch), + Keyword.init("test", .Keyword_test), + Keyword.init("threadlocal", .Keyword_threadlocal), + Keyword.init("true", .Keyword_true), + Keyword.init("try", .Keyword_try), + Keyword.init("undefined", .Keyword_undefined), + Keyword.init("union", .Keyword_union), + Keyword.init("unreachable", .Keyword_unreachable), + Keyword.init("usingnamespace", .Keyword_usingnamespace), + Keyword.init("var", .Keyword_var), + Keyword.init("volatile", .Keyword_volatile), + Keyword.init("while", .Keyword_while), }; // TODO perfect hash at comptime - fn getKeyword(bytes: []const u8) ?Id { + pub fn getKeyword(bytes: []const u8) ?Id { + var hash = std.hash_map.hashString(bytes); for (keywords) |kw| { - if (mem.eql(u8, kw.bytes, bytes)) { + if (kw.hash == hash and mem.eql(u8, kw.bytes, bytes)) { return kw.id; } } @@ -103,6 +114,7 @@ pub const Token = struct { LBracket, RBracket, Period, + PeriodAsterisk, Ellipsis2, Ellipsis3, Caret, @@ -142,8 +154,7 @@ pub const Token = struct { FloatLiteral, LineComment, DocComment, - BracketStarBracket, - BracketStarCBracket, + ContainerDocComment, ShebangLine, Keyword_align, Keyword_allowzero, @@ -152,6 +163,7 @@ pub const Token = struct { Keyword_async, Keyword_await, Keyword_break, + Keyword_callconv, Keyword_catch, Keyword_comptime, Keyword_const, @@ -211,6 +223,7 @@ pub const Token = struct { .FloatLiteral => "FloatLiteral", .LineComment => "LineComment", .DocComment => "DocComment", + .ContainerDocComment => "ContainerDocComment", .ShebangLine => "ShebangLine", .Bang => "!", @@ -231,6 +244,7 @@ pub const Token = struct { .LBracket => "[", .RBracket => "]", .Period => ".", + .PeriodAsterisk => ".*", .Ellipsis2 => "..", .Ellipsis3 => "...", .Caret => "^", @@ -266,8 +280,6 @@ pub const Token = struct { .AngleBracketAngleBracketRight => ">>", .AngleBracketAngleBracketRightEqual => ">>=", .Tilde => "~", - .BracketStarBracket => "[*]", - .BracketStarCBracket => "[*c]", .Keyword_align => "align", .Keyword_allowzero => "allowzero", .Keyword_and => "and", @@ -276,6 +288,7 @@ pub const Token = struct { .Keyword_async => "async", .Keyword_await => "await", .Keyword_break => "break", + .Keyword_callconv => "callconv", .Keyword_catch => "catch", .Keyword_comptime => "comptime", .Keyword_const => "const", @@ -331,37 +344,23 @@ pub const Tokenizer = struct { /// For debugging purposes pub fn dump(self: *Tokenizer, token: *const Token) void { - std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]); + std.debug.warn("{} \"{}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); } pub fn init(buffer: []const u8) Tokenizer { - if (mem.startsWith(u8, buffer, "#!")) { - const src_start = if (mem.indexOfScalar(u8, buffer, '\n')) |i| i + 1 else buffer.len; - return Tokenizer{ - .buffer = buffer, - .index = src_start, - .pending_invalid_token = Token{ - .id = Token.Id.ShebangLine, - .start = 0, - .end = src_start, - }, - }; - } else { - // Skip the UTF-8 BOM if present - const src_start = if (mem.startsWith(u8, buffer, "\xEF\xBB\xBF")) 3 else usize(0); - return Tokenizer{ - .buffer = buffer, - .index = src_start, - .pending_invalid_token = null, - }; - } + // Skip the UTF-8 BOM if present + const src_start = if (mem.startsWith(u8, buffer, "\xEF\xBB\xBF")) 3 else @as(usize, 0); + return Tokenizer{ + .buffer = buffer, + .index = src_start, + .pending_invalid_token = null, + }; } const State = enum { Start, Identifier, Builtin, - C, StringLiteral, StringLiteralBackslash, MultilineStringLiteralLine, @@ -371,6 +370,7 @@ pub const Tokenizer = struct { CharLiteralUnicodeEscapeSawU, CharLiteralUnicodeEscape, CharLiteralUnicodeInvalid, + CharLiteralUnicode, CharLiteralEnd, Backslash, Equal, @@ -385,6 +385,7 @@ pub const Tokenizer = struct { LineComment, DocCommentStart, DocComment, + ContainerDocComment, Zero, IntegerLiteral, IntegerLiteralWithRadix, @@ -409,15 +410,14 @@ pub const Tokenizer = struct { Period, Period2, SawAtSign, - LBracket, - LBracketStar, - LBracketStarC, }; pub fn next(self: *Tokenizer) Token { if (self.pending_invalid_token) |token| { + // TODO: Audit this pattern once #2915 is closed + const copy = token; self.pending_invalid_token = null; - return token; + return copy; } const start_index = self.index; var state = State.Start; @@ -427,6 +427,7 @@ pub const Tokenizer = struct { .end = undefined, }; var seen_escape_digits: usize = undefined; + var remaining_code_units: usize = undefined; while (self.index < self.buffer.len) : (self.index += 1) { const c = self.buffer[self.index]; switch (state) { @@ -434,10 +435,6 @@ pub const Tokenizer = struct { ' ', '\n', '\t', '\r' => { result.start = self.index + 1; }, - 'c' => { - state = State.C; - result.id = Token.Id.Identifier; - }, '"' => { state = State.StringLiteral; result.id = Token.Id.StringLiteral; @@ -445,7 +442,7 @@ pub const Tokenizer = struct { '\'' => { state = State.CharLiteral; }, - 'a'...'b', 'd'...'z', 'A'...'Z', '_' => { + 'a'...'z', 'A'...'Z', '_' => { state = State.Identifier; result.id = Token.Id.Identifier; }, @@ -472,7 +469,9 @@ pub const Tokenizer = struct { break; }, '[' => { - state = State.LBracket; + result.id = .LBracket; + self.index += 1; + break; }, ']' => { result.id = Token.Id.RBracket; @@ -576,43 +575,6 @@ pub const Tokenizer = struct { }, }, - State.LBracket => switch (c) { - '*' => { - state = State.LBracketStar; - }, - else => { - result.id = Token.Id.LBracket; - break; - }, - }, - - State.LBracketStar => switch (c) { - 'c' => { - state = State.LBracketStarC; - }, - ']' => { - result.id = Token.Id.BracketStarBracket; - self.index += 1; - break; - }, - else => { - result.id = Token.Id.Invalid; - break; - }, - }, - - State.LBracketStarC => switch (c) { - ']' => { - result.id = Token.Id.BracketStarCBracket; - self.index += 1; - break; - }, - else => { - result.id = Token.Id.Invalid; - break; - }, - }, - State.Ampersand => switch (c) { '&' => { result.id = Token.Id.Invalid_ampersands; @@ -737,20 +699,6 @@ pub const Tokenizer = struct { }, else => break, }, - State.C => switch (c) { - '\\' => { - state = State.Backslash; - result.id = Token.Id.MultilineStringLiteralLine; - }, - '"' => { - state = State.StringLiteral; - result.id = Token.Id.StringLiteral; - }, - 'a'...'z', 'A'...'Z', '_', '0'...'9' => { - state = State.Identifier; - }, - else => break, - }, State.StringLiteral => switch (c) { '\\' => { state = State.StringLiteralBackslash; @@ -759,12 +707,12 @@ pub const Tokenizer = struct { self.index += 1; break; }, - '\n' => break, // Look for this error later. + '\n', '\r' => break, // Look for this error later. else => self.checkLiteralCharacter(), }, State.StringLiteralBackslash => switch (c) { - '\n' => break, // Look for this error later. + '\n', '\r' => break, // Look for this error later. else => { state = State.StringLiteral; }, @@ -774,16 +722,23 @@ pub const Tokenizer = struct { '\\' => { state = State.CharLiteralBackslash; }, - '\'' => { + '\'', 0x80...0xbf, 0xf8...0xff => { result.id = Token.Id.Invalid; break; }, + 0xc0...0xdf => { // 110xxxxx + remaining_code_units = 1; + state = State.CharLiteralUnicode; + }, + 0xe0...0xef => { // 1110xxxx + remaining_code_units = 2; + state = State.CharLiteralUnicode; + }, + 0xf0...0xf7 => { // 11110xxx + remaining_code_units = 3; + state = State.CharLiteralUnicode; + }, else => { - if (c < 0x20 or c == 0x7f) { - result.id = Token.Id.Invalid; - break; - } - state = State.CharLiteralEnd; }, }, @@ -867,6 +822,19 @@ pub const Tokenizer = struct { }, }, + State.CharLiteralUnicode => switch (c) { + 0x80...0xbf => { + remaining_code_units -= 1; + if (remaining_code_units == 0) { + state = State.CharLiteralEnd; + } + }, + else => { + result.id = Token.Id.Invalid; + break; + }, + }, + State.MultilineStringLiteralLine => switch (c) { '\n' => { self.index += 1; @@ -1011,6 +979,11 @@ pub const Tokenizer = struct { '.' => { state = State.Period2; }, + '*' => { + result.id = Token.Id.PeriodAsterisk; + self.index += 1; + break; + }, else => { result.id = Token.Id.Period; break; @@ -1048,6 +1021,10 @@ pub const Tokenizer = struct { '/' => { state = State.DocCommentStart; }, + '!' => { + result.id = Token.Id.ContainerDocComment; + state = State.ContainerDocComment; + }, '\n' => break, else => { state = State.LineComment; @@ -1068,7 +1045,7 @@ pub const Tokenizer = struct { self.checkLiteralCharacter(); }, }, - State.LineComment, State.DocComment => switch (c) { + State.LineComment, State.DocComment, State.ContainerDocComment => switch (c) { '\n' => break, else => self.checkLiteralCharacter(), }, @@ -1182,7 +1159,6 @@ pub const Tokenizer = struct { } else if (self.index == self.buffer.len) { switch (state) { State.Start, - State.C, State.IntegerLiteral, State.IntegerLiteralWithRadix, State.IntegerLiteralWithRadixHex, @@ -1206,6 +1182,9 @@ pub const Tokenizer = struct { State.DocComment, State.DocCommentStart => { result.id = Token.Id.DocComment; }, + State.ContainerDocComment => { + result.id = Token.Id.ContainerDocComment; + }, State.NumberDot, State.NumberDotHex, @@ -1220,9 +1199,8 @@ pub const Tokenizer = struct { State.CharLiteralUnicodeEscape, State.CharLiteralUnicodeInvalid, State.CharLiteralEnd, + State.CharLiteralUnicode, State.StringLiteralBackslash, - State.LBracketStar, - State.LBracketStarC, => { result.id = Token.Id.Invalid; }, @@ -1239,9 +1217,6 @@ pub const Tokenizer = struct { State.Slash => { result.id = Token.Id.Slash; }, - State.LBracket => { - result.id = Token.Id.LBracket; - }, State.Zero => { result.id = Token.Id.IntegerLiteral; }, @@ -1295,8 +1270,10 @@ pub const Tokenizer = struct { if (result.id == Token.Id.Eof) { if (self.pending_invalid_token) |token| { + // TODO: Audit this pattern once #2915 is closed + const copy = token; self.pending_invalid_token = null; - return token; + return copy; } } @@ -1354,17 +1331,22 @@ pub const Tokenizer = struct { }; test "tokenizer" { - testTokenize("test", [_]Token.Id{Token.Id.Keyword_test}); + testTokenize("test", &[_]Token.Id{Token.Id.Keyword_test}); } test "tokenizer - unknown length pointer and then c pointer" { testTokenize( \\[*]u8 \\[*c]u8 - , [_]Token.Id{ - Token.Id.BracketStarBracket, + , &[_]Token.Id{ + Token.Id.LBracket, + Token.Id.Asterisk, + Token.Id.RBracket, Token.Id.Identifier, - Token.Id.BracketStarCBracket, + Token.Id.LBracket, + Token.Id.Asterisk, + Token.Id.Identifier, + Token.Id.RBracket, Token.Id.Identifier, }); } @@ -1372,64 +1354,70 @@ test "tokenizer - unknown length pointer and then c pointer" { test "tokenizer - char literal with hex escape" { testTokenize( \\'\x1b' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\'\x1' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); } test "tokenizer - char literal with unicode escapes" { // Valid unicode escapes testTokenize( \\'\u{3}' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\'\u{01}' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\'\u{2a}' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\'\u{3f9}' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\'\u{6E09aBc1523}' - , [_]Token.Id{.CharLiteral}); + , &[_]Token.Id{.CharLiteral}); testTokenize( \\"\u{440}" - , [_]Token.Id{.StringLiteral}); + , &[_]Token.Id{.StringLiteral}); // Invalid unicode escapes testTokenize( \\'\u' - , [_]Token.Id{.Invalid}); + , &[_]Token.Id{.Invalid}); testTokenize( \\'\u{{' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); testTokenize( \\'\u{}' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); testTokenize( \\'\u{s}' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); testTokenize( \\'\u{2z}' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); testTokenize( \\'\u{4a' - , [_]Token.Id{.Invalid}); + , &[_]Token.Id{.Invalid}); // Test old-style unicode literals testTokenize( \\'\u0333' - , [_]Token.Id{ .Invalid, .Invalid }); + , &[_]Token.Id{ .Invalid, .Invalid }); testTokenize( \\'\U0333' - , [_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid }); + , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid }); +} + +test "tokenizer - char literal with unicode code point" { + testTokenize( + \\'๐Ÿ’ฉ' + , &[_]Token.Id{.CharLiteral}); } test "tokenizer - float literal e exponent" { - testTokenize("a = 4.94065645841246544177e-324;\n", [_]Token.Id{ + testTokenize("a = 4.94065645841246544177e-324;\n", &[_]Token.Id{ Token.Id.Identifier, Token.Id.Equal, Token.Id.FloatLiteral, @@ -1438,7 +1426,7 @@ test "tokenizer - float literal e exponent" { } test "tokenizer - float literal p exponent" { - testTokenize("a = 0x1.a827999fcef32p+1022;\n", [_]Token.Id{ + testTokenize("a = 0x1.a827999fcef32p+1022;\n", &[_]Token.Id{ Token.Id.Identifier, Token.Id.Equal, Token.Id.FloatLiteral, @@ -1447,71 +1435,71 @@ test "tokenizer - float literal p exponent" { } test "tokenizer - chars" { - testTokenize("'c'", [_]Token.Id{Token.Id.CharLiteral}); + testTokenize("'c'", &[_]Token.Id{Token.Id.CharLiteral}); } test "tokenizer - invalid token characters" { - testTokenize("#", [_]Token.Id{Token.Id.Invalid}); - testTokenize("`", [_]Token.Id{Token.Id.Invalid}); - testTokenize("'c", [_]Token.Id{Token.Id.Invalid}); - testTokenize("'", [_]Token.Id{Token.Id.Invalid}); - testTokenize("''", [_]Token.Id{ Token.Id.Invalid, Token.Id.Invalid }); + testTokenize("#", &[_]Token.Id{Token.Id.Invalid}); + testTokenize("`", &[_]Token.Id{Token.Id.Invalid}); + testTokenize("'c", &[_]Token.Id{Token.Id.Invalid}); + testTokenize("'", &[_]Token.Id{Token.Id.Invalid}); + testTokenize("''", &[_]Token.Id{ Token.Id.Invalid, Token.Id.Invalid }); } test "tokenizer - invalid literal/comment characters" { - testTokenize("\"\x00\"", [_]Token.Id{ + testTokenize("\"\x00\"", &[_]Token.Id{ Token.Id.StringLiteral, Token.Id.Invalid, }); - testTokenize("//\x00", [_]Token.Id{ + testTokenize("//\x00", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\x1f", [_]Token.Id{ + testTokenize("//\x1f", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\x7f", [_]Token.Id{ + testTokenize("//\x7f", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); } test "tokenizer - utf8" { - testTokenize("//\xc2\x80", [_]Token.Id{Token.Id.LineComment}); - testTokenize("//\xf4\x8f\xbf\xbf", [_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xc2\x80", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xf4\x8f\xbf\xbf", &[_]Token.Id{Token.Id.LineComment}); } test "tokenizer - invalid utf8" { - testTokenize("//\x80", [_]Token.Id{ + testTokenize("//\x80", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xbf", [_]Token.Id{ + testTokenize("//\xbf", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xf8", [_]Token.Id{ + testTokenize("//\xf8", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xff", [_]Token.Id{ + testTokenize("//\xff", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xc2\xc0", [_]Token.Id{ + testTokenize("//\xc2\xc0", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xe0", [_]Token.Id{ + testTokenize("//\xe0", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xf0", [_]Token.Id{ + testTokenize("//\xf0", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xf0\x90\x80\xc0", [_]Token.Id{ + testTokenize("//\xf0\x90\x80\xc0", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); @@ -1519,28 +1507,28 @@ test "tokenizer - invalid utf8" { test "tokenizer - illegal unicode codepoints" { // unicode newline characters.U+0085, U+2028, U+2029 - testTokenize("//\xc2\x84", [_]Token.Id{Token.Id.LineComment}); - testTokenize("//\xc2\x85", [_]Token.Id{ + testTokenize("//\xc2\x84", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xc2\x85", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xc2\x86", [_]Token.Id{Token.Id.LineComment}); - testTokenize("//\xe2\x80\xa7", [_]Token.Id{Token.Id.LineComment}); - testTokenize("//\xe2\x80\xa8", [_]Token.Id{ + testTokenize("//\xc2\x86", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xe2\x80\xa7", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xe2\x80\xa8", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xe2\x80\xa9", [_]Token.Id{ + testTokenize("//\xe2\x80\xa9", &[_]Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\xe2\x80\xaa", [_]Token.Id{Token.Id.LineComment}); + testTokenize("//\xe2\x80\xaa", &[_]Token.Id{Token.Id.LineComment}); } test "tokenizer - string identifier and builtin fns" { testTokenize( \\const @"if" = @import("std"); - , [_]Token.Id{ + , &[_]Token.Id{ Token.Id.Keyword_const, Token.Id.Identifier, Token.Id.Equal, @@ -1553,19 +1541,21 @@ test "tokenizer - string identifier and builtin fns" { } test "tokenizer - pipe and then invalid" { - testTokenize("||=", [_]Token.Id{ + testTokenize("||=", &[_]Token.Id{ Token.Id.PipePipe, Token.Id.Equal, }); } test "tokenizer - line comment and doc comment" { - testTokenize("//", [_]Token.Id{Token.Id.LineComment}); - testTokenize("// a / b", [_]Token.Id{Token.Id.LineComment}); - testTokenize("// /", [_]Token.Id{Token.Id.LineComment}); - testTokenize("/// a", [_]Token.Id{Token.Id.DocComment}); - testTokenize("///", [_]Token.Id{Token.Id.DocComment}); - testTokenize("////", [_]Token.Id{Token.Id.LineComment}); + testTokenize("//", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("// a / b", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("// /", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("/// a", &[_]Token.Id{Token.Id.DocComment}); + testTokenize("///", &[_]Token.Id{Token.Id.DocComment}); + testTokenize("////", &[_]Token.Id{Token.Id.LineComment}); + testTokenize("//!", &[_]Token.Id{Token.Id.ContainerDocComment}); + testTokenize("//!!", &[_]Token.Id{Token.Id.ContainerDocComment}); } test "tokenizer - line comment followed by identifier" { @@ -1573,7 +1563,7 @@ test "tokenizer - line comment followed by identifier" { \\ Unexpected, \\ // another \\ Another, - , [_]Token.Id{ + , &[_]Token.Id{ Token.Id.Identifier, Token.Id.Comma, Token.Id.LineComment, @@ -1583,18 +1573,28 @@ test "tokenizer - line comment followed by identifier" { } test "tokenizer - UTF-8 BOM is recognized and skipped" { - testTokenize("\xEF\xBB\xBFa;\n", [_]Token.Id{ + testTokenize("\xEF\xBB\xBFa;\n", &[_]Token.Id{ Token.Id.Identifier, Token.Id.Semicolon, }); } +test "correctly parse pointer assignment" { + testTokenize("b.*=3;\n", &[_]Token.Id{ + Token.Id.Identifier, + Token.Id.PeriodAsterisk, + Token.Id.Equal, + Token.Id.IntegerLiteral, + Token.Id.Semicolon, + }); +} + fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { var tokenizer = Tokenizer.init(source); for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); if (token.id != expected_token_id) { - std.debug.panic("expected {}, found {}\n", @tagName(expected_token_id), @tagName(token.id)); + std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); } } const last_token = tokenizer.next(); diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig deleted file mode 100644 index 8b0ea5ea6..000000000 --- a/src-self-hosted/arg.zig +++ /dev/null @@ -1,293 +0,0 @@ -const std = @import("std"); -const debug = std.debug; -const testing = std.testing; -const mem = std.mem; - -const Allocator = mem.Allocator; -const ArrayList = std.ArrayList; -const StringHashMap = std.StringHashMap; - -fn trimStart(slice: []const u8, ch: u8) []const u8 { - var i: usize = 0; - for (slice) |b| { - if (b != '-') break; - i += 1; - } - - return slice[i..]; -} - -fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool { - if (maybe_set) |set| { - for (set) |possible| { - if (mem.eql(u8, arg, possible)) { - return true; - } - } - return false; - } else { - return true; - } -} - -// Modifies the current argument index during iteration -fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: *usize) !FlagArg { - switch (required) { - 0 => return FlagArg{ .None = undefined }, // TODO: Required to force non-tag but value? - 1 => { - if (index.* + 1 >= args.len) { - return error.MissingFlagArguments; - } - - index.* += 1; - const arg = args[index.*]; - - if (!argInAllowedSet(allowed_set, arg)) { - return error.ArgumentNotInAllowedSet; - } - - return FlagArg{ .Single = arg }; - }, - else => |needed| { - var extra = ArrayList([]const u8).init(allocator); - errdefer extra.deinit(); - - var j: usize = 0; - while (j < needed) : (j += 1) { - if (index.* + 1 >= args.len) { - return error.MissingFlagArguments; - } - - index.* += 1; - const arg = args[index.*]; - - if (!argInAllowedSet(allowed_set, arg)) { - return error.ArgumentNotInAllowedSet; - } - - try extra.append(arg); - } - - return FlagArg{ .Many = extra }; - }, - } -} - -const HashMapFlags = StringHashMap(FlagArg); - -// A store for querying found flags and positional arguments. -pub const Args = struct { - flags: HashMapFlags, - positionals: ArrayList([]const u8), - - pub fn parse(allocator: *Allocator, comptime spec: []const Flag, args: []const []const u8) !Args { - var parsed = Args{ - .flags = HashMapFlags.init(allocator), - .positionals = ArrayList([]const u8).init(allocator), - }; - - var i: usize = 0; - next: while (i < args.len) : (i += 1) { - const arg = args[i]; - - if (arg.len != 0 and arg[0] == '-') { - // TODO: hashmap, although the linear scan is okay for small argument sets as is - for (spec) |flag| { - if (mem.eql(u8, arg, flag.name)) { - const flag_name_trimmed = trimStart(flag.name, '-'); - const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| { - switch (err) { - error.ArgumentNotInAllowedSet => { - std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg); - std.debug.warn("allowed options are "); - for (flag.allowed_set.?) |possible| { - std.debug.warn("'{}' ", possible); - } - std.debug.warn("\n"); - }, - error.MissingFlagArguments => { - std.debug.warn("missing argument for flag: {}\n", arg); - }, - else => {}, - } - - return err; - }; - - if (flag.mergable) { - var prev = if (parsed.flags.get(flag_name_trimmed)) |entry| entry.value.Many else ArrayList([]const u8).init(allocator); - - // MergeN creation disallows 0 length flag entry (doesn't make sense) - switch (flag_args) { - FlagArg.None => unreachable, - FlagArg.Single => |inner| try prev.append(inner), - FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()), - } - - _ = try parsed.flags.put(flag_name_trimmed, FlagArg{ .Many = prev }); - } else { - _ = try parsed.flags.put(flag_name_trimmed, flag_args); - } - - continue :next; - } - } - - // TODO: Better errors with context, global error state and return is sufficient. - std.debug.warn("could not match flag: {}\n", arg); - return error.UnknownFlag; - } else { - try parsed.positionals.append(arg); - } - } - - return parsed; - } - - pub fn deinit(self: *Args) void { - self.flags.deinit(); - self.positionals.deinit(); - } - - // e.g. --help - pub fn present(self: *const Args, name: []const u8) bool { - return self.flags.contains(name); - } - - // e.g. --name value - pub fn single(self: *Args, name: []const u8) ?[]const u8 { - if (self.flags.get(name)) |entry| { - switch (entry.value) { - FlagArg.Single => |inner| { - return inner; - }, - else => @panic("attempted to retrieve flag with wrong type"), - } - } else { - return null; - } - } - - // e.g. --names value1 value2 value3 - pub fn many(self: *Args, name: []const u8) []const []const u8 { - if (self.flags.get(name)) |entry| { - switch (entry.value) { - FlagArg.Many => |inner| { - return inner.toSliceConst(); - }, - else => @panic("attempted to retrieve flag with wrong type"), - } - } else { - return [_][]const u8{}; - } - } -}; - -// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`. -const FlagArg = union(enum) { - None, - Single: []const u8, - Many: ArrayList([]const u8), -}; - -// Specification for how a flag should be parsed. -pub const Flag = struct { - name: []const u8, - required: usize, - mergable: bool, - allowed_set: ?[]const []const u8, - - pub fn Bool(comptime name: []const u8) Flag { - return ArgN(name, 0); - } - - pub fn Arg1(comptime name: []const u8) Flag { - return ArgN(name, 1); - } - - pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag { - return Flag{ - .name = name, - .required = n, - .mergable = false, - .allowed_set = null, - }; - } - - pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag { - if (n == 0) { - @compileError("n must be greater than 0"); - } - - return Flag{ - .name = name, - .required = n, - .mergable = true, - .allowed_set = null, - }; - } - - pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag { - return Flag{ - .name = name, - .required = 1, - .mergable = false, - .allowed_set = set, - }; - } -}; - -test "parse arguments" { - const spec1 = comptime [_]Flag{ - Flag.Bool("--help"), - Flag.Bool("--init"), - Flag.Arg1("--build-file"), - Flag.Option("--color", [_][]const u8{ - "on", - "off", - "auto", - }), - Flag.ArgN("--pkg-begin", 2), - Flag.ArgMergeN("--object", 1), - Flag.ArgN("--library", 1), - }; - - const cliargs = [_][]const u8{ - "build", - "--help", - "pos1", - "--build-file", - "build.zig", - "--object", - "obj1", - "--object", - "obj2", - "--library", - "lib1", - "--library", - "lib2", - "--color", - "on", - "pos2", - }; - - var args = try Args.parse(std.debug.global_allocator, spec1, cliargs); - - testing.expect(args.present("help")); - testing.expect(!args.present("help2")); - testing.expect(!args.present("init")); - - testing.expect(mem.eql(u8, args.single("build-file").?, "build.zig")); - testing.expect(mem.eql(u8, args.single("color").?, "on")); - - const objects = args.many("object").?; - testing.expect(mem.eql(u8, objects[0], "obj1")); - testing.expect(mem.eql(u8, objects[1], "obj2")); - - testing.expect(mem.eql(u8, args.single("library").?, "lib2")); - - const pos = args.positionals.toSliceConst(); - testing.expect(mem.eql(u8, pos[0], "build")); - testing.expect(mem.eql(u8, pos[1], "pos1")); - testing.expect(mem.eql(u8, pos[2], "pos2")); -} diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig index 33d2faa5c..2a840372b 100644 --- a/src-self-hosted/c_int.zig +++ b/src-self-hosted/c_int.zig @@ -1,3 +1,5 @@ +const Target = @import("std").Target; + pub const CInt = struct { id: Id, zig_name: []const u8, @@ -17,52 +19,151 @@ pub const CInt = struct { pub const list = [_]CInt{ CInt{ - .id = Id.Short, + .id = .Short, .zig_name = "c_short", .c_name = "short", .is_signed = true, }, CInt{ - .id = Id.UShort, + .id = .UShort, .zig_name = "c_ushort", .c_name = "unsigned short", .is_signed = false, }, CInt{ - .id = Id.Int, + .id = .Int, .zig_name = "c_int", .c_name = "int", .is_signed = true, }, CInt{ - .id = Id.UInt, + .id = .UInt, .zig_name = "c_uint", .c_name = "unsigned int", .is_signed = false, }, CInt{ - .id = Id.Long, + .id = .Long, .zig_name = "c_long", .c_name = "long", .is_signed = true, }, CInt{ - .id = Id.ULong, + .id = .ULong, .zig_name = "c_ulong", .c_name = "unsigned long", .is_signed = false, }, CInt{ - .id = Id.LongLong, + .id = .LongLong, .zig_name = "c_longlong", .c_name = "long long", .is_signed = true, }, CInt{ - .id = Id.ULongLong, + .id = .ULongLong, .zig_name = "c_ulonglong", .c_name = "unsigned long long", .is_signed = false, }, }; + + pub fn sizeInBits(cint: CInt, self: Target) u32 { + const arch = self.getArch(); + switch (self.getOs()) { + .freestanding, .other => switch (self.getArch()) { + .msp430 => switch (cint.id) { + .Short, + .UShort, + .Int, + .UInt, + => return 16, + .Long, + .ULong, + => return 32, + .LongLong, + .ULongLong, + => return 64, + }, + else => switch (cint.id) { + .Short, + .UShort, + => return 16, + .Int, + .UInt, + => return 32, + .Long, + .ULong, + => return self.getArchPtrBitWidth(), + .LongLong, + .ULongLong, + => return 64, + }, + }, + + .linux, + .macosx, + .freebsd, + .openbsd, + => switch (cint.id) { + .Short, + .UShort, + => return 16, + .Int, + .UInt, + => return 32, + .Long, + .ULong, + => return self.getArchPtrBitWidth(), + .LongLong, + .ULongLong, + => return 64, + }, + + .windows, .uefi => switch (cint.id) { + .Short, + .UShort, + => return 16, + .Int, + .UInt, + => return 32, + .Long, + .ULong, + .LongLong, + .ULongLong, + => return 64, + }, + + .ananas, + .cloudabi, + .dragonfly, + .fuchsia, + .ios, + .kfreebsd, + .lv2, + .netbsd, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .tvos, + .watchos, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + => @panic("TODO specify the C integer type sizes for this OS"), + } + } }; diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig new file mode 100644 index 000000000..7cff969c3 --- /dev/null +++ b/src-self-hosted/c_tokenizer.zig @@ -0,0 +1,977 @@ +const std = @import("std"); +const expect = std.testing.expect; +const ZigClangSourceLocation = @import("clang.zig").ZigClangSourceLocation; +const Context = @import("translate_c.zig").Context; +const failDecl = @import("translate_c.zig").failDecl; + +pub const TokenList = std.SegmentedList(CToken, 32); + +pub const CToken = struct { + id: Id, + bytes: []const u8 = "", + num_lit_suffix: NumLitSuffix = .None, + + pub const Id = enum { + CharLit, + StrLit, + NumLitInt, + NumLitFloat, + Identifier, + Plus, + Minus, + Slash, + LParen, + RParen, + Eof, + Dot, + Asterisk, // * + Ampersand, // & + And, // && + Assign, // = + Or, // || + Bang, // ! + Tilde, // ~ + Shl, // << + Shr, // >> + Lt, // < + Lte, // <= + Gt, // > + Gte, // >= + Eq, // == + Ne, // != + Increment, // ++ + Decrement, // -- + Comma, + Fn, + Arrow, // -> + LBrace, + RBrace, + Pipe, + QuestionMark, + Colon, + }; + + pub const NumLitSuffix = enum { + None, + F, + L, + U, + LU, + LL, + LLU, + }; +}; + +pub fn tokenizeCMacro(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, tl: *TokenList, chars: [*:0]const u8) !void { + var index: usize = 0; + var first = true; + while (true) { + const tok = try next(ctx, loc, name, chars, &index); + if (tok.id == .StrLit or tok.id == .CharLit) + try tl.push(try zigifyEscapeSequences(ctx, loc, name, tl.allocator, tok)) + else + try tl.push(tok); + if (tok.id == .Eof) + return; + if (first) { + // distinguish NAME (EXPR) from NAME(ARGS) + first = false; + if (chars[index] == '(') { + try tl.push(.{ + .id = .Fn, + .bytes = "", + }); + } + } + } +} + +fn zigifyEscapeSequences(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, allocator: *std.mem.Allocator, tok: CToken) !CToken { + for (tok.bytes) |c| { + if (c == '\\') { + break; + } + } else return tok; + var bytes = try allocator.alloc(u8, tok.bytes.len * 2); + var state: enum { + Start, + Escape, + Hex, + Octal, + } = .Start; + var i: usize = 0; + var count: u8 = 0; + var num: u8 = 0; + for (tok.bytes) |c| { + switch (state) { + .Escape => { + switch (c) { + 'n', 'r', 't', '\\', '\'', '\"' => { + bytes[i] = c; + }, + '0'...'7' => { + count += 1; + num += c - '0'; + state = .Octal; + bytes[i] = 'x'; + }, + 'x' => { + state = .Hex; + bytes[i] = 'x'; + }, + 'a' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '7'; + }, + 'b' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '8'; + }, + 'f' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'C'; + }, + 'v' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'B'; + }, + '?' => { + i -= 1; + bytes[i] = '?'; + }, + 'u', 'U' => { + try failDecl(ctx, loc, name, "macro tokenizing failed: TODO unicode escape sequences", .{}); + return error.TokenizingFailed; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: unknown escape sequence", .{}); + return error.TokenizingFailed; + }, + } + i += 1; + if (state == .Escape) + state = .Start; + }, + .Start => { + if (c == '\\') { + state = .Escape; + } + bytes[i] = c; + i += 1; + }, + .Hex => { + switch (c) { + '0'...'9' => { + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; + num += c - '0'; + }, + 'a'...'f' => { + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; + num += c - 'a' + 10; + }, + 'A'...'F' => { + num = std.math.mul(u8, num, 16) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{}); + return error.TokenizingFailed; + }; + num += c - 'A' + 10; + }, + else => { + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + num = 0; + if (c == '\\') + state = .Escape + else + state = .Start; + bytes[i] = c; + i += 1; + }, + } + }, + .Octal => { + const accept_digit = switch (c) { + // The maximum length of a octal literal is 3 digits + '0'...'7' => count < 3, + else => false, + }; + + if (accept_digit) { + count += 1; + num = std.math.mul(u8, num, 8) catch { + try failDecl(ctx, loc, name, "macro tokenizing failed: octal literal overflowed", .{}); + return error.TokenizingFailed; + }; + num += c - '0'; + } else { + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + num = 0; + count = 0; + if (c == '\\') + state = .Escape + else + state = .Start; + bytes[i] = c; + i += 1; + } + }, + } + } + if (state == .Hex or state == .Octal) + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + return CToken{ + .id = tok.id, + .bytes = bytes[0..i], + }; +} + +fn next(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, chars: [*:0]const u8, i: *usize) !CToken { + var state: enum { + Start, + SawLt, + SawGt, + SawPlus, + SawMinus, + SawAmpersand, + SawPipe, + SawBang, + SawEq, + CharLit, + OpenComment, + Comment, + CommentStar, + Backslash, + String, + Identifier, + Decimal, + Octal, + SawZero, + Hex, + Bin, + Float, + ExpSign, + FloatExp, + FloatExpFirst, + NumLitIntSuffixU, + NumLitIntSuffixL, + NumLitIntSuffixLL, + NumLitIntSuffixUL, + Done, + } = .Start; + + var result = CToken{ + .bytes = "", + .id = .Eof, + }; + var begin_index: usize = 0; + var digits: u8 = 0; + var pre_escape = state; + + while (true) { + const c = chars[i.*]; + if (c == 0) { + switch (state) { + .Identifier, + .Decimal, + .Hex, + .Bin, + .Octal, + .SawZero, + .Float, + .FloatExp, + => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + .Start, + .SawMinus, + .Done, + .NumLitIntSuffixU, + .NumLitIntSuffixL, + .NumLitIntSuffixUL, + .NumLitIntSuffixLL, + .SawLt, + .SawGt, + .SawPlus, + .SawAmpersand, + .SawPipe, + .SawBang, + .SawEq, + => { + return result; + }, + .CharLit, + .OpenComment, + .Comment, + .CommentStar, + .Backslash, + .String, + .ExpSign, + .FloatExpFirst, + => { + try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected EOF", .{}); + return error.TokenizingFailed; + }, + } + } + switch (state) { + .Start => { + switch (c) { + ' ', '\t', '\x0B', '\x0C' => {}, + '\'' => { + state = .CharLit; + result.id = .CharLit; + begin_index = i.*; + }, + '\"' => { + state = .String; + result.id = .StrLit; + begin_index = i.*; + }, + '/' => { + state = .OpenComment; + }, + '\\' => { + state = .Backslash; + }, + '\n', '\r' => { + return result; + }, + 'a'...'z', 'A'...'Z', '_' => { + state = .Identifier; + result.id = .Identifier; + begin_index = i.*; + }, + '1'...'9' => { + state = .Decimal; + result.id = .NumLitInt; + begin_index = i.*; + }, + '0' => { + state = .SawZero; + result.id = .NumLitInt; + begin_index = i.*; + }, + '.' => { + result.id = .Dot; + state = .Done; + }, + '<' => { + result.id = .Lt; + state = .SawLt; + }, + '>' => { + result.id = .Gt; + state = .SawGt; + }, + '(' => { + result.id = .LParen; + state = .Done; + }, + ')' => { + result.id = .RParen; + state = .Done; + }, + '*' => { + result.id = .Asterisk; + state = .Done; + }, + '+' => { + result.id = .Plus; + state = .SawPlus; + }, + '-' => { + result.id = .Minus; + state = .SawMinus; + }, + '!' => { + result.id = .Bang; + state = .SawBang; + }, + '~' => { + result.id = .Tilde; + state = .Done; + }, + '=' => { + result.id = .Assign; + state = .SawEq; + }, + ',' => { + result.id = .Comma; + state = .Done; + }, + '[' => { + result.id = .LBrace; + state = .Done; + }, + ']' => { + result.id = .RBrace; + state = .Done; + }, + '|' => { + result.id = .Pipe; + state = .SawPipe; + }, + '&' => { + result.id = .Ampersand; + state = .SawAmpersand; + }, + '?' => { + result.id = .QuestionMark; + state = .Done; + }, + ':' => { + result.id = .Colon; + state = .Done; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected character '{c}'", .{c}); + return error.TokenizingFailed; + }, + } + }, + .Done => return result, + .SawMinus => { + switch (c) { + '>' => { + result.id = .Arrow; + state = .Done; + }, + '-' => { + result.id = .Decrement; + state = .Done; + }, + else => return result, + } + }, + .SawPlus => { + switch (c) { + '+' => { + result.id = .Increment; + state = .Done; + }, + else => return result, + } + }, + .SawLt => { + switch (c) { + '<' => { + result.id = .Shl; + state = .Done; + }, + '=' => { + result.id = .Lte; + state = .Done; + }, + else => return result, + } + }, + .SawGt => { + switch (c) { + '>' => { + result.id = .Shr; + state = .Done; + }, + '=' => { + result.id = .Gte; + state = .Done; + }, + else => return result, + } + }, + .SawPipe => { + switch (c) { + '|' => { + result.id = .Or; + state = .Done; + }, + else => return result, + } + }, + .SawAmpersand => { + switch (c) { + '&' => { + result.id = .And; + state = .Done; + }, + else => return result, + } + }, + .SawBang => { + switch (c) { + '=' => { + result.id = .Ne; + state = .Done; + }, + else => return result, + } + }, + .SawEq => { + switch (c) { + '=' => { + result.id = .Eq; + state = .Done; + }, + else => return result, + } + }, + .Float => { + switch (c) { + '.', '0'...'9' => {}, + 'e', 'E' => { + state = .ExpSign; + }, + 'f', + 'F', + => { + result.num_lit_suffix = .F; + result.bytes = chars[begin_index..i.*]; + state = .Done; + }, + 'l', 'L' => { + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + state = .Done; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .ExpSign => { + switch (c) { + '+', '-' => { + state = .FloatExpFirst; + }, + '0'...'9' => { + state = .FloatExp; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit or '+' or '-'", .{}); + return error.TokenizingFailed; + }, + } + }, + .FloatExpFirst => { + switch (c) { + '0'...'9' => { + state = .FloatExp; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit", .{}); + return error.TokenizingFailed; + }, + } + }, + .FloatExp => { + switch (c) { + '0'...'9' => {}, + 'f', 'F' => { + result.num_lit_suffix = .F; + result.bytes = chars[begin_index..i.*]; + state = .Done; + }, + 'l', 'L' => { + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + state = .Done; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Decimal => { + switch (c) { + '0'...'9' => {}, + '\'' => {}, + 'u', 'U' => { + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index..i.*]; + }, + 'l', 'L' => { + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + }, + '.' => { + result.id = .NumLitFloat; + state = .Float; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .SawZero => { + switch (c) { + 'x', 'X' => { + state = .Hex; + }, + 'b', 'B' => { + state = .Bin; + }, + '.' => { + state = .Float; + result.id = .NumLitFloat; + }, + 'u', 'U' => { + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index..i.*]; + }, + 'l', 'L' => { + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + }, + else => { + i.* -= 1; + state = .Octal; + }, + } + }, + .Octal => { + switch (c) { + '0'...'7' => {}, + '8', '9' => { + try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in octal number", .{c}); + return error.TokenizingFailed; + }, + 'u', 'U' => { + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index..i.*]; + }, + 'l', 'L' => { + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Hex => { + switch (c) { + '0'...'9', 'a'...'f', 'A'...'F' => {}, + 'u', 'U' => { + // marks the number literal as unsigned + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index..i.*]; + }, + 'l', 'L' => { + // marks the number literal as long + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .Bin => { + switch (c) { + '0'...'1' => {}, + '2'...'9' => { + try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in binary number", .{c}); + return error.TokenizingFailed; + }, + 'u', 'U' => { + // marks the number literal as unsigned + state = .NumLitIntSuffixU; + result.num_lit_suffix = .U; + result.bytes = chars[begin_index..i.*]; + }, + 'l', 'L' => { + // marks the number literal as long + state = .NumLitIntSuffixL; + result.num_lit_suffix = .L; + result.bytes = chars[begin_index..i.*]; + }, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .NumLitIntSuffixU => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LU; + state = .NumLitIntSuffixUL; + }, + else => { + return result; + }, + } + }, + .NumLitIntSuffixL => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LL; + state = .NumLitIntSuffixLL; + }, + 'u', 'U' => { + result.num_lit_suffix = .LU; + state = .Done; + }, + else => { + return result; + }, + } + }, + .NumLitIntSuffixLL => { + switch (c) { + 'u', 'U' => { + result.num_lit_suffix = .LLU; + state = .Done; + }, + else => { + return result; + }, + } + }, + .NumLitIntSuffixUL => { + switch (c) { + 'l', 'L' => { + result.num_lit_suffix = .LLU; + state = .Done; + }, + else => { + return result; + }, + } + }, + .Identifier => { + switch (c) { + '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, + else => { + result.bytes = chars[begin_index..i.*]; + return result; + }, + } + }, + .String => { + switch (c) { + '\"' => { + result.bytes = chars[begin_index .. i.* + 1]; + state = .Done; + }, + else => {}, + } + }, + .CharLit => { + switch (c) { + '\'' => { + result.bytes = chars[begin_index .. i.* + 1]; + state = .Done; + }, + else => {}, + } + }, + .OpenComment => { + switch (c) { + '/' => { + return result; + }, + '*' => { + state = .Comment; + }, + else => { + result.id = .Slash; + state = .Done; + }, + } + }, + .Comment => { + switch (c) { + '*' => { + state = .CommentStar; + }, + else => {}, + } + }, + .CommentStar => { + switch (c) { + '/' => { + state = .Start; + }, + else => { + state = .Comment; + }, + } + }, + .Backslash => { + switch (c) { + ' ', '\t', '\x0B', '\x0C' => {}, + '\n', '\r' => { + state = .Start; + }, + else => { + try failDecl(ctx, loc, name, "macro tokenizing failed: expected whitespace", .{}); + return error.TokenizingFailed; + }, + } + }, + } + i.* += 1; + } + unreachable; +} + +fn expectTokens(tl: *TokenList, src: [*:0]const u8, expected: []CToken) void { + // these can be undefined since they are only used for error reporting + tokenizeCMacro(undefined, undefined, undefined, tl, src) catch unreachable; + var it = tl.iterator(0); + for (expected) |t| { + var tok = it.next().?; + std.testing.expectEqual(t.id, tok.id); + if (t.bytes.len > 0) { + //std.debug.warn(" {} = {}\n", .{tok.bytes, t.bytes}); + std.testing.expectEqualSlices(u8, tok.bytes, t.bytes); + } + if (t.num_lit_suffix != .None) { + std.testing.expectEqual(t.num_lit_suffix, tok.num_lit_suffix); + } + } + std.testing.expect(it.next() == null); + tl.shrink(0); +} + +test "tokenize macro" { + var tl = TokenList.init(std.heap.page_allocator); + defer tl.deinit(); + + expectTokens(&tl, "TEST(0\n", &[_]CToken{ + .{ .id = .Identifier, .bytes = "TEST" }, + .{ .id = .Fn }, + .{ .id = .LParen }, + .{ .id = .NumLitInt, .bytes = "0" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "__FLT_MIN_10_EXP__ -37\n", &[_]CToken{ + .{ .id = .Identifier, .bytes = "__FLT_MIN_10_EXP__" }, + .{ .id = .Minus }, + .{ .id = .NumLitInt, .bytes = "37" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "__llvm__ 1\n#define", &[_]CToken{ + .{ .id = .Identifier, .bytes = "__llvm__" }, + .{ .id = .NumLitInt, .bytes = "1" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "TEST 2", &[_]CToken{ + .{ .id = .Identifier, .bytes = "TEST" }, + .{ .id = .NumLitInt, .bytes = "2" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "FOO 0ull", &[_]CToken{ + .{ .id = .Identifier, .bytes = "FOO" }, + .{ .id = .NumLitInt, .bytes = "0", .num_lit_suffix = .LLU }, + .{ .id = .Eof }, + }); +} + +test "tokenize macro ops" { + var tl = TokenList.init(std.heap.page_allocator); + defer tl.deinit(); + + expectTokens(&tl, "ADD A + B", &[_]CToken{ + .{ .id = .Identifier, .bytes = "ADD" }, + .{ .id = .Identifier, .bytes = "A" }, + .{ .id = .Plus }, + .{ .id = .Identifier, .bytes = "B" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "ADD (A) + B", &[_]CToken{ + .{ .id = .Identifier, .bytes = "ADD" }, + .{ .id = .LParen }, + .{ .id = .Identifier, .bytes = "A" }, + .{ .id = .RParen }, + .{ .id = .Plus }, + .{ .id = .Identifier, .bytes = "B" }, + .{ .id = .Eof }, + }); + + expectTokens(&tl, "ADD (A) + B", &[_]CToken{ + .{ .id = .Identifier, .bytes = "ADD" }, + .{ .id = .LParen }, + .{ .id = .Identifier, .bytes = "A" }, + .{ .id = .RParen }, + .{ .id = .Plus }, + .{ .id = .Identifier, .bytes = "B" }, + .{ .id = .Eof }, + }); +} + +test "escape sequences" { + var buf: [1024]u8 = undefined; + var alloc = std.heap.FixedBufferAllocator.init(buf[0..]); + const a = &alloc.allocator; + // these can be undefined since they are only used for error reporting + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .StrLit, + .bytes = "\\x0077", + })).bytes, "\\x77")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .StrLit, + .bytes = "\\24500", + })).bytes, "\\xa500")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .StrLit, + .bytes = "\\x0077 abc", + })).bytes, "\\x77 abc")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .StrLit, + .bytes = "\\045abc", + })).bytes, "\\x25abc")); + + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .CharLit, + .bytes = "\\0", + })).bytes, "\\x00")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .CharLit, + .bytes = "\\00", + })).bytes, "\\x00")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .CharLit, + .bytes = "\\000\\001", + })).bytes, "\\x00\\x01")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(undefined, undefined, undefined, a, .{ + .id = .CharLit, + .bytes = "\\000abc", + })).bytes, "\\x00abc")); +} diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 5266f2737..37f1eebe2 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1,5 +1,9 @@ const builtin = @import("builtin"); +pub const struct_ZigClangConditionalOperator = @OpaqueType(); +pub const struct_ZigClangBinaryConditionalOperator = @OpaqueType(); +pub const struct_ZigClangAbstractConditionalOperator = @OpaqueType(); +pub const struct_ZigClangAPInt = @OpaqueType(); pub const struct_ZigClangAPSInt = @OpaqueType(); pub const struct_ZigClangAPFloat = @OpaqueType(); pub const struct_ZigClangASTContext = @OpaqueType(); @@ -15,7 +19,6 @@ pub const struct_ZigClangCallExpr = @OpaqueType(); pub const struct_ZigClangCaseStmt = @OpaqueType(); pub const struct_ZigClangCompoundAssignOperator = @OpaqueType(); pub const struct_ZigClangCompoundStmt = @OpaqueType(); -pub const struct_ZigClangConditionalOperator = @OpaqueType(); pub const struct_ZigClangConstantArrayType = @OpaqueType(); pub const struct_ZigClangContinueStmt = @OpaqueType(); pub const struct_ZigClangDecayedType = @OpaqueType(); @@ -42,9 +45,12 @@ pub const struct_ZigClangImplicitCastExpr = @OpaqueType(); pub const struct_ZigClangIncompleteArrayType = @OpaqueType(); pub const struct_ZigClangIntegerLiteral = @OpaqueType(); pub const struct_ZigClangMacroDefinitionRecord = @OpaqueType(); +pub const struct_ZigClangMacroExpansion = @OpaqueType(); +pub const struct_ZigClangMacroQualifiedType = @OpaqueType(); pub const struct_ZigClangMemberExpr = @OpaqueType(); pub const struct_ZigClangNamedDecl = @OpaqueType(); pub const struct_ZigClangNone = @OpaqueType(); +pub const struct_ZigClangOpaqueValueExpr = @OpaqueType(); pub const struct_ZigClangPCHContainerOperations = @OpaqueType(); pub const struct_ZigClangParenExpr = @OpaqueType(); pub const struct_ZigClangParenType = @OpaqueType(); @@ -72,6 +78,12 @@ pub const struct_ZigClangVarDecl = @OpaqueType(); pub const struct_ZigClangWhileStmt = @OpaqueType(); pub const struct_ZigClangFunctionType = @OpaqueType(); pub const struct_ZigClangPredefinedExpr = @OpaqueType(); +pub const struct_ZigClangInitListExpr = @OpaqueType(); +pub const ZigClangPreprocessingRecord = @OpaqueType(); +pub const ZigClangFloatingLiteral = @OpaqueType(); +pub const ZigClangConstantExpr = @OpaqueType(); +pub const ZigClangCharacterLiteral = @OpaqueType(); +pub const ZigClangStmtExpr = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -706,20 +718,61 @@ pub const ZigClangStringLiteral_StringKind = extern enum { UTF32, }; +pub const ZigClangCharacterLiteral_CharacterKind = extern enum { + Ascii, + Wide, + UTF8, + UTF16, + UTF32, +}; + +pub const ZigClangRecordDecl_field_iterator = extern struct { + opaque: *c_void, +}; + +pub const ZigClangEnumDecl_enumerator_iterator = extern struct { + opaque: *c_void, +}; + +pub const ZigClangPreprocessingRecord_iterator = extern struct { + I: c_int, + Self: *ZigClangPreprocessingRecord, +}; + +pub const ZigClangPreprocessedEntity_EntityKind = extern enum { + InvalidKind, + MacroExpansionKind, + MacroDefinitionKind, + InclusionDirectiveKind, +}; + +pub const ZigClangExpr_ConstExprUsage = extern enum { + EvaluateForCodeGen, + EvaluateForMangling, +}; + pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation; -pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*]const u8; +pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8; pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint; pub extern fn ZigClangSourceManager_getSpellingColumnNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint; -pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*c]const u8; +pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*:0]const u8; pub extern fn ZigClangASTContext_getPointerType(self: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType; pub extern fn ZigClangASTUnit_getASTContext(self: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext; pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager; pub extern fn ZigClangASTUnit_visitLocalTopLevelDecls(self: *struct_ZigClangASTUnit, context: ?*c_void, Fn: ?extern fn (?*c_void, *const struct_ZigClangDecl) bool) bool; -pub extern fn ZigClangRecordType_getDecl(record_ty: ?*const struct_ZigClangRecordType) ?*const struct_ZigClangRecordDecl; -pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumType) ?*const struct_ZigClangEnumDecl; +pub extern fn ZigClangRecordType_getDecl(record_ty: ?*const struct_ZigClangRecordType) *const struct_ZigClangRecordDecl; +pub extern fn ZigClangTagDecl_isThisDeclarationADefinition(self: *const ZigClangTagDecl) bool; +pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumType) *const struct_ZigClangEnumDecl; pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl; +pub extern fn ZigClangFieldDecl_getCanonicalDecl(field_decl: ?*const struct_ZigClangFieldDecl) ?*const struct_ZigClangFieldDecl; pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl; pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl; +pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl; +pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl; +pub extern fn ZigClangVarDecl_getSectionAttribute(self: *const ZigClangVarDecl, len: *usize) ?[*]const u8; +pub extern fn ZigClangFunctionDecl_getAlignedAttribute(self: *const ZigClangFunctionDecl, *const ZigClangASTContext) c_uint; +pub extern fn ZigClangVarDecl_getAlignedAttribute(self: *const ZigClangVarDecl, *const ZigClangASTContext) c_uint; +pub extern fn ZigClangRecordDecl_getPackedAttribute(self: ?*const struct_ZigClangRecordDecl) bool; pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl; pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl; pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation; @@ -729,15 +782,26 @@ pub extern fn ZigClangDecl_getLocation(self: *const ZigClangDecl) ZigClangSource pub extern fn ZigClangRecordDecl_isUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool; pub extern fn ZigClangRecordDecl_isStruct(record_decl: ?*const struct_ZigClangRecordDecl) bool; pub extern fn ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool; +pub extern fn ZigClangRecordDecl_field_begin(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator; +pub extern fn ZigClangRecordDecl_field_end(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator; +pub extern fn ZigClangRecordDecl_field_iterator_next(ZigClangRecordDecl_field_iterator) ZigClangRecordDecl_field_iterator; +pub extern fn ZigClangRecordDecl_field_iterator_deref(ZigClangRecordDecl_field_iterator) *const struct_ZigClangFieldDecl; +pub extern fn ZigClangRecordDecl_field_iterator_neq(ZigClangRecordDecl_field_iterator, ZigClangRecordDecl_field_iterator) bool; pub extern fn ZigClangEnumDecl_getIntegerType(self: ?*const struct_ZigClangEnumDecl) struct_ZigClangQualType; -pub extern fn ZigClangDecl_getName_bytes_begin(decl: ?*const struct_ZigClangDecl) [*c]const u8; +pub extern fn ZigClangEnumDecl_enumerator_begin(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator; +pub extern fn ZigClangEnumDecl_enumerator_end(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator; +pub extern fn ZigClangEnumDecl_enumerator_iterator_next(ZigClangEnumDecl_enumerator_iterator) ZigClangEnumDecl_enumerator_iterator; +pub extern fn ZigClangEnumDecl_enumerator_iterator_deref(ZigClangEnumDecl_enumerator_iterator) *const ZigClangEnumConstantDecl; +pub extern fn ZigClangEnumDecl_enumerator_iterator_neq(ZigClangEnumDecl_enumerator_iterator, ZigClangEnumDecl_enumerator_iterator) bool; +pub extern fn ZigClangDecl_castToNamedDecl(decl: *const struct_ZigClangDecl) ?*const ZigClangNamedDecl; +pub extern fn ZigClangNamedDecl_getName_bytes_begin(decl: ?*const struct_ZigClangNamedDecl) [*:0]const u8; pub extern fn ZigClangSourceLocation_eq(a: struct_ZigClangSourceLocation, b: struct_ZigClangSourceLocation) bool; -pub extern fn ZigClangTypedefType_getDecl(self: ?*const struct_ZigClangTypedefType) ?*const struct_ZigClangTypedefNameDecl; +pub extern fn ZigClangTypedefType_getDecl(self: ?*const struct_ZigClangTypedefType) *const struct_ZigClangTypedefNameDecl; pub extern fn ZigClangTypedefNameDecl_getUnderlyingType(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangQualType; pub extern fn ZigClangQualType_getCanonicalType(self: struct_ZigClangQualType) struct_ZigClangQualType; pub extern fn ZigClangQualType_getTypeClass(self: struct_ZigClangQualType) ZigClangTypeClass; pub extern fn ZigClangQualType_getTypePtr(self: struct_ZigClangQualType) *const struct_ZigClangType; -pub extern fn ZigClangQualType_addConst(self: [*c]struct_ZigClangQualType) void; +pub extern fn ZigClangQualType_addConst(self: *struct_ZigClangQualType) void; pub extern fn ZigClangQualType_eq(self: struct_ZigClangQualType, arg1: struct_ZigClangQualType) bool; pub extern fn ZigClangQualType_isConstQualified(self: struct_ZigClangQualType) bool; pub extern fn ZigClangQualType_isVolatileQualified(self: struct_ZigClangQualType) bool; @@ -745,26 +809,36 @@ pub extern fn ZigClangQualType_isRestrictQualified(self: struct_ZigClangQualType pub extern fn ZigClangType_getTypeClass(self: ?*const struct_ZigClangType) ZigClangTypeClass; pub extern fn ZigClangType_getPointeeType(self: ?*const struct_ZigClangType) struct_ZigClangQualType; pub extern fn ZigClangType_isVoidType(self: ?*const struct_ZigClangType) bool; -pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*]const u8; +pub extern fn ZigClangType_isRecordType(self: ?*const struct_ZigClangType) bool; +pub extern fn ZigClangType_isArrayType(self: ?*const struct_ZigClangType) bool; +pub extern fn ZigClangType_isBooleanType(self: ?*const struct_ZigClangType) bool; +pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*:0]const u8; +pub extern fn ZigClangType_getAsArrayTypeUnsafe(self: *const ZigClangType) *const ZigClangArrayType; +pub extern fn ZigClangType_getAsRecordType(self: *const ZigClangType) ?*const ZigClangRecordType; +pub extern fn ZigClangType_getAsUnionType(self: *const ZigClangType) ?*const ZigClangRecordType; pub extern fn ZigClangStmt_getBeginLoc(self: *const struct_ZigClangStmt) struct_ZigClangSourceLocation; pub extern fn ZigClangStmt_getStmtClass(self: ?*const struct_ZigClangStmt) ZigClangStmtClass; pub extern fn ZigClangStmt_classof_Expr(self: ?*const struct_ZigClangStmt) bool; -pub extern fn ZigClangExpr_getStmtClass(self: ?*const struct_ZigClangExpr) ZigClangStmtClass; -pub extern fn ZigClangExpr_getType(self: ?*const struct_ZigClangExpr) struct_ZigClangQualType; +pub extern fn ZigClangExpr_getStmtClass(self: *const struct_ZigClangExpr) ZigClangStmtClass; +pub extern fn ZigClangExpr_getType(self: *const struct_ZigClangExpr) struct_ZigClangQualType; pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_ZigClangSourceLocation; +pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitListExpr, i: c_uint) *const ZigClangExpr; +pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr; +pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint; +pub extern fn ZigClangInitListExpr_getInitializedFieldInUnion(self: ?*const struct_ZigClangInitListExpr) ?*ZigClangFieldDecl; pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind; -pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt; +pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt; pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint; -pub extern fn ZigClangAPValue_getArrayInitializedElt(self: ?*const struct_ZigClangAPValue, i: c_uint) ?*const struct_ZigClangAPValue; -pub extern fn ZigClangAPValue_getArrayFiller(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPValue; pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint; pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase; -pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt; -pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void; -pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*c]const u64; -pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint; +pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt; +pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void; +pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64; +pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint; + +pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64; pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr; pub extern fn ZigClangASTUnit_delete(self: ?*struct_ZigClangASTUnit) void; @@ -774,6 +848,13 @@ pub extern fn ZigClangFunctionDecl_hasBody(self: *const ZigClangFunctionDecl) bo pub extern fn ZigClangFunctionDecl_getStorageClass(self: *const ZigClangFunctionDecl) ZigClangStorageClass; pub extern fn ZigClangFunctionDecl_getParamDecl(self: *const ZigClangFunctionDecl, i: c_uint) *const struct_ZigClangParmVarDecl; pub extern fn ZigClangFunctionDecl_getBody(self: *const ZigClangFunctionDecl) *const struct_ZigClangStmt; +pub extern fn ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefinition(self: *const ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_isThisDeclarationADefinition(self: *const ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_doesThisDeclarationHaveABody(self: *const ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_isInlineSpecified(self: *const ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_isDefined(self: *const ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_getDefinition(self: *const ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl; +pub extern fn ZigClangFunctionDecl_getSectionAttribute(self: *const ZigClangFunctionDecl, len: *usize) ?[*]const u8; pub extern fn ZigClangBuiltinType_getKind(self: *const struct_ZigClangBuiltinType) ZigClangBuiltinTypeKind; @@ -784,9 +865,13 @@ pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionTy pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool; pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint; pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType; +pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType; pub const ZigClangSourceLocation = struct_ZigClangSourceLocation; pub const ZigClangQualType = struct_ZigClangQualType; +pub const ZigClangConditionalOperator = struct_ZigClangConditionalOperator; +pub const ZigClangBinaryConditionalOperator = struct_ZigClangBinaryConditionalOperator; +pub const ZigClangAbstractConditionalOperator = struct_ZigClangAbstractConditionalOperator; pub const ZigClangAPValueLValueBase = struct_ZigClangAPValueLValueBase; pub const ZigClangAPValue = struct_ZigClangAPValue; pub const ZigClangAPSInt = struct_ZigClangAPSInt; @@ -804,7 +889,6 @@ pub const ZigClangCallExpr = struct_ZigClangCallExpr; pub const ZigClangCaseStmt = struct_ZigClangCaseStmt; pub const ZigClangCompoundAssignOperator = struct_ZigClangCompoundAssignOperator; pub const ZigClangCompoundStmt = struct_ZigClangCompoundStmt; -pub const ZigClangConditionalOperator = struct_ZigClangConditionalOperator; pub const ZigClangConstantArrayType = struct_ZigClangConstantArrayType; pub const ZigClangContinueStmt = struct_ZigClangContinueStmt; pub const ZigClangDecayedType = struct_ZigClangDecayedType; @@ -831,9 +915,12 @@ pub const ZigClangImplicitCastExpr = struct_ZigClangImplicitCastExpr; pub const ZigClangIncompleteArrayType = struct_ZigClangIncompleteArrayType; pub const ZigClangIntegerLiteral = struct_ZigClangIntegerLiteral; pub const ZigClangMacroDefinitionRecord = struct_ZigClangMacroDefinitionRecord; +pub const ZigClangMacroExpansion = struct_ZigClangMacroExpansion; +pub const ZigClangMacroQualifiedType = struct_ZigClangMacroQualifiedType; pub const ZigClangMemberExpr = struct_ZigClangMemberExpr; pub const ZigClangNamedDecl = struct_ZigClangNamedDecl; pub const ZigClangNone = struct_ZigClangNone; +pub const ZigClangOpaqueValueExpr = struct_ZigClangOpaqueValueExpr; pub const ZigClangPCHContainerOperations = struct_ZigClangPCHContainerOperations; pub const ZigClangParenExpr = struct_ZigClangParenExpr; pub const ZigClangParenType = struct_ZigClangParenType; @@ -861,6 +948,7 @@ pub const ZigClangVarDecl = struct_ZigClangVarDecl; pub const ZigClangWhileStmt = struct_ZigClangWhileStmt; pub const ZigClangFunctionType = struct_ZigClangFunctionType; pub const ZigClangPredefinedExpr = struct_ZigClangPredefinedExpr; +pub const ZigClangInitListExpr = struct_ZigClangInitListExpr; pub const struct_ZigClangSourceLocation = extern struct { ID: c_uint, @@ -891,29 +979,32 @@ pub const struct_ZigClangAPValueLValueBase = extern struct { Version: c_uint, }; -pub extern fn ZigClangErrorMsg_delete(ptr: [*c]Stage2ErrorMsg, len: usize) void; +pub extern fn ZigClangErrorMsg_delete(ptr: [*]Stage2ErrorMsg, len: usize) void; pub extern fn ZigClangLoadFromCommandLine( args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, errors_ptr: *[*]Stage2ErrorMsg, errors_len: *usize, - resources_path: [*c]const u8, + resources_path: [*:0]const u8, ) ?*ZigClangASTUnit; pub extern fn ZigClangDecl_getKind(decl: *const ZigClangDecl) ZigClangDeclKind; -pub extern fn ZigClangDecl_getDeclKindName(decl: *const struct_ZigClangDecl) [*]const u8; +pub extern fn ZigClangDecl_getDeclKindName(decl: *const struct_ZigClangDecl) [*:0]const u8; -pub const ZigClangCompoundStmt_const_body_iterator = [*c]const *struct_ZigClangStmt; +pub const ZigClangCompoundStmt_const_body_iterator = [*]const *struct_ZigClangStmt; pub extern fn ZigClangCompoundStmt_body_begin(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator; pub extern fn ZigClangCompoundStmt_body_end(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator; -pub const ZigClangDeclStmt_const_decl_iterator = [*c]const *struct_ZigClangDecl; +pub const ZigClangDeclStmt_const_decl_iterator = [*]const *struct_ZigClangDecl; pub extern fn ZigClangDeclStmt_decl_begin(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator; pub extern fn ZigClangDeclStmt_decl_end(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator; +pub extern fn ZigClangVarDecl_getLocation(self: *const struct_ZigClangVarDecl) ZigClangSourceLocation; +pub extern fn ZigClangVarDecl_hasInit(self: *const struct_ZigClangVarDecl) bool; +pub extern fn ZigClangVarDecl_getStorageClass(self: *const ZigClangVarDecl) ZigClangStorageClass; pub extern fn ZigClangVarDecl_getType(self: ?*const struct_ZigClangVarDecl) struct_ZigClangQualType; pub extern fn ZigClangVarDecl_getInit(*const ZigClangVarDecl) ?*const ZigClangExpr; pub extern fn ZigClangVarDecl_getTLSKind(self: ?*const struct_ZigClangVarDecl) ZigClangVarDecl_TLSKind; @@ -928,8 +1019,12 @@ pub extern fn ZigClangImplicitCastExpr_getCastKind(*const ZigClangImplicitCastEx pub extern fn ZigClangImplicitCastExpr_getSubExpr(*const ZigClangImplicitCastExpr) *const ZigClangExpr; pub extern fn ZigClangArrayType_getElementType(*const ZigClangArrayType) ZigClangQualType; +pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncompleteArrayType) ZigClangQualType; +pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType; +pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt; pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl; +pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl; pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType; @@ -937,6 +1032,8 @@ pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType; +pub extern fn ZigClangMacroQualifiedType_getModifiedType(*const ZigClangMacroQualifiedType) ZigClangQualType; + pub extern fn ZigClangCStyleCastExpr_getBeginLoc(*const ZigClangCStyleCastExpr) ZigClangSourceLocation; pub extern fn ZigClangCStyleCastExpr_getSubExpr(*const ZigClangCStyleCastExpr) *const ZigClangExpr; pub extern fn ZigClangCStyleCastExpr_getType(*const ZigClangCStyleCastExpr) ZigClangQualType; @@ -953,6 +1050,7 @@ pub const struct_ZigClangAPValue = extern struct { Kind: ZigClangAPValueKind, Data: if (builtin.os == .windows and builtin.abi == .msvc) [52]u8 else [68]u8, }; +pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType; pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool; pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation; @@ -965,5 +1063,101 @@ pub extern fn ZigClangBinaryOperator_getLHS(*const ZigClangBinaryOperator) *cons pub extern fn ZigClangBinaryOperator_getRHS(*const ZigClangBinaryOperator) *const ZigClangExpr; pub extern fn ZigClangBinaryOperator_getType(*const ZigClangBinaryOperator) ZigClangQualType; +pub extern fn ZigClangDecayedType_getDecayedType(*const ZigClangDecayedType) ZigClangQualType; + pub extern fn ZigClangStringLiteral_getKind(*const ZigClangStringLiteral) ZigClangStringLiteral_StringKind; -pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*c]const u8; +pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*]const u8; + +pub extern fn ZigClangParenExpr_getSubExpr(*const ZigClangParenExpr) *const ZigClangExpr; + +pub extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const struct_ZigClangFieldDecl) bool; +pub extern fn ZigClangFieldDecl_isBitField(*const struct_ZigClangFieldDecl) bool; +pub extern fn ZigClangFieldDecl_getType(*const struct_ZigClangFieldDecl) struct_ZigClangQualType; +pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) struct_ZigClangSourceLocation; + +pub extern fn ZigClangEnumConstantDecl_getInitExpr(*const ZigClangEnumConstantDecl) ?*const ZigClangExpr; +pub extern fn ZigClangEnumConstantDecl_getInitVal(*const ZigClangEnumConstantDecl) *const ZigClangAPSInt; + +pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator; +pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_end(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator; +pub extern fn ZigClangPreprocessingRecord_iterator_deref(ZigClangPreprocessingRecord_iterator) *ZigClangPreprocessedEntity; +pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEntity) ZigClangPreprocessedEntity_EntityKind; + +pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8; +pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; +pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; + +pub extern fn ZigClangMacroExpansion_getDefinition(*const ZigClangMacroExpansion) *const ZigClangMacroDefinitionRecord; + +pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt; +pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt; +pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt; + +pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr; +pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt; + +pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; +pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; + +pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt; +pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr; +pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr; +pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt; + +pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8; +pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64; + +pub extern fn ZigClangAbstractConditionalOperator_getCond(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr; +pub extern fn ZigClangAbstractConditionalOperator_getTrueExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr; +pub extern fn ZigClangAbstractConditionalOperator_getFalseExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr; + +pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt; +pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr; +pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt; +pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool; + +pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr; +pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr; +pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation; +pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt; + +pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt; + +pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool; + +pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral; + +pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation; +pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind; +pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint; + +pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt; + +pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr; +pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool; +pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl; + +pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; +pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; + +pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr; +pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint; +pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr; + +pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType; +pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation; + +pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO; +pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType; +pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr; +pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation; + +pub extern fn ZigClangOpaqueValueExpr_getSourceExpr(*const ZigClangOpaqueValueExpr) ?*const ZigClangExpr; + +pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation; +pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO; +pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr; +pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr; diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 90f5309fa..11bb83ae1 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const Compilation = @import("compilation.zig").Compilation; const llvm = @import("llvm.zig"); const c = @import("c.zig"); @@ -7,17 +6,18 @@ const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const Scope = @import("scope.zig").Scope; +const util = @import("util.zig"); const event = std.event; const assert = std.debug.assert; const DW = std.dwarf; const maxInt = std.math.maxInt; -pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { +pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) Compilation.BuildError!void { fn_val.base.ref(); defer fn_val.base.deref(comp); defer code.destroy(comp.gpa()); - var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable); + var output_path = try comp.createRandomOutputPath(comp.target.oFileExt()); errdefer output_path.deinit(); const llvm_handle = try comp.zig_compiler.getAnyLlvmContext(); @@ -25,13 +25,13 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const context = llvm_handle.node.data; - const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; + const module = llvm.ModuleCreateWithNameInContext(comp.name.toSliceConst(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); - llvm.SetTarget(module, comp.llvm_triple.ptr()); + llvm.SetTarget(module, comp.llvm_triple.toSliceConst()); llvm.SetDataLayout(module, comp.target_layout_str); - if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) { + if (util.getObjectFormat(comp.target) == .coff) { llvm.AddModuleCodeViewFlag(module); } else { llvm.AddModuleDebugInfoFlag(module); @@ -45,30 +45,28 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes // the git revision. - const producer = try std.Buffer.allocPrint( - &code.arena.allocator, - "zig {}.{}.{}", - u32(c.ZIG_VERSION_MAJOR), - u32(c.ZIG_VERSION_MINOR), - u32(c.ZIG_VERSION_PATCH), - ); - const flags = c""; + const producer = try std.Buffer.allocPrint(&code.arena.allocator, "zig {}.{}.{}", .{ + @as(u32, c.ZIG_VERSION_MAJOR), + @as(u32, c.ZIG_VERSION_MINOR), + @as(u32, c.ZIG_VERSION_PATCH), + }); + const flags = ""; const runtime_version = 0; const compile_unit_file = llvm.CreateFile( dibuilder, - comp.name.ptr(), - comp.root_package.root_src_dir.ptr(), + comp.name.toSliceConst(), + comp.root_package.root_src_dir.toSliceConst(), ) orelse return error.OutOfMemory; - const is_optimized = comp.build_mode != builtin.Mode.Debug; + const is_optimized = comp.build_mode != .Debug; const compile_unit = llvm.CreateCompileUnit( dibuilder, DW.LANG_C99, compile_unit_file, - producer.ptr(), + producer.toSliceConst(), is_optimized, flags, runtime_version, - c"", + "", 0, !comp.strip, ) orelse return error.OutOfMemory; @@ -79,7 +77,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) .builder = builder, .dibuilder = dibuilder, .context = context, - .lock = event.Lock.init(comp.loop), + .lock = event.Lock.init(), .arena = &code.arena.allocator, }; @@ -93,45 +91,43 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { - std.debug.warn("raw module:\n"); + std.debug.warn("raw module:\n", .{}); llvm.DumpModule(ofile.module); } // verify the llvm module when safety is on if (std.debug.runtime_safety) { - var error_ptr: ?[*]u8 = null; + var error_ptr: ?[*:0]u8 = null; _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); } - assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types + const is_small = comp.build_mode == .ReleaseSmall; + const is_debug = comp.build_mode == .Debug; - const is_small = comp.build_mode == builtin.Mode.ReleaseSmall; - const is_debug = comp.build_mode == builtin.Mode.Debug; - - var err_msg: [*]u8 = undefined; + var err_msg: [*:0]u8 = undefined; // TODO integrate this with evented I/O if (llvm.TargetMachineEmitToFile( comp.target_machine, module, - output_path.ptr(), + output_path.toSliceConst(), llvm.EmitBinary, &err_msg, is_debug, is_small, )) { if (std.debug.runtime_safety) { - std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg); + std.debug.panic("unable to write object file {}: {s}\n", .{ output_path.toSliceConst(), err_msg }); } return error.WritingObjectFileFailed; } //validate_inline_fns(g); TODO fn_val.containing_object = output_path; if (comp.verbose_llvm_ir) { - std.debug.warn("optimized module:\n"); + std.debug.warn("optimized module:\n", .{}); llvm.DumpModule(ofile.module); } if (comp.verbose_link) { - std.debug.warn("created {}\n", output_path.toSliceConst()); + std.debug.warn("created {}\n", .{output_path.toSliceConst()}); } } @@ -154,7 +150,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - fn_val.symbol_name.ptr(), + fn_val.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -234,8 +230,8 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) // create debug variable declarations for variables and allocate all local variables for (var_list) |var_scope, i| { const var_type = switch (var_scope.data) { - Scope.Var.Data.Const => unreachable, - Scope.Var.Data.Param => |param| param.typ, + .Const => unreachable, + .Param => |param| param.typ, }; // if (!type_has_bits(var->value->type)) { // continue; @@ -266,7 +262,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i)); } else { // gen_type = var->value->type; - var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi); + var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, .Abi); } // if (var->decl_node) { // var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), @@ -300,8 +296,8 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) ofile, llvm_param, scope_var.data.Param.llvm_value, - Type.Pointer.Align.Abi, - Type.Pointer.Vol.Non, + .Abi, + .Non, ); } @@ -379,18 +375,18 @@ fn renderLoadUntyped( ptr: *llvm.Value, alignment: Type.Pointer.Align, vol: Type.Pointer.Vol, - name: [*]const u8, + name: [*:0]const u8, ) !*llvm.Value { const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory; switch (vol) { - Type.Pointer.Vol.Non => {}, - Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1), + .Non => {}, + .Volatile => llvm.SetVolatile(result, 1), } llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr)))); return result; } -fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value { +fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value { return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name); } @@ -402,7 +398,7 @@ pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Poin if (child_type.handleIsPtr()) { return ptr; } - return try renderLoad(ofile, ptr, ptr_type, c""); + return try renderLoad(ofile, ptr, ptr_type, ""); } pub fn renderStoreUntyped( @@ -414,8 +410,8 @@ pub fn renderStoreUntyped( ) !*llvm.Value { const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory; switch (vol) { - Type.Pointer.Vol.Non => {}, - Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1), + .Non => {}, + .Volatile => llvm.SetVolatile(result, 1), } llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value))); return result; @@ -438,14 +434,14 @@ pub fn renderAlloca( ) !*llvm.Value { const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context); const name_with_null = try std.cstr.addNullByte(ofile.arena, name); - const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory; + const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory; llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type)); return result; } pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: *llvm.Type) u32 { return switch (alignment) { - Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type), - Type.Pointer.Align.Override => |a| a, + .Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type), + .Override => |a| a, }; } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1e71a5e56..0f455fadc 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -5,8 +5,8 @@ const Allocator = mem.Allocator; const Buffer = std.Buffer; const llvm = @import("llvm.zig"); const c = @import("c.zig"); -const builtin = @import("builtin"); -const Target = @import("target.zig").Target; +const builtin = std.builtin; +const Target = std.Target; const warn = std.debug.warn; const Token = std.zig.Token; const ArrayList = std.ArrayList; @@ -30,14 +30,15 @@ const link = @import("link.zig").link; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const CInt = @import("c_int.zig").CInt; const fs = event.fs; +const util = @import("util.zig"); const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB /// Data that is local to the event loop. pub const ZigCompiler = struct { - loop: *event.Loop, llvm_handle_pool: std.atomic.Stack(*llvm.Context), lld_lock: event.Lock, + allocator: *Allocator, /// TODO pool these so that it doesn't have to lock prng: event.Locked(std.rand.DefaultPrng), @@ -46,9 +47,9 @@ pub const ZigCompiler = struct { var lazy_init_targets = std.lazyInit(void); - pub fn init(loop: *event.Loop) !ZigCompiler { + pub fn init(allocator: *Allocator) !ZigCompiler { lazy_init_targets.get() orelse { - Target.initializeAll(); + util.initializeAllTargets(); lazy_init_targets.resolve(); }; @@ -57,11 +58,11 @@ pub const ZigCompiler = struct { const seed = mem.readIntNative(u64, &seed_bytes); return ZigCompiler{ - .loop = loop, - .lld_lock = event.Lock.init(loop), + .allocator = allocator, + .lld_lock = event.Lock.init(), .llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(), - .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), - .native_libc = event.Future(LibCInstallation).init(loop), + .prng = event.Locked(std.rand.DefaultPrng).init(std.rand.DefaultPrng.init(seed)), + .native_libc = event.Future(LibCInstallation).init(), }; } @@ -70,7 +71,7 @@ pub const ZigCompiler = struct { self.lld_lock.deinit(); while (self.llvm_handle_pool.pop()) |node| { llvm.ContextDispose(node.data); - self.loop.allocator.destroy(node); + self.allocator.destroy(node); } } @@ -82,19 +83,19 @@ pub const ZigCompiler = struct { const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory; errdefer llvm.ContextDispose(context_ref); - const node = try self.loop.allocator.create(std.atomic.Stack(*llvm.Context).Node); + const node = try self.allocator.create(std.atomic.Stack(*llvm.Context).Node); node.* = std.atomic.Stack(*llvm.Context).Node{ .next = undefined, .data = context_ref, }; - errdefer self.loop.allocator.destroy(node); + errdefer self.allocator.destroy(node); return LlvmHandle{ .node = node }; } - pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { - if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr; - try await (async self.native_libc.data.findNative(self.loop) catch unreachable); + pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { + if (self.native_libc.start()) |ptr| return ptr; + try self.native_libc.data.findNative(self.allocator); self.native_libc.resolve(); return &self.native_libc.data; } @@ -102,8 +103,8 @@ pub const ZigCompiler = struct { /// Must be called only once, ever. Sets global state. pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void { if (llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, [_][]const []const u8{ - [_][]const u8{"zig (LLVM option parsing)"}, + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, &[_][]const []const u8{ + &[_][]const u8{"zig (LLVM option parsing)"}, llvm_argv, }); defer c_compatible_args.deinit(); @@ -122,7 +123,6 @@ pub const LlvmHandle = struct { pub const Compilation = struct { zig_compiler: *ZigCompiler, - loop: *event.Loop, name: Buffer, llvm_triple: Buffer, root_src_path: ?[]const u8, @@ -133,62 +133,57 @@ pub const Compilation = struct { zig_std_dir: []const u8, /// lazily created when we need it - tmp_dir: event.Future(BuildError![]u8), + tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(), - version_major: u32, - version_minor: u32, - version_patch: u32, + version: builtin.Version = builtin.Version{ .major = 0, .minor = 0, .patch = 0 }, - linker_script: ?[]const u8, - out_h_path: ?[]const u8, + linker_script: ?[]const u8 = null, + out_h_path: ?[]const u8 = null, - is_test: bool, - each_lib_rpath: bool, - strip: bool, + is_test: bool = false, + strip: bool = false, is_static: bool, - linker_rdynamic: bool, + linker_rdynamic: bool = false, - clang_argv: []const []const u8, - lib_dirs: []const []const u8, - rpath_list: []const []const u8, - assembly_files: []const []const u8, + clang_argv: []const []const u8 = &[_][]const u8{}, + assembly_files: []const []const u8 = &[_][]const u8{}, /// paths that are explicitly provided by the user to link against - link_objects: []const []const u8, + link_objects: []const []const u8 = &[_][]const u8{}, /// functions that have their own objects that we need to link /// it uses an optional pointer so that tombstone removals are possible - fn_link_set: event.Locked(FnLinkSet), + fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()), pub const FnLinkSet = std.TailQueue(?*Value.Fn); - windows_subsystem_windows: bool, - windows_subsystem_console: bool, - link_libs_list: ArrayList(*LinkLib), - libc_link_lib: ?*LinkLib, + libc_link_lib: ?*LinkLib = null, - err_color: errmsg.Color, + err_color: errmsg.Color = .Auto, - verbose_tokenize: bool, - verbose_ast_tree: bool, - verbose_ast_fmt: bool, - verbose_cimport: bool, - verbose_ir: bool, - verbose_llvm_ir: bool, - verbose_link: bool, + verbose_tokenize: bool = false, + verbose_ast_tree: bool = false, + verbose_ast_fmt: bool = false, + verbose_cimport: bool = false, + verbose_ir: bool = false, + verbose_llvm_ir: bool = false, + verbose_link: bool = false, - darwin_frameworks: []const []const u8, - darwin_version_min: DarwinVersionMin, + link_eh_frame_hdr: bool = false, - test_filters: []const []const u8, - test_name_prefix: ?[]const u8, + darwin_version_min: DarwinVersionMin = .None, - emit_file_type: Emit, + test_filters: []const []const u8 = &[_][]const u8{}, + test_name_prefix: ?[]const u8 = null, + + emit_bin: bool = true, + emit_asm: bool = false, + emit_llvm_ir: bool = false, + emit_h: bool = false, kind: Kind, - link_out_file: ?[]const u8, events: *event.Channel(Event), exported_symbol_names: event.Locked(Decl.Table), @@ -213,7 +208,7 @@ pub const Compilation = struct { target_machine: *llvm.TargetMachine, target_data_ref: *llvm.TargetData, - target_layout_str: [*]u8, + target_layout_str: [*:0]u8, target_ptr_bits: u32, /// for allocating things which have the same lifetime as this Compilation @@ -222,16 +217,16 @@ pub const Compilation = struct { root_package: *Package, std_package: *Package, - override_libc: ?*LibCInstallation, + override_libc: ?*LibCInstallation = null, /// need to wait on this group before deinitializing deinit_group: event.Group(void), - destroy_handle: promise, - main_loop_handle: promise, - main_loop_future: event.Future(void), + destroy_frame: *@Frame(createAsync), + main_loop_frame: *@Frame(Compilation.mainLoop), + main_loop_future: event.Future(void) = event.Future(void).init(), - have_err_ret_tracing: bool, + have_err_ret_tracing: bool = false, /// not locked because it is read-only primitive_type_table: TypeTable, @@ -245,6 +240,8 @@ pub const Compilation = struct { fs_watch: *fs.Watch(*Scope.Root), + cancelled: bool = false, + const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); @@ -348,7 +345,9 @@ pub const Compilation = struct { zig_lib_dir: []const u8, ) !*Compilation { var optional_comp: ?*Compilation = null; - const handle = try async createAsync( + var frame = try zig_compiler.allocator.create(@Frame(createAsync)); + errdefer zig_compiler.allocator.destroy(frame); + frame.* = async createAsync( &optional_comp, zig_compiler, name, @@ -359,10 +358,11 @@ pub const Compilation = struct { is_static, zig_lib_dir, ); - return optional_comp orelse if (getAwaitResult( - zig_compiler.loop.allocator, - handle, - )) |_| unreachable else |err| err; + // TODO causes segfault + // return optional_comp orelse if (await frame) |_| unreachable else |err| err; + if (optional_comp) |comp| { + return comp; + } else if (await frame) |_| unreachable else |err| return err; } async fn createAsync( @@ -376,15 +376,9 @@ pub const Compilation = struct { is_static: bool, zig_lib_dir: []const u8, ) !void { - // workaround for https://github.com/ziglang/zig/issues/1194 - suspend { - resume @handle(); - } - - const loop = zig_compiler.loop; + const allocator = zig_compiler.allocator; var comp = Compilation{ - .loop = loop, - .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), + .arena_allocator = std.heap.ArenaAllocator.init(allocator), .zig_compiler = zig_compiler, .events = undefined, .root_src_path = root_src_path, @@ -394,58 +388,21 @@ pub const Compilation = struct { .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, .zig_std_dir = undefined, - .tmp_dir = event.Future(BuildError![]u8).init(loop), - .destroy_handle = @handle(), - .main_loop_handle = undefined, - .main_loop_future = event.Future(void).init(loop), + .destroy_frame = @frame(), + .main_loop_frame = undefined, .name = undefined, .llvm_triple = undefined, - - .version_major = 0, - .version_minor = 0, - .version_patch = 0, - - .verbose_tokenize = false, - .verbose_ast_tree = false, - .verbose_ast_fmt = false, - .verbose_cimport = false, - .verbose_ir = false, - .verbose_llvm_ir = false, - .verbose_link = false, - - .linker_script = null, - .out_h_path = null, - .is_test = false, - .each_lib_rpath = false, - .strip = false, .is_static = is_static, - .linker_rdynamic = false, - .clang_argv = [_][]const u8{}, - .lib_dirs = [_][]const u8{}, - .rpath_list = [_][]const u8{}, - .assembly_files = [_][]const u8{}, - .link_objects = [_][]const u8{}, - .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), - .windows_subsystem_windows = false, - .windows_subsystem_console = false, .link_libs_list = undefined, - .libc_link_lib = null, - .err_color = errmsg.Color.Auto, - .darwin_frameworks = [_][]const u8{}, - .darwin_version_min = DarwinVersionMin.None, - .test_filters = [_][]const u8{}, - .test_name_prefix = null, - .emit_file_type = Emit.Binary, - .link_out_file = null, - .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), - .prelink_group = event.Group(BuildError!void).init(loop), - .deinit_group = event.Group(void).init(loop), - .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), - .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), - .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), - .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), - .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)), + .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)), + .prelink_group = event.Group(BuildError!void).init(allocator), + .deinit_group = event.Group(void).init(allocator), + .compile_errors = event.Locked(CompileErrList).init(CompileErrList.init(allocator)), + .int_type_table = event.Locked(IntTypeTable).init(IntTypeTable.init(allocator)), + .array_type_table = event.Locked(ArrayTypeTable).init(ArrayTypeTable.init(allocator)), + .ptr_type_table = event.Locked(PtrTypeTable).init(PtrTypeTable.init(allocator)), + .fn_type_table = event.Locked(FnTypeTable).init(FnTypeTable.init(allocator)), .c_int_types = undefined, .meta_type = undefined, @@ -467,8 +424,6 @@ pub const Compilation = struct { .root_package = undefined, .std_package = undefined, - .override_libc = null, - .have_err_ret_tracing = false, .primitive_type_table = undefined, .fs_watch = undefined, @@ -485,12 +440,12 @@ pub const Compilation = struct { } comp.name = try Buffer.init(comp.arena(), name); - comp.llvm_triple = try target.getTriple(comp.arena()); - comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); - comp.zig_std_dir = try std.fs.path.join(comp.arena(), [_][]const u8{ zig_lib_dir, "std" }); + comp.llvm_triple = try util.getTriple(comp.arena(), target); + comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple); + comp.zig_std_dir = try std.fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" }); const opt_level = switch (build_mode) { - builtin.Mode.Debug => llvm.CodeGenLevelNone, + .Debug => llvm.CodeGenLevelNone, else => llvm.CodeGenLevelAggressive, }; @@ -499,8 +454,8 @@ pub const Compilation = struct { // LLVM creates invalid binaries on Windows sometimes. // See https://github.com/ziglang/zig/issues/508 // As a workaround we do not use target native features on Windows. - var target_specific_cpu_args: ?[*]u8 = null; - var target_specific_cpu_features: ?[*]u8 = null; + var target_specific_cpu_args: ?[*:0]u8 = null; + var target_specific_cpu_features: ?[*:0]u8 = null; defer llvm.DisposeMessage(target_specific_cpu_args); defer llvm.DisposeMessage(target_specific_cpu_features); if (target == Target.Native and !target.isWindows()) { @@ -510,13 +465,13 @@ pub const Compilation = struct { comp.target_machine = llvm.CreateTargetMachine( comp.llvm_target, - comp.llvm_triple.ptr(), - target_specific_cpu_args orelse c"", - target_specific_cpu_features orelse c"", + comp.llvm_triple.toSliceConst(), + target_specific_cpu_args orelse "", + target_specific_cpu_features orelse "", opt_level, reloc_mode, llvm.CodeModelDefault, - false // TODO: add -ffunction-sections option + false, // TODO: add -ffunction-sections option ) orelse return error.OutOfMemory; defer llvm.DisposeTargetMachine(comp.target_machine); @@ -526,8 +481,11 @@ pub const Compilation = struct { comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; defer llvm.DisposeMessage(comp.target_layout_str); - comp.events = try event.Channel(Event).create(comp.loop, 0); - defer comp.events.destroy(); + comp.events = try allocator.create(event.Channel(Event)); + defer allocator.destroy(comp.events); + + comp.events.init(&[0]Event{}); + defer comp.events.deinit(); if (root_src_path) |root_src| { const dirname = std.fs.path.dirname(root_src) orelse "."; @@ -540,13 +498,16 @@ pub const Compilation = struct { comp.root_package = try Package.create(comp.arena(), ".", ""); } - comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16); - defer comp.fs_watch.destroy(); + comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16); + defer comp.fs_watch.deinit(); try comp.initTypes(); defer comp.primitive_type_table.deinit(); - comp.main_loop_handle = async comp.mainLoop() catch unreachable; + comp.main_loop_frame = try allocator.create(@Frame(mainLoop)); + defer allocator.destroy(comp.main_loop_frame); + + comp.main_loop_frame.* = async comp.mainLoop(); // Set this to indicate that initialization completed successfully. // from here on out we must not return an error. // This must occur before the first suspend/await. @@ -555,16 +516,17 @@ pub const Compilation = struct { suspend; // From here on is cleanup. - await (async comp.deinit_group.wait() catch unreachable); + comp.deinit_group.wait(); - if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { - // TODO evented I/O? - std.fs.deleteTree(comp.arena(), tmp_dir) catch {}; - } else |_| {}; + if (comp.tmp_dir.getOrNull()) |tmp_dir_result| + if (tmp_dir_result.*) |tmp_dir| { + // TODO evented I/O? + std.fs.deleteTree(tmp_dir) catch {}; + } else |_| {}; } /// it does ref the result because it could be an arbitrary integer size - pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { + pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { if (name.len >= 2) { switch (name[0]) { 'i', 'u' => blk: { @@ -578,10 +540,10 @@ pub const Compilation = struct { error.Overflow => return error.Overflow, error.InvalidCharacter => unreachable, // we just checked the characters above }; - const int_type = try await (async Type.Int.get(comp, Type.Int.Key{ + const int_type = try Type.Int.get(comp, Type.Int.Key{ .bit_count = bit_count, .is_signed = is_signed, - }) catch unreachable); + }); errdefer int_type.base.base.deref(); return &int_type.base; }, @@ -603,12 +565,12 @@ pub const Compilation = struct { .base = Type{ .name = "type", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = undefined, .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice }, - .id = builtin.TypeId.Type, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .Type, + .abi_alignment = Type.AbiAlignment.init(), }, .value = undefined, }; @@ -621,12 +583,12 @@ pub const Compilation = struct { .base = Type{ .name = "void", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.Void, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .Void, + .abi_alignment = Type.AbiAlignment.init(), }, }; assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); @@ -636,12 +598,12 @@ pub const Compilation = struct { .base = Type{ .name = "noreturn", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.NoReturn, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .NoReturn, + .abi_alignment = Type.AbiAlignment.init(), }, }; assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); @@ -651,12 +613,12 @@ pub const Compilation = struct { .base = Type{ .name = "comptime_int", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.ComptimeInt, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .ComptimeInt, + .abi_alignment = Type.AbiAlignment.init(), }, }; assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); @@ -666,12 +628,12 @@ pub const Compilation = struct { .base = Type{ .name = "bool", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.Bool, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .Bool, + .abi_alignment = Type.AbiAlignment.init(), }, }; assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); @@ -679,7 +641,7 @@ pub const Compilation = struct { comp.void_value = try comp.arena().create(Value.Void); comp.void_value.* = Value.Void{ .base = Value{ - .id = Value.Id.Void, + .id = .Void, .typ = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -688,7 +650,7 @@ pub const Compilation = struct { comp.true_value = try comp.arena().create(Value.Bool); comp.true_value.* = Value.Bool{ .base = Value{ - .id = Value.Id.Bool, + .id = .Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -698,7 +660,7 @@ pub const Compilation = struct { comp.false_value = try comp.arena().create(Value.Bool); comp.false_value.* = Value.Bool{ .base = Value{ - .id = Value.Id.Bool, + .id = .Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -708,7 +670,7 @@ pub const Compilation = struct { comp.noreturn_value = try comp.arena().create(Value.NoReturn); comp.noreturn_value.* = Value.NoReturn{ .base = Value{ - .id = Value.Id.NoReturn, + .id = .NoReturn, .typ = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -720,16 +682,16 @@ pub const Compilation = struct { .base = Type{ .name = cint.zig_name, .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.Int, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .Int, + .abi_alignment = Type.AbiAlignment.init(), }, .key = Type.Int.Key{ .is_signed = cint.is_signed, - .bit_count = comp.target.cIntTypeSizeInBits(cint.id), + .bit_count = cint.sizeInBits(comp.target), }, .garbage_node = undefined, }; @@ -741,12 +703,12 @@ pub const Compilation = struct { .base = Type{ .name = "u8", .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - .id = builtin.TypeId.Int, - .abi_alignment = Type.AbiAlignment.init(comp.loop), + .id = .Int, + .abi_alignment = Type.AbiAlignment.init(), }, .key = Type.Int.Key{ .is_signed = false, @@ -758,8 +720,11 @@ pub const Compilation = struct { } pub fn destroy(self: *Compilation) void { - cancel self.main_loop_handle; - resume self.destroy_handle; + const allocator = self.gpa(); + self.cancelled = true; + await self.main_loop_frame; + resume self.destroy_frame; + allocator.destroy(self.destroy_frame); } fn start(self: *Compilation) void { @@ -768,13 +733,13 @@ pub const Compilation = struct { async fn mainLoop(self: *Compilation) void { // wait until start() is called - _ = await (async self.main_loop_future.get() catch unreachable); + _ = self.main_loop_future.get(); - var build_result = await (async self.initialCompile() catch unreachable); + var build_result = self.initialCompile(); - while (true) { + while (!self.cancelled) { const link_result = if (build_result) blk: { - break :blk await (async self.maybeLink() catch unreachable); + break :blk self.maybeLink(); } else |err| err; // this makes a handy error return trace and stack trace in debug mode if (std.debug.runtime_safety) { @@ -782,42 +747,42 @@ pub const Compilation = struct { } const compile_errors = blk: { - const held = await (async self.compile_errors.acquire() catch unreachable); + const held = self.compile_errors.acquire(); defer held.release(); break :blk held.value.toOwnedSlice(); }; if (link_result) |_| { if (compile_errors.len == 0) { - await (async self.events.put(Event.Ok) catch unreachable); + self.events.put(Event.Ok); } else { - await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + self.events.put(Event{ .Fail = compile_errors }); } } else |err| { // if there's an error then the compile errors have dangling references self.gpa().free(compile_errors); - await (async self.events.put(Event{ .Error = err }) catch unreachable); + self.events.put(Event{ .Error = err }); } // First, get an item from the watch channel, waiting on the channel. - var group = event.Group(BuildError!void).init(self.loop); + var group = event.Group(BuildError!void).init(self.gpa()); { - const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| { + const ev = (self.fs_watch.channel.get()) catch |err| { build_result = err; continue; }; const root_scope = ev.data; - group.call(rebuildFile, self, root_scope) catch |err| { + group.call(rebuildFile, .{ self, root_scope }) catch |err| { build_result = err; continue; }; } // Next, get all the items from the channel that are buffered up. - while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| { + while (self.fs_watch.channel.getOrNull()) |ev_or_err| { if (ev_or_err) |ev| { const root_scope = ev.data; - group.call(rebuildFile, self, root_scope) catch |err| { + group.call(rebuildFile, .{ self, root_scope }) catch |err| { build_result = err; continue; }; @@ -826,18 +791,18 @@ pub const Compilation = struct { continue; } } - build_result = await (async group.wait() catch unreachable); + build_result = group.wait(); } } - async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void { + async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void { const tree_scope = blk: { - const source_code = (await (async fs.readFile( - self.loop, + const source_code = fs.readFile( + self.gpa(), root_scope.realpath, max_src_size, - ) catch unreachable)) catch |err| { - try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err)); + ) catch |err| { + try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", .{@errorName(err)}); return; }; errdefer self.gpa().free(source_code); @@ -856,19 +821,18 @@ pub const Compilation = struct { const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error); errdefer msg.destroy(); - try await (async self.addCompileErrorAsync(msg) catch unreachable); + try self.addCompileErrorAsync(msg); } if (tree_scope.tree.errors.len != 0) { return; } - const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable); + const locked_table = root_scope.decls.table.acquireWrite(); defer locked_table.release(); - var decl_group = event.Group(BuildError!void).init(self.loop); - defer decl_group.deinit(); + var decl_group = event.Group(BuildError!void).init(self.gpa()); - try await try async self.rebuildChangedDecls( + try self.rebuildChangedDecls( &decl_group, locked_table.value, root_scope.decls, @@ -876,10 +840,10 @@ pub const Compilation = struct { tree_scope, ); - try await (async decl_group.wait() catch unreachable); + try decl_group.wait(); } - async fn rebuildChangedDecls( + fn rebuildChangedDecls( self: *Compilation, group: *event.Group(BuildError!void), locked_table: *Decl.Table, @@ -894,22 +858,22 @@ pub const Compilation = struct { while (ast_it.next()) |decl_ptr| { const decl = decl_ptr.*; switch (decl.id) { - ast.Node.Id.Comptime => { + .Comptime => { const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); // TODO connect existing comptime decls to updated source files - try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node); + try self.prelink_group.call(addCompTimeBlock, .{ self, tree_scope, &decl_scope.base, comptime_node }); }, - ast.Node.Id.VarDecl => @panic("TODO"), - ast.Node.Id.FnProto => { + .VarDecl => @panic("TODO"), + .FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else { try self.addCompileError(tree_scope, Span{ .first = fn_proto.fn_token, .last = fn_proto.fn_token + 1, - }, "missing function name"); + }, "missing function name", .{}); continue; }; @@ -942,20 +906,20 @@ pub const Compilation = struct { .id = Decl.Id.Fn, .name = name, .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token), - .resolution = event.Future(BuildError!void).init(self.loop), + .resolution = event.Future(BuildError!void).init(), .parent_scope = &decl_scope.base, .tree_scope = tree_scope, }, - .value = Decl.Fn.Val{ .Unresolved = {} }, + .value = .Unresolved, .fn_proto = fn_proto, }; tree_scope.base.ref(); errdefer self.gpa().destroy(fn_decl); - try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table); + try group.call(addTopLevelDecl, .{ self, &fn_decl.base, locked_table }); } }, - ast.Node.Id.TestDecl => @panic("TODO"), + .TestDecl => @panic("TODO"), else => unreachable, } } @@ -968,12 +932,12 @@ pub const Compilation = struct { } } - async fn initialCompile(self: *Compilation) !void { + fn initialCompile(self: *Compilation) !void { if (self.root_src_path) |root_src_path| { const root_scope = blk: { // TODO async/await std.fs.realpath const root_src_real_path = std.fs.realpathAlloc(self.gpa(), root_src_path) catch |err| { - try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err)); + try self.addCompileErrorCli(root_src_path, "unable to open: {}", .{@errorName(err)}); return; }; errdefer self.gpa().free(root_src_real_path); @@ -982,26 +946,26 @@ pub const Compilation = struct { }; defer root_scope.base.deref(self); - assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null); - try await try async self.rebuildFile(root_scope); + // assert((try self.fs_watch.addFile(root_scope.realpath, root_scope)) == null); + try self.rebuildFile(root_scope); } } - async fn maybeLink(self: *Compilation) !void { - (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) { + fn maybeLink(self: *Compilation) !void { + (self.prelink_group.wait()) catch |err| switch (err) { error.SemanticAnalysisFailed => {}, else => return err, }; const any_prelink_errors = blk: { - const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + const compile_errors = self.compile_errors.acquire(); defer compile_errors.release(); break :blk compile_errors.value.len != 0; }; if (!any_prelink_errors) { - try await (async link(self) catch unreachable); + try link(self); } } @@ -1013,28 +977,28 @@ pub const Compilation = struct { node: *ast.Node, expected_type: ?*Type, ) !*ir.Code { - const unanalyzed_code = try await (async ir.gen( + const unanalyzed_code = try ir.gen( comp, node, tree_scope, scope, - ) catch unreachable); + ); defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { - std.debug.warn("unanalyzed:\n"); + std.debug.warn("unanalyzed:\n", .{}); unanalyzed_code.dump(); } - const analyzed_code = try await (async ir.analyze( + const analyzed_code = try ir.analyze( comp, unanalyzed_code, expected_type, - ) catch unreachable); + ); errdefer analyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { - std.debug.warn("analyzed:\n"); + std.debug.warn("analyzed:\n", .{}); analyzed_code.dump(); } @@ -1046,17 +1010,17 @@ pub const Compilation = struct { tree_scope: *Scope.AstTree, scope: *Scope, comptime_node: *ast.Node.Comptime, - ) !void { + ) BuildError!void { const void_type = Type.Void.get(comp); defer void_type.base.base.deref(comp); - const analyzed_code = (await (async genAndAnalyzeCode( + const analyzed_code = genAndAnalyzeCode( comp, tree_scope, scope, comptime_node.expr, &void_type.base, - ) catch unreachable)) catch |err| switch (err) { + ) catch |err| switch (err) { // This poison value should not cause the errdefers to run. It simply means // that comp.compile_errors is populated. error.SemanticAnalysisFailed => return {}, @@ -1069,66 +1033,63 @@ pub const Compilation = struct { self: *Compilation, decl: *Decl, locked_table: *Decl.Table, - ) !void { + ) BuildError!void { const is_export = decl.isExported(decl.tree_scope.tree); if (is_export) { - try self.prelink_group.call(verifyUniqueSymbol, self, decl); - try self.prelink_group.call(resolveDecl, self, decl); + try self.prelink_group.call(verifyUniqueSymbol, .{ self, decl }); + try self.prelink_group.call(resolveDecl, .{ self, decl }); } const gop = try locked_table.getOrPut(decl.name); if (gop.found_existing) { - try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name); + try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", .{decl.name}); // TODO note: other definition here } else { gop.kv.value = decl; } } - fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void { + fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void { const text = try std.fmt.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromScope(self, tree_scope, span, text); errdefer msg.destroy(); - try self.prelink_group.call(addCompileErrorAsync, self, msg); + try self.prelink_group.call(addCompileErrorAsync, .{ self, msg }); } - fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: ...) !void { + fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void { const text = try std.fmt.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromCli(self, realpath, text); errdefer msg.destroy(); - try self.prelink_group.call(addCompileErrorAsync, self, msg); + try self.prelink_group.call(addCompileErrorAsync, .{ self, msg }); } async fn addCompileErrorAsync( self: *Compilation, msg: *Msg, - ) !void { + ) BuildError!void { errdefer msg.destroy(); - const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + const compile_errors = self.compile_errors.acquire(); defer compile_errors.release(); try compile_errors.value.append(msg); } - async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) !void { - const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); + async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) BuildError!void { + const exported_symbol_names = self.exported_symbol_names.acquire(); defer exported_symbol_names.release(); if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { - try self.addCompileError( - decl.tree_scope, - decl.getSpan(), - "exported symbol collision: '{}'", + try self.addCompileError(decl.tree_scope, decl.getSpan(), "exported symbol collision: '{}'", .{ decl.name, - ); + }); // TODO add error note showing location of other symbol } } @@ -1165,22 +1126,21 @@ pub const Compilation = struct { // get a head start on looking for the native libc if (self.target == Target.Native and self.override_libc == null) { - try self.deinit_group.call(startFindingNativeLibC, self); + try self.deinit_group.call(startFindingNativeLibC, .{self}); } } return link_lib; } - /// cancels itself so no need to await or cancel the promise. async fn startFindingNativeLibC(self: *Compilation) void { - await (async self.loop.yield() catch unreachable); + event.Loop.startCpuBoundOperation(); // we don't care if it fails, we're just trying to kick off the future resolution - _ = (await (async self.zig_compiler.getNativeLibC() catch unreachable)) catch return; + _ = self.zig_compiler.getNativeLibC() catch return; } /// General Purpose Allocator. Must free when done. fn gpa(self: Compilation) *mem.Allocator { - return self.loop.allocator; + return self.zig_compiler.allocator; } /// Arena Allocator. Automatically freed when the Compilation is destroyed. @@ -1190,14 +1150,14 @@ pub const Compilation = struct { /// If the temporary directory for this compilation has not been created, it creates it. /// Then it creates a random file name in that dir and returns it. - pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { - const tmp_dir = try await (async self.getTmpDir() catch unreachable); - const file_prefix = await (async self.getRandomFileName() catch unreachable); + pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { + const tmp_dir = try self.getTmpDir(); + const file_prefix = self.getRandomFileName(); - const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); + const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix }); defer self.gpa().free(file_name); - const full_path = try std.fs.path.join(self.gpa(), [_][]const u8{ tmp_dir, file_name[0..] }); + const full_path = try std.fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] }); errdefer self.gpa().free(full_path); return Buffer.fromOwnedSlice(self.gpa(), full_path); @@ -1206,24 +1166,24 @@ pub const Compilation = struct { /// If the temporary directory for this Compilation has not been created, creates it. /// Then returns it. The directory is unique to this Compilation and cleaned up when /// the Compilation deinitializes. - async fn getTmpDir(self: *Compilation) ![]const u8 { - if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*; - self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable); + fn getTmpDir(self: *Compilation) ![]const u8 { + if (self.tmp_dir.start()) |ptr| return ptr.*; + self.tmp_dir.data = self.getTmpDirImpl(); self.tmp_dir.resolve(); return self.tmp_dir.data; } - async fn getTmpDirImpl(self: *Compilation) ![]u8 { - const comp_dir_name = await (async self.getRandomFileName() catch unreachable); + fn getTmpDirImpl(self: *Compilation) ![]u8 { + const comp_dir_name = self.getRandomFileName(); const zig_dir_path = try getZigDir(self.gpa()); defer self.gpa().free(zig_dir_path); - const tmp_dir = try std.fs.path.join(self.arena(), [_][]const u8{ zig_dir_path, comp_dir_name[0..] }); + const tmp_dir = try std.fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] }); try std.fs.makePath(self.gpa(), tmp_dir); return tmp_dir; } - async fn getRandomFileName(self: *Compilation) [12]u8 { + fn getRandomFileName(self: *Compilation) [12]u8 { // here we replace the standard +/ with -_ so that it can be used in a file name const b64_fs_encoder = std.base64.Base64Encoder.init( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", @@ -1233,14 +1193,14 @@ pub const Compilation = struct { var rand_bytes: [9]u8 = undefined; { - const held = await (async self.zig_compiler.prng.acquire() catch unreachable); + const held = self.zig_compiler.prng.acquire(); defer held.release(); held.value.random.bytes(rand_bytes[0..]); } var result: [12]u8 = undefined; - b64_fs_encoder.encode(result[0..], rand_bytes); + b64_fs_encoder.encode(result[0..], &rand_bytes); return result; } @@ -1249,34 +1209,37 @@ pub const Compilation = struct { } /// Returns a value which has been ref()'d once - async fn analyzeConstValue( + fn analyzeConstValue( comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node, expected_type: *Type, ) !*Value { - const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable); + var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode)); + defer comp.gpa().destroy(frame); + frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type); + const analyzed_code = try await frame; defer analyzed_code.destroy(comp.gpa()); return analyzed_code.getCompTimeResult(comp); } - async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type { + fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type { const meta_type = &Type.MetaType.get(comp).base; defer meta_type.base.deref(comp); - const result_val = try await (async comp.analyzeConstValue(tree_scope, scope, node, meta_type) catch unreachable); + const result_val = try comp.analyzeConstValue(tree_scope, scope, node, meta_type); errdefer result_val.base.deref(comp); return result_val.cast(Type).?; } /// This declaration has been blessed as going into the final code generation. - pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { - if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; + pub async fn resolveDecl(comp: *Compilation, decl: *Decl) BuildError!void { + if (decl.resolution.start()) |ptr| return ptr.*; - decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.data = try generateDecl(comp, decl); decl.resolution.resolve(); return decl.resolution.data; } @@ -1293,26 +1256,26 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib } /// The function that actually does the generation. -async fn generateDecl(comp: *Compilation, decl: *Decl) !void { +fn generateDecl(comp: *Compilation, decl: *Decl) !void { switch (decl.id) { - Decl.Id.Var => @panic("TODO"), - Decl.Id.Fn => { + .Var => @panic("TODO"), + .Fn => { const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); - return await (async generateDeclFn(comp, fn_decl) catch unreachable); + return generateDeclFn(comp, fn_decl); }, - Decl.Id.CompTime => @panic("TODO"), + .CompTime => @panic("TODO"), } } -async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { +fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const tree_scope = fn_decl.base.tree_scope; - const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable); + const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl); const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); defer fndef_scope.base.deref(comp); - const fn_type = try await (async analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable); + const fn_type = try analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto); defer fn_type.base.base.deref(comp); var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); @@ -1321,7 +1284,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { // The Decl.Fn owns the initial 1 reference count const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); - fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; + fn_decl.value = .{ .Fn = fn_val }; symbol_name_consumed = true; // Define local parameter variables @@ -1332,7 +1295,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { try comp.addCompileError(tree_scope, Span{ .first = param_decl.firstToken(), .last = param_decl.type_node.firstToken(), - }, "missing parameter name"); + }, "missing parameter name", .{}); return error.SemanticAnalysisFailed; }; const param_name = tree_scope.tree.tokenSlice(name_token); @@ -1356,29 +1319,32 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { try fn_type.non_key.Normal.variable_list.append(var_scope); } - const analyzed_code = try await (async comp.genAndAnalyzeCode( + var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode)); + defer comp.gpa().destroy(frame); + frame.* = async comp.genAndAnalyzeCode( tree_scope, fn_val.child_scope, body_node, fn_type.key.data.Normal.return_type, - ) catch unreachable); + ); + const analyzed_code = try await frame; errdefer analyzed_code.destroy(comp.gpa()); assert(fn_val.block_scope != null); // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. - try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); - try comp.prelink_group.call(addFnToLinkSet, comp, fn_val); + try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code }); + try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val }); } -async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { +async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void { fn_val.base.ref(); defer fn_val.base.deref(comp); fn_val.link_set_node.data = fn_val; - const held = await (async comp.fn_link_set.acquire() catch unreachable); + const held = comp.fn_link_set.acquire(); defer held.release(); held.value.append(fn_val.link_set_node); @@ -1388,17 +1354,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 { return std.fs.getAppDataDir(allocator, "zig"); } -async fn analyzeFnType( +fn analyzeFnType( comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, fn_proto: *ast.Node.FnProto, ) !*Type.Fn { const return_type_node = switch (fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |n| n, - ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, + .Explicit => |n| n, + .InferErrorSet => |n| n, }; - const return_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, return_type_node) catch unreachable); + const return_type = try comp.analyzeTypeExpr(tree_scope, scope, return_type_node); return_type.base.deref(comp); var params = ArrayList(Type.Fn.Param).init(comp.gpa()); @@ -1414,7 +1380,7 @@ async fn analyzeFnType( var it = fn_proto.params.iterator(0); while (it.next()) |param_node_ptr| { const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?; - const param_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node) catch unreachable); + const param_type = try comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node); errdefer param_type.base.deref(comp); try params.append(Type.Fn.Param{ .typ = param_type, @@ -1430,7 +1396,7 @@ async fn analyzeFnType( .return_type = return_type, .params = params.toOwnedSlice(), .is_var_args = false, // TODO - .cc = Type.Fn.CallingConvention.Auto, // TODO + .cc = .Unspecified, // TODO }, }, }; @@ -1443,20 +1409,20 @@ async fn analyzeFnType( comp.gpa().free(key.data.Normal.params); }; - const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable); + const fn_type = try Type.Fn.get(comp, key); key_consumed = true; errdefer fn_type.base.base.deref(comp); return fn_type; } -async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { - const fn_type = try await (async analyzeFnType( +fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { + const fn_type = try analyzeFnType( comp, fn_decl.base.tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto, - ) catch unreachable); + ); defer fn_type.base.base.deref(comp); var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); @@ -1465,17 +1431,6 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { // The Decl.Fn owns the initial 1 reference count const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name); - fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; + fn_decl.value = .{ .FnProto = fn_proto_val }; symbol_name_consumed = true; } - -// TODO these are hacks which should probably be solved by the language -fn getAwaitResult(allocator: *Allocator, handle: var) @typeInfo(@typeOf(handle)).Promise.child.? { - var result: ?@typeInfo(@typeOf(handle)).Promise.child.? = null; - cancel (async getAwaitResultAsync(handle, &result) catch unreachable); - return result.?; -} - -async fn getAwaitResultAsync(handle: var, out: *?@typeInfo(@typeOf(handle)).Promise.child.?) void { - out.* = await handle; -} diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index 1af06dea3..e68a1458d 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -29,7 +29,7 @@ pub const Decl = struct { pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { switch (base.id) { - Id.Fn => { + .Fn => { const fn_decl = @fieldParentPtr(Fn, "base", base); return fn_decl.isExported(tree); }, @@ -39,7 +39,7 @@ pub const Decl = struct { pub fn getSpan(base: *const Decl) errmsg.Span { switch (base.id) { - Id.Fn => { + .Fn => { const fn_decl = @fieldParentPtr(Fn, "base", base); const fn_proto = fn_decl.fn_proto; const start = fn_proto.fn_token; @@ -69,21 +69,18 @@ pub const Decl = struct { pub const Fn = struct { base: Decl, - value: Val, - fn_proto: *ast.Node.FnProto, - - // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous - pub const Val = union(enum) { - Unresolved: void, + value: union(enum) { + Unresolved, Fn: *Value.Fn, FnProto: *Value.FnProto, - }; + }, + fn_proto: *ast.Node.FnProto, pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { const token = tree.tokens.at(tok_index); break :x switch (token.id) { - Token.Id.Extern => tree.tokenSlicePtr(token), + .Extern => tree.tokenSlicePtr(token), else => null, }; } else null; @@ -92,7 +89,7 @@ pub const Decl = struct { pub fn isExported(self: Fn, tree: *ast.Tree) bool { if (self.fn_proto.extern_export_inline_token) |tok_index| { const token = tree.tokens.at(tok_index); - return token.id == Token.Id.Keyword_export; + return token.id == .Keyword_export; } else { return false; } diff --git a/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig index f802765bc..c5b0f0cd1 100644 --- a/src-self-hosted/dep_tokenizer.zig +++ b/src-self-hosted/dep_tokenizer.zig @@ -38,7 +38,7 @@ pub const Tokenizer = struct { }, .target => |*target| switch (char) { '\t', '\n', '\r', ' ' => { - return self.errorIllegalChar(self.index, char, "invalid target"); + return self.errorIllegalChar(self.index, char, "invalid target", .{}); }, '$' => { self.state = State{ .target_dollar_sign = target.* }; @@ -59,7 +59,7 @@ pub const Tokenizer = struct { }, .target_reverse_solidus => |*target| switch (char) { '\t', '\n', '\r' => { - return self.errorIllegalChar(self.index, char, "bad target escape"); + return self.errorIllegalChar(self.index, char, "bad target escape", .{}); }, ' ', '#', '\\' => { try target.appendByte(char); @@ -84,7 +84,7 @@ pub const Tokenizer = struct { break; // advance }, else => { - return self.errorIllegalChar(self.index, char, "expecting '$'"); + return self.errorIllegalChar(self.index, char, "expecting '$'", .{}); }, }, .target_colon => |*target| switch (char) { @@ -161,7 +161,7 @@ pub const Tokenizer = struct { break; // advance }, else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line"); + return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); }, }, .rhs_continuation_linefeed => switch (char) { @@ -170,7 +170,7 @@ pub const Tokenizer = struct { break; // advance }, else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line"); + return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); }, }, .prereq_quote => |*prereq| switch (char) { @@ -231,7 +231,7 @@ pub const Tokenizer = struct { return Token{ .id = .prereq, .bytes = bytes }; }, else => { - return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line"); + return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{}); }, }, } @@ -249,13 +249,13 @@ pub const Tokenizer = struct { .rhs_continuation_linefeed, => {}, .target => |target| { - return self.errorPosition(idx, target.toSlice(), "incomplete target"); + return self.errorPosition(idx, target.toSlice(), "incomplete target", .{}); }, .target_reverse_solidus, .target_dollar_sign, => { const index = self.index - 1; - return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape"); + return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape", .{}); }, .target_colon => |target| { const bytes = target.toSlice(); @@ -278,7 +278,7 @@ pub const Tokenizer = struct { self.state = State{ .lhs = {} }; }, .prereq_quote => |prereq| { - return self.errorPosition(idx, prereq.toSlice(), "incomplete quoted prerequisite"); + return self.errorPosition(idx, prereq.toSlice(), "incomplete quoted prerequisite", .{}); }, .prereq => |prereq| { const bytes = prereq.toSlice(); @@ -299,29 +299,29 @@ pub const Tokenizer = struct { return null; } - fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: ...) Error { + fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: var) Error { self.error_text = (try std.Buffer.allocPrint(&self.arena.allocator, fmt, args)).toSlice(); return Error.InvalidInput; } - fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: ...) Error { + fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: var) Error { var buffer = try std.Buffer.initSize(&self.arena.allocator, 0); std.fmt.format(&buffer, anyerror, std.Buffer.append, fmt, args) catch {}; try buffer.append(" '"); var out = makeOutput(std.Buffer.append, &buffer); try printCharValues(&out, bytes); try buffer.append("'"); - std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", position - (bytes.len - 1)) catch {}; + std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", .{position - (bytes.len - 1)}) catch {}; self.error_text = buffer.toSlice(); return Error.InvalidInput; } - fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: ...) Error { + fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: var) Error { var buffer = try std.Buffer.initSize(&self.arena.allocator, 0); try buffer.append("illegal char "); var out = makeOutput(std.Buffer.append, &buffer); try printUnderstandableChar(&out, char); - std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", position) catch {}; + std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", .{position}) catch {}; if (fmt.len != 0) std.fmt.format(&buffer, anyerror, std.Buffer.append, ": " ++ fmt, args) catch {}; self.error_text = buffer.toSlice(); return Error.InvalidInput; @@ -837,7 +837,7 @@ test "error prereq - continuation expecting end-of-line" { // - tokenize input, emit textual representation, and compare to expect fn depTokenizer(input: []const u8, expect: []const u8) !void { - var arena_allocator = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator); const arena = &arena_allocator.allocator; defer arena_allocator.deinit(); @@ -894,7 +894,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void { fn printLabel(out: var, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; - var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", label, bytes.len); + var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{label, bytes.len}); try out.write(text); var i: usize = text.len; const end = 79; @@ -992,16 +992,16 @@ fn printHexValue(out: var, value: u64, width: u8) !void { fn printCharValues(out: var, bytes: []const u8) !void { for (bytes) |b| { - try out.write([_]u8{printable_char_tab[b]}); + try out.write(&[_]u8{printable_char_tab[b]}); } } fn printUnderstandableChar(out: var, char: u8) !void { if (!std.ascii.isPrint(char) or char == ' ') { - std.fmt.format(out.context, anyerror, out.output, "\\x{X:2}", char) catch {}; + std.fmt.format(out.context, anyerror, out.output, "\\x{X:2}", .{char}) catch {}; } else { try out.write("'"); - try out.write([_]u8{printable_char_tab[char]}); + try out.write(&[_]u8{printable_char_tab[char]}); try out.write("'"); } } @@ -1021,8 +1021,8 @@ comptime { // output: must be a function that takes a `self` idiom parameter // and a bytes parameter // context: must be that self -fn makeOutput(output: var, context: var) Output(@typeOf(output)) { - return Output(@typeOf(output)){ +fn makeOutput(output: var, context: var) Output(@TypeOf(output)) { + return Output(@TypeOf(output)){ .output = output, .context = context, }; diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index eef5817d5..c1ebf0058 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -62,17 +62,17 @@ pub const Msg = struct { pub fn destroy(self: *Msg) void { switch (self.data) { - Data.Cli => |cli| { + .Cli => |cli| { cli.allocator.free(self.text); cli.allocator.free(self.realpath); cli.allocator.destroy(self); }, - Data.PathAndTree => |path_and_tree| { + .PathAndTree => |path_and_tree| { path_and_tree.allocator.free(self.text); path_and_tree.allocator.free(self.realpath); path_and_tree.allocator.destroy(self); }, - Data.ScopeAndComp => |scope_and_comp| { + .ScopeAndComp => |scope_and_comp| { scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation); scope_and_comp.compilation.gpa().free(self.text); scope_and_comp.compilation.gpa().free(self.realpath); @@ -83,11 +83,11 @@ pub const Msg = struct { fn getAllocator(self: *const Msg) *mem.Allocator { switch (self.data) { - Data.Cli => |cli| return cli.allocator, - Data.PathAndTree => |path_and_tree| { + .Cli => |cli| return cli.allocator, + .PathAndTree => |path_and_tree| { return path_and_tree.allocator; }, - Data.ScopeAndComp => |scope_and_comp| { + .ScopeAndComp => |scope_and_comp| { return scope_and_comp.compilation.gpa(); }, } @@ -95,11 +95,11 @@ pub const Msg = struct { pub fn getTree(self: *const Msg) *ast.Tree { switch (self.data) { - Data.Cli => unreachable, - Data.PathAndTree => |path_and_tree| { + .Cli => unreachable, + .PathAndTree => |path_and_tree| { return path_and_tree.tree; }, - Data.ScopeAndComp => |scope_and_comp| { + .ScopeAndComp => |scope_and_comp| { return scope_and_comp.tree_scope.tree; }, } @@ -107,9 +107,9 @@ pub const Msg = struct { pub fn getSpan(self: *const Msg) Span { return switch (self.data) { - Data.Cli => unreachable, - Data.PathAndTree => |path_and_tree| path_and_tree.span, - Data.ScopeAndComp => |scope_and_comp| scope_and_comp.span, + .Cli => unreachable, + .PathAndTree => |path_and_tree| path_and_tree.span, + .ScopeAndComp => |scope_and_comp| scope_and_comp.span, }; } @@ -230,8 +230,8 @@ pub const Msg = struct { pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void { switch (msg.data) { - Data.Cli => { - try stream.print("{}:-:-: error: {}\n", msg.realpath, msg.text); + .Cli => { + try stream.print("{}:-:-: error: {}\n", .{ msg.realpath, msg.text }); return; }, else => {}, @@ -254,24 +254,22 @@ pub const Msg = struct { const start_loc = tree.tokenLocationPtr(0, first_token); const end_loc = tree.tokenLocationPtr(first_token.end, last_token); if (!color_on) { - try stream.print( - "{}:{}:{}: error: {}\n", + try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, msg.text, - ); + }); return; } - try stream.print( - "{}:{}:{}: error: {}\n{}\n", + try stream.print("{}:{}:{}: error: {}\n{}\n", .{ path, start_loc.line + 1, start_loc.column + 1, msg.text, tree.source[start_loc.line_start..start_loc.line_end], - ); + }); try stream.writeByteNTimes(' ', start_loc.column); try stream.writeByteNTimes('~', last_token.end - first_token.start); try stream.write("\n"); @@ -279,9 +277,9 @@ pub const Msg = struct { pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void { const color_on = switch (color) { - Color.Auto => file.isTty(), - Color.On => true, - Color.Off => false, + .Auto => file.isTty(), + .On => true, + .Off => false, }; var stream = &file.outStream().stream; return msg.printToStream(stream, color_on); diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index d5204f031..67181b40a 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -8,10 +8,10 @@ const warn = std.debug.warn; /// Caller must free result pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { - const test_zig_dir = try fs.path.join(allocator, [_][]const u8{ test_path, "lib", "zig" }); + const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); errdefer allocator.free(test_zig_dir); - const test_index_file = try fs.path.join(allocator, [_][]const u8{ test_zig_dir, "std", "std.zig" }); + const test_index_file = try fs.path.join(allocator, &[_][]const u8{ test_zig_dir, "std", "std.zig" }); defer allocator.free(test_index_file); var file = try fs.File.openRead(test_index_file); @@ -48,7 +48,7 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { \\Unable to find zig lib directory: {}. \\Reinstall Zig or use --zig-install-prefix. \\ - , @errorName(err)); + , .{@errorName(err)}); return error.ZigLibDirNotFound; }; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index df4d436b5..a8f4980fa 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const Compilation = @import("compilation.zig").Compilation; const Scope = @import("scope.zig").Scope; const ast = std.zig.ast; @@ -33,16 +32,16 @@ pub const IrVal = union(enum) { pub fn dump(self: IrVal) void { switch (self) { - IrVal.Unknown => std.debug.warn("Unknown"), - IrVal.KnownType => |typ| { - std.debug.warn("KnownType("); + .Unknown => std.debug.warn("Unknown", .{}), + .KnownType => |typ| { + std.debug.warn("KnownType(", .{}); typ.dump(); - std.debug.warn(")"); + std.debug.warn(")", .{}); }, - IrVal.KnownValue => |value| { - std.debug.warn("KnownValue("); + .KnownValue => |value| { + std.debug.warn("KnownValue(", .{}); value.dump(); - std.debug.warn(")"); + std.debug.warn(")", .{}); }, } } @@ -91,9 +90,9 @@ pub const Inst = struct { inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { const T = @field(Inst, @memberName(Id, i)); - std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); + std.debug.warn("#{} = {}(", .{ base.debug_id, @tagName(base.id) }); @fieldParentPtr(T, "base", base).dump(); - std.debug.warn(")"); + std.debug.warn(")", .{}); return; } } @@ -111,39 +110,39 @@ pub const Inst = struct { unreachable; } - pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { + pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { switch (base.id) { - Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira), - Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira), - Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira), - Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable), - Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable), - Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira), - Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira), - Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira), - Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira), - Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), - Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable), - Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable), - Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable), + .Return => return @fieldParentPtr(Return, "base", base).analyze(ira), + .Const => return @fieldParentPtr(Const, "base", base).analyze(ira), + .Call => return @fieldParentPtr(Call, "base", base).analyze(ira), + .DeclRef => return @fieldParentPtr(DeclRef, "base", base).analyze(ira), + .Ref => return @fieldParentPtr(Ref, "base", base).analyze(ira), + .DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira), + .CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira), + .Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira), + .Br => return @fieldParentPtr(Br, "base", base).analyze(ira), + .AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), + .PtrType => return @fieldParentPtr(PtrType, "base", base).analyze(ira), + .VarPtr => return @fieldParentPtr(VarPtr, "base", base).analyze(ira), + .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).analyze(ira), } } pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?*llvm.Value) { switch (base.id) { - Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), - Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), - Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), - Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val), - Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val), - Id.DeclRef => unreachable, - Id.PtrType => unreachable, - Id.Ref => @panic("TODO"), - Id.DeclVar => @panic("TODO"), - Id.CheckVoidStmt => @panic("TODO"), - Id.Phi => @panic("TODO"), - Id.Br => @panic("TODO"), - Id.AddImplicitReturnType => @panic("TODO"), + .Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), + .Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), + .Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), + .VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val), + .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val), + .DeclRef => unreachable, + .PtrType => unreachable, + .Ref => @panic("TODO"), + .DeclVar => @panic("TODO"), + .CheckVoidStmt => @panic("TODO"), + .Phi => @panic("TODO"), + .Br => @panic("TODO"), + .AddImplicitReturnType => @panic("TODO"), } } @@ -165,7 +164,7 @@ pub const Inst = struct { param.ref_count -= 1; const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { - IrVal.Unknown => return error.SemanticAnalysisFailed, + .Unknown => return error.SemanticAnalysisFailed, else => return child, } } @@ -174,7 +173,7 @@ pub const Inst = struct { if (self.isCompTime()) { return self.val.KnownValue; } else { - try ira.addCompileError(self.span, "unable to evaluate constant expression"); + try ira.addCompileError(self.span, "unable to evaluate constant expression", .{}); return error.SemanticAnalysisFailed; } } @@ -213,9 +212,9 @@ pub const Inst = struct { /// asserts that the type is known fn getKnownType(self: *Inst) *Type { switch (self.val) { - IrVal.KnownType => |typ| return typ, - IrVal.KnownValue => |value| return value.typ, - IrVal.Unknown => unreachable, + .KnownType => |typ| return typ, + .KnownValue => |value| return value.typ, + .Unknown => unreachable, } } @@ -225,14 +224,14 @@ pub const Inst = struct { pub fn isNoReturn(base: *const Inst) bool { switch (base.val) { - IrVal.Unknown => return false, - IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn, - IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn, + .Unknown => return false, + .KnownValue => |x| return x.typ.id == .NoReturn, + .KnownType => |typ| return typ.id == .NoReturn, } } pub fn isCompTime(base: *const Inst) bool { - return base.val == IrVal.KnownValue; + return base.val == .KnownValue; } pub fn linkToParent(self: *Inst, parent: *Inst) void { @@ -270,11 +269,11 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(self: *const Call) void { - std.debug.warn("#{}(", self.params.fn_ref.debug_id); + std.debug.warn("#{}(", .{self.params.fn_ref.debug_id}); for (self.params.args) |arg| { - std.debug.warn("#{},", arg.debug_id); + std.debug.warn("#{},", .{arg.debug_id}); } - std.debug.warn(")"); + std.debug.warn(")", .{}); } pub fn hasSideEffects(self: *const Call) bool { @@ -285,19 +284,17 @@ pub const Inst = struct { const fn_ref = try self.params.fn_ref.getAsParam(); const fn_ref_type = fn_ref.getKnownType(); const fn_type = fn_ref_type.cast(Type.Fn) orelse { - try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name); + try ira.addCompileError(fn_ref.span, "type '{}' not a function", .{fn_ref_type.name}); return error.SemanticAnalysisFailed; }; const fn_type_param_count = fn_type.paramCount(); if (fn_type_param_count != self.params.args.len) { - try ira.addCompileError( - self.base.span, - "expected {} arguments, found {}", + try ira.addCompileError(self.base.span, "expected {} arguments, found {}", .{ fn_type_param_count, self.params.args.len, - ); + }); return error.SemanticAnalysisFailed; } @@ -322,7 +319,7 @@ pub const Inst = struct { } const llvm_cc = llvm.CCallConv; - const fn_inline = llvm.FnInline.Auto; + const call_attr = llvm.CallAttr.Auto; return llvm.BuildCall( ofile.builder, @@ -330,8 +327,8 @@ pub const Inst = struct { args.ptr, @intCast(c_uint, args.len), llvm_cc, - fn_inline, - c"", + call_attr, + "", ) orelse error.OutOfMemory; } }; @@ -376,7 +373,7 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.NoReturn; pub fn dump(self: *const Return) void { - std.debug.warn("#{}", self.params.return_value.debug_id); + std.debug.warn("#{}", .{self.params.return_value.debug_id}); } pub fn hasSideEffects(self: *const Return) bool { @@ -423,7 +420,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst { + pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); if (ira.getCompTimeValOrNullUndefOk(target)) |val| { @@ -441,13 +438,13 @@ pub const Inst = struct { .volatility = self.params.volatility, }); const elem_type = target.getKnownType(); - const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ .child_type = elem_type, .mut = self.params.mut, .vol = self.params.volatility, - .size = Type.Pointer.Size.One, - .alignment = Type.Pointer.Align.Abi, - }) catch unreachable); + .size = .One, + .alignment = .Abi, + }); // TODO: potentially set the hint that this is a stack pointer. But it might not be - this // could be a ref of a global, for example new_inst.val = IrVal{ .KnownType = &ptr_type.base }; @@ -473,26 +470,26 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { - (await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) { + pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { + (ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return error.SemanticAnalysisFailed, }; switch (self.params.decl.id) { - Decl.Id.CompTime => unreachable, - Decl.Id.Var => return error.Unimplemented, - Decl.Id.Fn => { + .CompTime => unreachable, + .Var => return error.Unimplemented, + .Fn => { const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl); const decl_val = switch (fn_decl.value) { - Decl.Fn.Val.Unresolved => unreachable, - Decl.Fn.Val.Fn => |fn_val| &fn_val.base, - Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base, + .Unresolved => unreachable, + .Fn => |fn_val| &fn_val.base, + .FnProto => |fn_proto| &fn_proto.base, }; switch (self.params.lval) { - LVal.None => { + .None => { return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val); }, - LVal.Ptr => return error.Unimplemented, + .Ptr => return error.Unimplemented, } }, } @@ -510,30 +507,30 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const VarPtr) void { - std.debug.warn("{}", inst.params.var_scope.name); + std.debug.warn("{}", .{inst.params.var_scope.name}); } pub fn hasSideEffects(inst: *const VarPtr) bool { return false; } - pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { + pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { switch (self.params.var_scope.data) { - Scope.Var.Data.Const => @panic("TODO"), - Scope.Var.Data.Param => |param| { + .Const => @panic("TODO"), + .Param => |param| { const new_inst = try ira.irb.build( Inst.VarPtr, self.base.scope, self.base.span, Inst.VarPtr.Params{ .var_scope = self.params.var_scope }, ); - const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ .child_type = param.typ, - .mut = Type.Pointer.Mut.Const, - .vol = Type.Pointer.Vol.Non, - .size = Type.Pointer.Size.One, - .alignment = Type.Pointer.Align.Abi, - }) catch unreachable); + .mut = .Const, + .vol = .Non, + .size = .One, + .alignment = .Abi, + }); new_inst.val = IrVal{ .KnownType = &ptr_type.base }; return new_inst; }, @@ -542,8 +539,8 @@ pub const Inst = struct { pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value { switch (self.params.var_scope.data) { - Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass - Scope.Var.Data.Param => |param| return param.llvm_value, + .Const => unreachable, // turned into Inst.Const in analyze pass + .Param => |param| return param.llvm_value, } } }; @@ -564,11 +561,11 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { + pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); const target_type = target.getKnownType(); - if (target_type.id != Type.Id.Pointer) { - try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name); + if (target_type.id != .Pointer) { + try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", .{target_type.name}); return error.SemanticAnalysisFailed; } const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type); @@ -646,7 +643,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { + pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { const child_type = try self.params.child_type.getAsConstType(ira); // if (child_type->id == TypeTableEntryIdUnreachable) { // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); @@ -659,15 +656,15 @@ pub const Inst = struct { const amt = try align_inst.getAsConstAlign(ira); break :blk Type.Pointer.Align{ .Override = amt }; } else blk: { - break :blk Type.Pointer.Align{ .Abi = {} }; + break :blk .Abi; }; - const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ .child_type = child_type, .mut = self.params.mut, .vol = self.params.vol, .size = self.params.size, .alignment = alignment, - }) catch unreachable); + }); ptr_type.base.base.deref(ira.irb.comp); return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base); @@ -706,7 +703,7 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(self: *const CheckVoidStmt) void { - std.debug.warn("#{}", self.params.target.debug_id); + std.debug.warn("#{}", .{self.params.target.debug_id}); } pub fn hasSideEffects(inst: *const CheckVoidStmt) bool { @@ -715,8 +712,8 @@ pub const Inst = struct { pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); - if (target.getKnownType().id != Type.Id.Void) { - try ira.addCompileError(self.base.span, "expression value is ignored"); + if (target.getKnownType().id != .Void) { + try ira.addCompileError(self.base.span, "expression value is ignored", .{}); return error.SemanticAnalysisFailed; } return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); @@ -802,7 +799,7 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const AddImplicitReturnType) void { - std.debug.warn("#{}", inst.params.target.debug_id); + std.debug.warn("#{}", .{inst.params.target.debug_id}); } pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool { @@ -827,7 +824,7 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const TestErr) void { - std.debug.warn("#{}", inst.params.target.debug_id); + std.debug.warn("#{}", .{inst.params.target.debug_id}); } pub fn hasSideEffects(inst: *const TestErr) bool { @@ -838,7 +835,7 @@ pub const Inst = struct { const target = try self.params.target.getAsParam(); const target_type = target.getKnownType(); switch (target_type.id) { - Type.Id.ErrorUnion => { + .ErrorUnion => { return error.Unimplemented; // if (instr_is_comptime(value)) { // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad); @@ -868,7 +865,7 @@ pub const Inst = struct { // ir_build_test_err_from(&ira->new_irb, &instruction->base, value); // return ira->codegen->builtin_types.entry_bool; }, - Type.Id.ErrorSet => { + .ErrorSet => { return ira.irb.buildConstBool(self.base.scope, self.base.span, true); }, else => { @@ -889,7 +886,7 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const TestCompTime) void { - std.debug.warn("#{}", inst.params.target.debug_id); + std.debug.warn("#{}", .{inst.params.target.debug_id}); } pub fn hasSideEffects(inst: *const TestCompTime) bool { @@ -928,7 +925,7 @@ pub const Variable = struct { pub const BasicBlock = struct { ref_count: usize, - name_hint: [*]const u8, // must be a C string literal + name_hint: [*:0]const u8, debug_id: usize, scope: *Scope, instruction_list: std.ArrayList(*Inst), @@ -972,11 +969,11 @@ pub const Code = struct { pub fn dump(self: *Code) void { var bb_i: usize = 0; for (self.basic_block_list.toSliceConst()) |bb| { - std.debug.warn("{s}_{}:\n", bb.name_hint, bb.debug_id); + std.debug.warn("{s}_{}:\n", .{ bb.name_hint, bb.debug_id }); for (bb.instruction_list.toSliceConst()) |instr| { - std.debug.warn(" "); + std.debug.warn(" ", .{}); instr.dump(); - std.debug.warn("\n"); + std.debug.warn("\n", .{}); } } } @@ -994,6 +991,7 @@ pub const Code = struct { self.tree_scope, ret_value.span, "unable to evaluate constant expression", + .{}, ); return error.SemanticAnalysisFailed; } else if (inst.hasSideEffects()) { @@ -1001,6 +999,7 @@ pub const Code = struct { self.tree_scope, inst.span, "unable to evaluate constant expression", + .{}, ); return error.SemanticAnalysisFailed; } @@ -1052,7 +1051,7 @@ pub const Builder = struct { } /// No need to clean up resources thanks to the arena allocator. - pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { + pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock { const basic_block = try self.arena().create(BasicBlock); basic_block.* = BasicBlock{ .ref_count = 0, @@ -1079,122 +1078,130 @@ pub const Builder = struct { self.current_basic_block = basic_block; } + pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { + const alloc = irb.comp.gpa(); + var frame = try alloc.create(@Frame(genNode)); + defer alloc.destroy(frame); + frame.* = async irb.genNode(node, scope, lval); + return await frame; + } + pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { switch (node.id) { - ast.Node.Id.Root => unreachable, - ast.Node.Id.Use => unreachable, - ast.Node.Id.TestDecl => unreachable, - ast.Node.Id.VarDecl => return error.Unimplemented, - ast.Node.Id.Defer => return error.Unimplemented, - ast.Node.Id.InfixOp => return error.Unimplemented, - ast.Node.Id.PrefixOp => { + .Root => unreachable, + .Use => unreachable, + .TestDecl => unreachable, + .VarDecl => return error.Unimplemented, + .Defer => return error.Unimplemented, + .InfixOp => return error.Unimplemented, + .PrefixOp => { const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node); switch (prefix_op.op) { - ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented, - ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented, - ast.Node.PrefixOp.Op.Await => return error.Unimplemented, - ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented, - ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented, - ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented, - ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented, - ast.Node.PrefixOp.Op.Negation => return error.Unimplemented, - ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented, - ast.Node.PrefixOp.Op.Resume => return error.Unimplemented, - ast.Node.PrefixOp.Op.PtrType => |ptr_info| { - const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable); + .AddressOf => return error.Unimplemented, + .ArrayType => |n| return error.Unimplemented, + .Await => return error.Unimplemented, + .BitNot => return error.Unimplemented, + .BoolNot => return error.Unimplemented, + .Cancel => return error.Unimplemented, + .OptionalType => return error.Unimplemented, + .Negation => return error.Unimplemented, + .NegationWrap => return error.Unimplemented, + .Resume => return error.Unimplemented, + .PtrType => |ptr_info| { + const inst = try irb.genPtrType(prefix_op, ptr_info, scope); return irb.lvalWrap(scope, inst, lval); }, - ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented, - ast.Node.PrefixOp.Op.Try => return error.Unimplemented, + .SliceType => |ptr_info| return error.Unimplemented, + .Try => return error.Unimplemented, } }, - ast.Node.Id.SuffixOp => { + .SuffixOp => { const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); switch (suffix_op.op) { - @TagType(ast.Node.SuffixOp.Op).Call => |*call| { - const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable); + .Call => |*call| { + const inst = try irb.genCall(suffix_op, call, scope); return irb.lvalWrap(scope, inst, lval); }, - @TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented, - @TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented, - @TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented, - @TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented, - @TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented, - @TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented, + .ArrayAccess => |n| return error.Unimplemented, + .Slice => |slice| return error.Unimplemented, + .ArrayInitializer => |init_list| return error.Unimplemented, + .StructInitializer => |init_list| return error.Unimplemented, + .Deref => return error.Unimplemented, + .UnwrapOptional => return error.Unimplemented, } }, - ast.Node.Id.Switch => return error.Unimplemented, - ast.Node.Id.While => return error.Unimplemented, - ast.Node.Id.For => return error.Unimplemented, - ast.Node.Id.If => return error.Unimplemented, - ast.Node.Id.ControlFlowExpression => { + .Switch => return error.Unimplemented, + .While => return error.Unimplemented, + .For => return error.Unimplemented, + .If => return error.Unimplemented, + .ControlFlowExpression => { const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node); - return await (async irb.genControlFlowExpr(control_flow_expr, scope, lval) catch unreachable); + return irb.genControlFlowExpr(control_flow_expr, scope, lval); }, - ast.Node.Id.Suspend => return error.Unimplemented, - ast.Node.Id.VarType => return error.Unimplemented, - ast.Node.Id.ErrorType => return error.Unimplemented, - ast.Node.Id.FnProto => return error.Unimplemented, - ast.Node.Id.PromiseType => return error.Unimplemented, - ast.Node.Id.IntegerLiteral => { + .Suspend => return error.Unimplemented, + .VarType => return error.Unimplemented, + .ErrorType => return error.Unimplemented, + .FnProto => return error.Unimplemented, + .AnyFrameType => return error.Unimplemented, + .IntegerLiteral => { const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node); return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval); }, - ast.Node.Id.FloatLiteral => return error.Unimplemented, - ast.Node.Id.StringLiteral => { + .FloatLiteral => return error.Unimplemented, + .StringLiteral => { const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node); - const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable); + const inst = try irb.genStrLit(str_lit, scope); return irb.lvalWrap(scope, inst, lval); }, - ast.Node.Id.MultilineStringLiteral => return error.Unimplemented, - ast.Node.Id.CharLiteral => return error.Unimplemented, - ast.Node.Id.BoolLiteral => return error.Unimplemented, - ast.Node.Id.NullLiteral => return error.Unimplemented, - ast.Node.Id.UndefinedLiteral => return error.Unimplemented, - ast.Node.Id.Unreachable => return error.Unimplemented, - ast.Node.Id.Identifier => { + .MultilineStringLiteral => return error.Unimplemented, + .CharLiteral => return error.Unimplemented, + .BoolLiteral => return error.Unimplemented, + .NullLiteral => return error.Unimplemented, + .UndefinedLiteral => return error.Unimplemented, + .Unreachable => return error.Unimplemented, + .Identifier => { const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node); - return await (async irb.genIdentifier(identifier, scope, lval) catch unreachable); + return irb.genIdentifier(identifier, scope, lval); }, - ast.Node.Id.GroupedExpression => { + .GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); - return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable); + return irb.genNodeRecursive(grouped_expr.expr, scope, lval); }, - ast.Node.Id.BuiltinCall => return error.Unimplemented, - ast.Node.Id.ErrorSetDecl => return error.Unimplemented, - ast.Node.Id.ContainerDecl => return error.Unimplemented, - ast.Node.Id.Asm => return error.Unimplemented, - ast.Node.Id.Comptime => return error.Unimplemented, - ast.Node.Id.Block => { + .BuiltinCall => return error.Unimplemented, + .ErrorSetDecl => return error.Unimplemented, + .ContainerDecl => return error.Unimplemented, + .Asm => return error.Unimplemented, + .Comptime => return error.Unimplemented, + .Block => { const block = @fieldParentPtr(ast.Node.Block, "base", node); - const inst = try await (async irb.genBlock(block, scope) catch unreachable); + const inst = try irb.genBlock(block, scope); return irb.lvalWrap(scope, inst, lval); }, - ast.Node.Id.DocComment => return error.Unimplemented, - ast.Node.Id.SwitchCase => return error.Unimplemented, - ast.Node.Id.SwitchElse => return error.Unimplemented, - ast.Node.Id.Else => return error.Unimplemented, - ast.Node.Id.Payload => return error.Unimplemented, - ast.Node.Id.PointerPayload => return error.Unimplemented, - ast.Node.Id.PointerIndexPayload => return error.Unimplemented, - ast.Node.Id.ContainerField => return error.Unimplemented, - ast.Node.Id.ErrorTag => return error.Unimplemented, - ast.Node.Id.AsmInput => return error.Unimplemented, - ast.Node.Id.AsmOutput => return error.Unimplemented, - ast.Node.Id.ParamDecl => return error.Unimplemented, - ast.Node.Id.FieldInitializer => return error.Unimplemented, - ast.Node.Id.EnumLiteral => return error.Unimplemented, + .DocComment => return error.Unimplemented, + .SwitchCase => return error.Unimplemented, + .SwitchElse => return error.Unimplemented, + .Else => return error.Unimplemented, + .Payload => return error.Unimplemented, + .PointerPayload => return error.Unimplemented, + .PointerIndexPayload => return error.Unimplemented, + .ContainerField => return error.Unimplemented, + .ErrorTag => return error.Unimplemented, + .AsmInput => return error.Unimplemented, + .AsmOutput => return error.Unimplemented, + .ParamDecl => return error.Unimplemented, + .FieldInitializer => return error.Unimplemented, + .EnumLiteral => return error.Unimplemented, } } - async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { - const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable); + fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { + const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None); const args = try irb.arena().alloc(*Inst, call.params.len); var it = call.params.iterator(0); var i: usize = 0; while (it.next()) |arg_node_ptr| : (i += 1) { - args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable); + args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None); } //bool is_async = node->data.fn_call_expr.is_async; @@ -1215,7 +1222,7 @@ pub const Builder = struct { //return ir_lval_wrap(irb, scope, fn_call, lval); } - async fn genPtrType( + fn genPtrType( irb: *Builder, prefix_op: *ast.Node.PrefixOp, ptr_info: ast.Node.PrefixOp.PtrInfo, @@ -1239,7 +1246,7 @@ pub const Builder = struct { //} else { // align_value = nullptr; //} - const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable); + const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None); //uint32_t bit_offset_start = 0; //if (node->data.pointer_type.bit_offset_start != nullptr) { @@ -1273,9 +1280,9 @@ pub const Builder = struct { return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{ .child_type = child_type, - .mut = Type.Pointer.Mut.Mut, - .vol = Type.Pointer.Vol.Non, - .size = Type.Pointer.Size.Many, + .mut = .Mut, + .vol = .Non, + .size = .Many, .alignment = null, }); } @@ -1287,15 +1294,15 @@ pub const Builder = struct { var scope = target_scope; while (true) { switch (scope.id) { - Scope.Id.CompTime => return true, - Scope.Id.FnDef => return false, - Scope.Id.Decls => unreachable, - Scope.Id.Root => unreachable, - Scope.Id.AstTree => unreachable, - Scope.Id.Block, - Scope.Id.Defer, - Scope.Id.DeferExpr, - Scope.Id.Var, + .CompTime => return true, + .FnDef => return false, + .Decls => unreachable, + .Root => unreachable, + .AstTree => unreachable, + .Block, + .Defer, + .DeferExpr, + .Var, => scope = scope.parent.?, } } @@ -1308,9 +1315,9 @@ pub const Builder = struct { var rest: []const u8 = undefined; if (int_token.len >= 3 and int_token[0] == '0') { base = switch (int_token[1]) { - 'b' => u8(2), - 'o' => u8(8), - 'x' => u8(16), + 'b' => 2, + 'o' => 8, + 'x' => 16, else => unreachable, }; rest = int_token[2..]; @@ -1340,7 +1347,7 @@ pub const Builder = struct { return inst; } - pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { + pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token); const src_span = Span.token(str_lit.token); @@ -1352,7 +1359,7 @@ pub const Builder = struct { irb.code.tree_scope, src_span, "invalid character in string literal: '{c}'", - str_token[bad_index], + .{str_token[bad_index]}, ); return error.SemanticAnalysisFailed; }, @@ -1366,23 +1373,23 @@ pub const Builder = struct { buf[buf.len - 1] = 0; // next make an array value - const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); + const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf); buf_cleaned = true; defer array_val.base.deref(irb.comp); // then make a pointer value pointing at the first element - const ptr_val = try await (async Value.Ptr.createArrayElemPtr( + const ptr_val = try Value.Ptr.createArrayElemPtr( irb.comp, array_val, - Type.Pointer.Mut.Const, - Type.Pointer.Size.Many, + .Const, + .Many, 0, - ) catch unreachable); + ); defer ptr_val.base.deref(irb.comp); return irb.buildConstValue(scope, src_span, &ptr_val.base); } else { - const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); + const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf); buf_cleaned = true; defer array_val.base.deref(irb.comp); @@ -1390,7 +1397,7 @@ pub const Builder = struct { } } - pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { + pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); const outer_block_scope = &block_scope.base; @@ -1410,7 +1417,7 @@ pub const Builder = struct { if (block.label) |label| { block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena()); block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); - block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); + block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); block_scope.is_comptime = try irb.buildConstBool( parent_scope, Span.token(block.lbrace), @@ -1438,7 +1445,7 @@ pub const Builder = struct { child_scope = &defer_child_scope.base; continue; } - const statement_value = try await (async irb.genNode(statement_node, child_scope, LVal.None) catch unreachable); + const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None); is_continuation_unreachable = statement_value.isNoReturn(); if (is_continuation_unreachable) { @@ -1481,7 +1488,7 @@ pub const Builder = struct { try block_scope.incoming_values.append( try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true), ); - _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); + _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit); _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{ .dest_block = block_scope.end_block, @@ -1496,26 +1503,27 @@ pub const Builder = struct { }); } - _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); + _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit); return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); } - pub async fn genControlFlowExpr( + pub fn genControlFlowExpr( irb: *Builder, control_flow_expr: *ast.Node.ControlFlowExpression, scope: *Scope, lval: LVal, ) !*Inst { switch (control_flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |arg| return error.Unimplemented, - ast.Node.ControlFlowExpression.Kind.Continue => |arg| return error.Unimplemented, - ast.Node.ControlFlowExpression.Kind.Return => { + .Break => |arg| return error.Unimplemented, + .Continue => |arg| return error.Unimplemented, + .Return => { const src_span = Span.token(control_flow_expr.ltoken); if (scope.findFnDef() == null) { try irb.comp.addCompileError( irb.code.tree_scope, src_span, "return expression outside function definition", + .{}, ); return error.SemanticAnalysisFailed; } @@ -1526,6 +1534,7 @@ pub const Builder = struct { irb.code.tree_scope, src_span, "cannot return from defer expression", + .{}, ); scope_defer_expr.reported_err = true; } @@ -1534,7 +1543,7 @@ pub const Builder = struct { const outer_scope = irb.begin_scope.?; const return_value = if (control_flow_expr.rhs) |rhs| blk: { - break :blk try await (async irb.genNode(rhs, scope, LVal.None) catch unreachable); + break :blk try irb.genNodeRecursive(rhs, scope, .None); } else blk: { break :blk try irb.buildConstVoid(scope, src_span, true); }; @@ -1542,10 +1551,10 @@ pub const Builder = struct { const defer_counts = irb.countDefers(scope, outer_scope); const have_err_defers = defer_counts.error_exit != 0; if (have_err_defers or irb.comp.have_err_ret_tracing) { - const err_block = try irb.createBasicBlock(scope, c"ErrRetErr"); - const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk"); + const err_block = try irb.createBasicBlock(scope, "ErrRetErr"); + const ok_block = try irb.createBasicBlock(scope, "ErrRetOk"); if (!have_err_defers) { - _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); + _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit); } const is_err = try irb.build( @@ -1564,11 +1573,11 @@ pub const Builder = struct { .is_comptime = err_is_comptime, }); - const ret_stmt_block = try irb.createBasicBlock(scope, c"RetStmt"); + const ret_stmt_block = try irb.createBasicBlock(scope, "RetStmt"); try irb.setCursorAtEndAndAppendBlock(err_block); if (have_err_defers) { - _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit) catch unreachable); + _ = try irb.genDefersForBlock(scope, outer_scope, .ErrorExit); } if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) { _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{}); @@ -1580,7 +1589,7 @@ pub const Builder = struct { try irb.setCursorAtEndAndAppendBlock(ok_block); if (have_err_defers) { - _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); + _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit); } _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ .dest_block = ret_stmt_block, @@ -1590,14 +1599,14 @@ pub const Builder = struct { try irb.setCursorAtEndAndAppendBlock(ret_stmt_block); return irb.genAsyncReturn(scope, src_span, return_value, false); } else { - _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); + _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit); return irb.genAsyncReturn(scope, src_span, return_value, false); } }, } } - pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { + pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { const src_span = Span.token(identifier.token); const name = irb.code.tree_scope.tree.tokenSlice(identifier.token); @@ -1610,41 +1619,41 @@ pub const Builder = struct { // return &const_instruction->base; //} - if (await (async irb.comp.getPrimitiveType(name) catch unreachable)) |result| { + if (irb.comp.getPrimitiveType(name)) |result| { if (result) |primitive_type| { defer primitive_type.base.deref(irb.comp); switch (lval) { // if (lval == LValPtr) { // return ir_build_ref(irb, scope, node, value, false, false); - LVal.Ptr => return error.Unimplemented, - LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base), + .Ptr => return error.Unimplemented, + .None => return irb.buildConstValue(scope, src_span, &primitive_type.base), } } } else |err| switch (err) { error.Overflow => { - try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large"); + try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large", .{}); return error.SemanticAnalysisFailed; }, error.OutOfMemory => return error.OutOfMemory, } - switch (await (async irb.findIdent(scope, name) catch unreachable)) { - Ident.Decl => |decl| { + switch (irb.findIdent(scope, name)) { + .Decl => |decl| { return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{ .decl = decl, .lval = lval, }); }, - Ident.VarScope => |var_scope| { + .VarScope => |var_scope| { const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope }); switch (lval) { - LVal.Ptr => return var_ptr, - LVal.None => { + .Ptr => return var_ptr, + .None => { return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr }); }, } }, - Ident.NotFound => {}, + .NotFound => {}, } //if (node->owner->any_imports_failed) { @@ -1656,7 +1665,7 @@ pub const Builder = struct { // TODO put a variable of same name with invalid type in global scope // so that future references to this same name will find a variable with an invalid type - try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", name); + try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", .{name}); return error.SemanticAnalysisFailed; } @@ -1671,31 +1680,31 @@ pub const Builder = struct { var scope = inner_scope; while (scope != outer_scope) { switch (scope.id) { - Scope.Id.Defer => { + .Defer => { const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); switch (defer_scope.kind) { - Scope.Defer.Kind.ScopeExit => result.scope_exit += 1, - Scope.Defer.Kind.ErrorExit => result.error_exit += 1, + .ScopeExit => result.scope_exit += 1, + .ErrorExit => result.error_exit += 1, } scope = scope.parent orelse break; }, - Scope.Id.FnDef => break, + .FnDef => break, - Scope.Id.CompTime, - Scope.Id.Block, - Scope.Id.Decls, - Scope.Id.Root, - Scope.Id.Var, + .CompTime, + .Block, + .Decls, + .Root, + .Var, => scope = scope.parent orelse break, - Scope.Id.DeferExpr => unreachable, - Scope.Id.AstTree => unreachable, + .DeferExpr => unreachable, + .AstTree => unreachable, } } return result; } - async fn genDefersForBlock( + fn genDefersForBlock( irb: *Builder, inner_scope: *Scope, outer_scope: *Scope, @@ -1705,19 +1714,19 @@ pub const Builder = struct { var is_noreturn = false; while (true) { switch (scope.id) { - Scope.Id.Defer => { + .Defer => { const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); const generate = switch (defer_scope.kind) { - Scope.Defer.Kind.ScopeExit => true, - Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit, + .ScopeExit => true, + .ErrorExit => gen_kind == .ErrorExit, }; if (generate) { const defer_expr_scope = defer_scope.defer_expr_scope; - const instruction = try await (async irb.genNode( + const instruction = try irb.genNodeRecursive( defer_expr_scope.expr_node, &defer_expr_scope.base, - LVal.None, - ) catch unreachable); + .None, + ); if (instruction.isNoReturn()) { is_noreturn = true; } else { @@ -1730,32 +1739,32 @@ pub const Builder = struct { } } }, - Scope.Id.FnDef, - Scope.Id.Decls, - Scope.Id.Root, + .FnDef, + .Decls, + .Root, => return is_noreturn, - Scope.Id.CompTime, - Scope.Id.Block, - Scope.Id.Var, + .CompTime, + .Block, + .Var, => scope = scope.parent orelse return is_noreturn, - Scope.Id.DeferExpr => unreachable, - Scope.Id.AstTree => unreachable, + .DeferExpr => unreachable, + .AstTree => unreachable, } } } pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst { switch (lval) { - LVal.None => return instruction, - LVal.Ptr => { + .None => return instruction, + .Ptr => { // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{ .target = instruction, - .mut = Type.Pointer.Mut.Const, - .volatility = Type.Pointer.Vol.Non, + .mut = .Const, + .volatility = .Non, }); }, } @@ -1781,9 +1790,9 @@ pub const Builder = struct { .scope = scope, .debug_id = self.next_debug_id, .val = switch (I.ir_val_init) { - IrVal.Init.Unknown => IrVal.Unknown, - IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base }, - IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base }, + .Unknown => IrVal.Unknown, + .NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base }, + .Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base }, }, .ref_count = 0, .span = span, @@ -1798,7 +1807,7 @@ pub const Builder = struct { // Look at the params and ref() other instructions comptime var i = 0; inline while (i < @memberCount(I.Params)) : (i += 1) { - const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); + const FieldType = comptime @TypeOf(@field(@as(I.Params, undefined), @memberName(I.Params, i))); switch (FieldType) { *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self), *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self), @@ -1902,7 +1911,6 @@ pub const Builder = struct { ); } return error.Unimplemented; - } const Ident = union(enum) { @@ -1911,20 +1919,20 @@ pub const Builder = struct { VarScope: *Scope.Var, }; - async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { + fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { var s = scope; while (true) { switch (s.id) { - Scope.Id.Root => return Ident.NotFound, - Scope.Id.Decls => { + .Root => return .NotFound, + .Decls => { const decls = @fieldParentPtr(Scope.Decls, "base", s); - const locked_table = await (async decls.table.acquireRead() catch unreachable); + const locked_table = decls.table.acquireRead(); defer locked_table.release(); if (locked_table.value.get(name)) |entry| { return Ident{ .Decl = entry.value }; } }, - Scope.Id.Var => { + .Var => { const var_scope = @fieldParentPtr(Scope.Var, "base", s); if (mem.eql(u8, var_scope.name, name)) { return Ident{ .VarScope = var_scope }; @@ -2002,7 +2010,7 @@ const Analyze = struct { const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index); if (!next_instruction.is_generated) { - try ira.addCompileError(next_instruction.span, "unreachable code"); + try ira.addCompileError(next_instruction.span, "unreachable code", .{}); break; } ira.instruction_index += 1; @@ -2035,7 +2043,7 @@ const Analyze = struct { } } - fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { + fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: var) !void { return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args); } @@ -2047,7 +2055,7 @@ const Analyze = struct { fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst { const dest_type = optional_dest_type orelse return target; const from_type = target.getKnownType(); - if (from_type == dest_type or from_type.id == Type.Id.NoReturn) return target; + if (from_type == dest_type or from_type.id == .NoReturn) return target; return self.analyzeCast(target, target, dest_type); } @@ -2311,7 +2319,7 @@ const Analyze = struct { //} // cast from comptime-known integer to another integer where the value fits - if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: { + if (target.isCompTime() and (from_type.id == .Int or from_type.id == .ComptimeInt)) cast: { const target_val = target.val.KnownValue; const from_int = &target_val.cast(Value.Int).?.big_int; const fits = fits: { @@ -2324,12 +2332,10 @@ const Analyze = struct { break :cast; }; if (!fits) { - try ira.addCompileError( - source_instr.span, - "integer value '{}' cannot be stored in type '{}'", + try ira.addCompileError(source_instr.span, "integer value '{}' cannot be stored in type '{}'", .{ from_int, dest_type.name, - ); + }); return error.SemanticAnalysisFailed; } @@ -2492,12 +2498,10 @@ const Analyze = struct { // } //} - try ira.addCompileError( - source_instr.span, - "expected type '{}', found '{}'", + try ira.addCompileError(source_instr.span, "expected type '{}', found '{}'", .{ dest_type.name, from_type.name, - ); + }); //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, // buf_sprintf("expected type '%s', found '%s'", // buf_ptr(&wanted_type->name), @@ -2521,7 +2525,7 @@ const Analyze = struct { } }; -pub async fn gen( +pub fn gen( comp: *Compilation, body_node: *ast.Node, tree_scope: *Scope.AstTree, @@ -2530,11 +2534,11 @@ pub async fn gen( var irb = try Builder.init(comp, tree_scope, scope); errdefer irb.abort(); - const entry_block = try irb.createBasicBlock(scope, c"Entry"); + const entry_block = try irb.createBasicBlock(scope, "Entry"); entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin. try irb.setCursorAtEndAndAppendBlock(entry_block); - const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable); + const result = try irb.genNode(body_node, scope, .None); if (!result.isNoReturn()) { // no need for save_err_ret_addr because this cannot return error _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true); @@ -2543,7 +2547,7 @@ pub async fn gen( return irb.finish(); } -pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { +pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { const old_entry_bb = old_code.basic_block_list.at(0); var ira = try Analyze.init(comp, old_code.tree_scope, expected_type); @@ -2564,7 +2568,7 @@ pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) continue; } - const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable); + const return_inst = try old_instruction.analyze(&ira); assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point return_inst.linkToParent(old_instruction); // Note: if we ever modify the above to handle error.CompileError by continuing analysis, diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index e6236c567..701823c4b 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -1,9 +1,11 @@ const std = @import("std"); const builtin = @import("builtin"); const event = std.event; -const Target = @import("target.zig").Target; +const util = @import("util.zig"); +const Target = std.Target; const c = @import("c.zig"); const fs = std.fs; +const Allocator = std.mem.Allocator; /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { @@ -29,7 +31,7 @@ pub const LibCInstallation = struct { pub fn parse( self: *LibCInstallation, - allocator: *std.mem.Allocator, + allocator: *Allocator, libc_file: []const u8, stderr: *std.io.OutStream(fs.File.WriteError), ) !void { @@ -63,15 +65,15 @@ pub const LibCInstallation = struct { if (line.len == 0 or line[0] == '#') continue; var line_it = std.mem.separate(line, "="); const name = line_it.next() orelse { - try stderr.print("missing equal sign after field name\n"); + try stderr.print("missing equal sign after field name\n", .{}); return error.ParseError; }; const value = line_it.rest(); inline for (keys) |key, i| { if (std.mem.eql(u8, name, key)) { found_keys[i].found = true; - switch (@typeInfo(@typeOf(@field(self, key)))) { - builtin.TypeId.Optional => { + switch (@typeInfo(@TypeOf(@field(self, key)))) { + .Optional => { if (value.len == 0) { @field(self, key) = null; } else { @@ -81,7 +83,7 @@ pub const LibCInstallation = struct { }, else => { if (value.len == 0) { - try stderr.print("field cannot be empty: {}\n", key); + try stderr.print("field cannot be empty: {}\n", .{key}); return error.ParseError; } const dupe = try std.mem.dupe(allocator, u8, value); @@ -95,7 +97,7 @@ pub const LibCInstallation = struct { } for (found_keys) |found_key, i| { if (!found_key.found) { - try stderr.print("missing field: {}\n", keys[i]); + try stderr.print("missing field: {}\n", .{keys[i]}); return error.ParseError; } } @@ -103,6 +105,11 @@ pub const LibCInstallation = struct { pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void { @setEvalBranchQuota(4000); + const lib_dir = self.lib_dir orelse ""; + const static_lib_dir = self.static_lib_dir orelse ""; + const msvc_lib_dir = self.msvc_lib_dir orelse ""; + const kernel32_lib_dir = self.kernel32_lib_dir orelse ""; + const dynamic_linker_path = self.dynamic_linker_path orelse util.getDynamicLinkerPath(Target{ .Native = {} }); try out.print( \\# The directory that contains `stdlib.h`. \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` @@ -130,21 +137,14 @@ pub const LibCInstallation = struct { \\# Only needed when targeting Linux. \\dynamic_linker_path={} \\ - , - self.include_dir, - self.lib_dir orelse "", - self.static_lib_dir orelse "", - self.msvc_lib_dir orelse "", - self.kernel32_lib_dir orelse "", - self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(), - ); + , .{ self.include_dir, lib_dir, static_lib_dir, msvc_lib_dir, kernel32_lib_dir, dynamic_linker_path }); } /// Finds the default, native libc. - pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void { + pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void { self.initEmpty(); - var group = event.Group(FindError!void).init(loop); - errdefer group.deinit(); + var group = event.Group(FindError!void).init(allocator); + errdefer group.wait() catch {}; var windows_sdk: ?*c.ZigWindowsSDK = null; errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); @@ -156,11 +156,11 @@ pub const LibCInstallation = struct { windows_sdk = sdk; if (sdk.msvc_lib_dir_ptr != 0) { - self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); + self.msvc_lib_dir = try std.mem.dupe(allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); } - try group.call(findNativeKernel32LibDir, self, loop, sdk); - try group.call(findNativeIncludeDirWindows, self, loop, sdk); - try group.call(findNativeLibDirWindows, self, loop, sdk); + try group.call(findNativeKernel32LibDir, .{ allocator, self, sdk }); + try group.call(findNativeIncludeDirWindows, .{ self, allocator, sdk }); + try group.call(findNativeLibDirWindows, .{ self, allocator, sdk }); }, c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, c.ZigFindWindowsSdkError.NotFound => return error.NotFound, @@ -168,20 +168,20 @@ pub const LibCInstallation = struct { } }, .linux => { - try group.call(findNativeIncludeDirLinux, self, loop); - try group.call(findNativeLibDirLinux, self, loop); - try group.call(findNativeStaticLibDir, self, loop); - try group.call(findNativeDynamicLinker, self, loop); + try group.call(findNativeIncludeDirLinux, .{ self, allocator }); + try group.call(findNativeLibDirLinux, .{ self, allocator }); + try group.call(findNativeStaticLibDir, .{ self, allocator }); + try group.call(findNativeDynamicLinker, .{ self, allocator }); }, .macosx, .freebsd, .netbsd => { - self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); + self.include_dir = try std.mem.dupe(allocator, u8, "/usr/include"); }, else => @compileError("unimplemented: find libc for this OS"), } - return await (async group.wait() catch unreachable); + return group.wait(); } - async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void { + async fn findNativeIncludeDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void { const cc_exe = std.os.getenv("CC") orelse "cc"; const argv = [_][]const u8{ cc_exe, @@ -191,7 +191,7 @@ pub const LibCInstallation = struct { "/dev/null", }; // TODO make this use event loop - const errorable_result = std.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); + const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024); const exec_result = if (std.debug.runtime_safety) blk: { break :blk errorable_result catch unreachable; } else blk: { @@ -201,12 +201,12 @@ pub const LibCInstallation = struct { }; }; defer { - loop.allocator.free(exec_result.stdout); - loop.allocator.free(exec_result.stderr); + allocator.free(exec_result.stdout); + allocator.free(exec_result.stderr); } switch (exec_result.term) { - std.ChildProcess.Term.Exited => |code| { + .Exited => |code| { if (code != 0) return error.CCompilerExitCode; }, else => { @@ -215,7 +215,7 @@ pub const LibCInstallation = struct { } var it = std.mem.tokenize(exec_result.stderr, "\n\r"); - var search_paths = std.ArrayList([]const u8).init(loop.allocator); + var search_paths = std.ArrayList([]const u8).init(allocator); defer search_paths.deinit(); while (it.next()) |line| { if (line.len != 0 and line[0] == ' ') { @@ -231,11 +231,11 @@ pub const LibCInstallation = struct { while (path_i < search_paths.len) : (path_i += 1) { const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); - const stdlib_path = try fs.path.join(loop.allocator, [_][]const u8{ search_path, "stdlib.h" }); - defer loop.allocator.free(stdlib_path); + const stdlib_path = try fs.path.join(allocator, &[_][]const u8{ search_path, "stdlib.h" }); + defer allocator.free(stdlib_path); if (try fileExists(stdlib_path)) { - self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); + self.include_dir = try std.mem.dupe(allocator, u8, search_path); return; } } @@ -243,23 +243,23 @@ pub const LibCInstallation = struct { return error.LibCStdLibHeaderNotFound; } - async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void { + async fn findNativeIncludeDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) !void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); - var result_buf = try std.Buffer.initSize(loop.allocator, 0); + var result_buf = try std.Buffer.initSize(allocator, 0); defer result_buf.deinit(); for (searches) |search| { result_buf.shrink(0); const stream = &std.io.BufferOutStream.init(&result_buf).stream; - try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); + try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version }); const stdlib_path = try fs.path.join( - loop.allocator, + allocator, [_][]const u8{ result_buf.toSliceConst(), "stdlib.h" }, ); - defer loop.allocator.free(stdlib_path); + defer allocator.free(stdlib_path); if (try fileExists(stdlib_path)) { self.include_dir = result_buf.toOwnedSlice(); @@ -270,28 +270,28 @@ pub const LibCInstallation = struct { return error.LibCStdLibHeaderNotFound; } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + async fn findNativeLibDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); - var result_buf = try std.Buffer.initSize(loop.allocator, 0); + var result_buf = try std.Buffer.initSize(allocator, 0); defer result_buf.deinit(); for (searches) |search| { result_buf.shrink(0); const stream = &std.io.BufferOutStream.init(&result_buf).stream; - try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version); + try stream.print("{}\\Lib\\{}\\ucrt\\", .{ search.path, search.version }); switch (builtin.arch) { - builtin.Arch.i386 => try stream.write("x86"), - builtin.Arch.x86_64 => try stream.write("x64"), - builtin.Arch.aarch64 => try stream.write("arm"), + .i386 => try stream.write("x86"), + .x86_64 => try stream.write("x64"), + .aarch64 => try stream.write("arm"), else => return error.UnsupportedArchitecture, } const ucrt_lib_path = try fs.path.join( - loop.allocator, + allocator, [_][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }, ); - defer loop.allocator.free(ucrt_lib_path); + defer allocator.free(ucrt_lib_path); if (try fileExists(ucrt_lib_path)) { self.lib_dir = result_buf.toOwnedSlice(); return; @@ -300,15 +300,15 @@ pub const LibCInstallation = struct { return error.LibCRuntimeNotFound; } - async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void { - self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable); + async fn findNativeLibDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void { + self.lib_dir = try ccPrintFileName(allocator, "crt1.o", true); } - async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { - self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable); + async fn findNativeStaticLibDir(self: *LibCInstallation, allocator: *Allocator) FindError!void { + self.static_lib_dir = try ccPrintFileName(allocator, "crtbegin.o", true); } - async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void { + async fn findNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator) FindError!void { var dyn_tests = [_]DynTest{ DynTest{ .name = "ld-linux-x86-64.so.2", @@ -319,12 +319,12 @@ pub const LibCInstallation = struct { .result = null, }, }; - var group = event.Group(FindError!void).init(loop); - errdefer group.deinit(); + var group = event.Group(FindError!void).init(allocator); + errdefer group.wait() catch {}; for (dyn_tests) |*dyn_test| { - try group.call(testNativeDynamicLinker, self, loop, dyn_test); + try group.call(testNativeDynamicLinker, .{ self, allocator, dyn_test }); } - try await (async group.wait() catch unreachable); + try group.wait(); for (dyn_tests) |*dyn_test| { if (dyn_test.result) |result| { self.dynamic_linker_path = result; @@ -338,8 +338,8 @@ pub const LibCInstallation = struct { result: ?[]const u8, }; - async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void { - if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| { + async fn testNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator, dyn_test: *DynTest) FindError!void { + if (ccPrintFileName(allocator, dyn_test.name, false)) |result| { dyn_test.result = result; return; } else |err| switch (err) { @@ -348,28 +348,28 @@ pub const LibCInstallation = struct { } } - async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + async fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); - var result_buf = try std.Buffer.initSize(loop.allocator, 0); + var result_buf = try std.Buffer.initSize(allocator, 0); defer result_buf.deinit(); for (searches) |search| { result_buf.shrink(0); const stream = &std.io.BufferOutStream.init(&result_buf).stream; - try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version); + try stream.print("{}\\Lib\\{}\\um\\", .{ search.path, search.version }); switch (builtin.arch) { - builtin.Arch.i386 => try stream.write("x86\\"), - builtin.Arch.x86_64 => try stream.write("x64\\"), - builtin.Arch.aarch64 => try stream.write("arm\\"), + .i386 => try stream.write("x86\\"), + .x86_64 => try stream.write("x64\\"), + .aarch64 => try stream.write("arm\\"), else => return error.UnsupportedArchitecture, } const kernel32_path = try fs.path.join( - loop.allocator, + allocator, [_][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }, ); - defer loop.allocator.free(kernel32_path); + defer allocator.free(kernel32_path); if (try fileExists(kernel32_path)) { self.kernel32_lib_dir = result_buf.toOwnedSlice(); return; @@ -380,7 +380,7 @@ pub const LibCInstallation = struct { fn initEmpty(self: *LibCInstallation) void { self.* = LibCInstallation{ - .include_dir = ([*]const u8)(undefined)[0..0], + .include_dir = @as([*]const u8, undefined)[0..0], .lib_dir = null, .static_lib_dir = null, .msvc_lib_dir = null, @@ -391,15 +391,15 @@ pub const LibCInstallation = struct { }; /// caller owns returned memory -async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 { +fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 { const cc_exe = std.os.getenv("CC") orelse "cc"; - const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file); - defer loop.allocator.free(arg1); + const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{o_file}); + defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; // TODO This simulates evented I/O for the child process exec - await (async loop.yield() catch unreachable); - const errorable_result = std.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); + event.Loop.startCpuBoundOperation(); + const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024); const exec_result = if (std.debug.runtime_safety) blk: { break :blk errorable_result catch unreachable; } else blk: { @@ -409,8 +409,8 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo }; }; defer { - loop.allocator.free(exec_result.stdout); - loop.allocator.free(exec_result.stderr); + allocator.free(exec_result.stdout); + allocator.free(exec_result.stderr); } switch (exec_result.term) { .Exited => |code| { @@ -425,9 +425,9 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; if (want_dirname) { - return std.mem.dupe(loop.allocator, u8, dirname); + return std.mem.dupe(allocator, u8, dirname); } else { - return std.mem.dupe(loop.allocator, u8, line); + return std.mem.dupe(allocator, u8, line); } } diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 1f5f07eff..efb83710d 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -1,17 +1,17 @@ const std = @import("std"); const mem = std.mem; const c = @import("c.zig"); -const builtin = @import("builtin"); -const ObjectFormat = builtin.ObjectFormat; const Compilation = @import("compilation.zig").Compilation; -const Target = @import("target.zig").Target; +const Target = std.Target; +const ObjectFormat = Target.ObjectFormat; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const assert = std.debug.assert; +const util = @import("util.zig"); const Context = struct { comp: *Compilation, arena: std.heap.ArenaAllocator, - args: std.ArrayList([*]const u8), + args: std.ArrayList([*:0]const u8), link_in_crt: bool, link_err: error{OutOfMemory}!void, @@ -21,51 +21,49 @@ const Context = struct { out_file_path: std.Buffer, }; -pub async fn link(comp: *Compilation) !void { +pub fn link(comp: *Compilation) !void { var ctx = Context{ .comp = comp, .arena = std.heap.ArenaAllocator.init(comp.gpa()), .args = undefined, - .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe, + .link_in_crt = comp.haveLibC() and comp.kind == .Exe, .link_err = {}, .link_msg = undefined, .libc = undefined, .out_file_path = undefined, }; defer ctx.arena.deinit(); - ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); + ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator); ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); - if (comp.link_out_file) |out_file| { - ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file); - } else { - ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst()); - switch (comp.kind) { - Compilation.Kind.Exe => { - try ctx.out_file_path.append(comp.target.exeFileExt()); - }, - Compilation.Kind.Lib => { - try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static)); - }, - Compilation.Kind.Obj => { - try ctx.out_file_path.append(comp.target.objFileExt()); - }, - } + ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst()); + switch (comp.kind) { + .Exe => { + try ctx.out_file_path.append(comp.target.exeFileExt()); + }, + .Lib => { + try ctx.out_file_path.append(if (comp.is_static) comp.target.staticLibSuffix() else comp.target.dynamicLibSuffix()); + }, + .Obj => { + try ctx.out_file_path.append(comp.target.oFileExt()); + }, } // even though we're calling LLD as a library it thinks the first // argument is its own exe name - try ctx.args.append(c"lld"); + try ctx.args.append("lld"); if (comp.haveLibC()) { - ctx.libc = ctx.comp.override_libc orelse blk: { + // TODO https://github.com/ziglang/zig/issues/3190 + var libc = ctx.comp.override_libc orelse blk: { switch (comp.target) { Target.Native => { - break :blk (await (async comp.zig_compiler.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound; + break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound; }, else => return error.LibCRequiredButNotProvidedOrFound, } }; + ctx.libc = libc; } try constructLinkerArgs(&ctx); @@ -73,17 +71,17 @@ pub async fn link(comp: *Compilation) !void { if (comp.verbose_link) { for (ctx.args.toSliceConst()) |arg, i| { const space = if (i == 0) "" else " "; - std.debug.warn("{}{s}", space, arg); + std.debug.warn("{}{s}", .{ space, arg }); } - std.debug.warn("\n"); + std.debug.warn("\n", .{}); } - const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); + const extern_ofmt = toExternObjectFormatType(util.getObjectFormat(comp.target)); const args_slice = ctx.args.toSlice(); { // LLD is not thread-safe, so we grab a global lock. - const held = await (async comp.zig_compiler.lld_lock.acquire() catch unreachable); + const held = comp.zig_compiler.lld_lock.acquire(); defer held.release(); // Not evented I/O. LLD does its own multithreading internally. @@ -92,7 +90,7 @@ pub async fn link(comp: *Compilation) !void { // TODO capture these messages and pass them through the system, reporting them through the // event system instead of printing them directly here. // perhaps try to parse and understand them. - std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); + std.debug.warn("{}\n", .{ctx.link_msg.toSliceConst()}); } return error.LinkFailed; } @@ -121,21 +119,21 @@ fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void { fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType { return switch (ofmt) { - ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat, - ObjectFormat.coff => c.ZigLLVM_COFF, - ObjectFormat.elf => c.ZigLLVM_ELF, - ObjectFormat.macho => c.ZigLLVM_MachO, - ObjectFormat.wasm => c.ZigLLVM_Wasm, + .unknown => .ZigLLVM_UnknownObjectFormat, + .coff => .ZigLLVM_COFF, + .elf => .ZigLLVM_ELF, + .macho => .ZigLLVM_MachO, + .wasm => .ZigLLVM_Wasm, }; } fn constructLinkerArgs(ctx: *Context) !void { - switch (ctx.comp.target.getObjectFormat()) { - ObjectFormat.unknown => unreachable, - ObjectFormat.coff => return constructLinkerArgsCoff(ctx), - ObjectFormat.elf => return constructLinkerArgsElf(ctx), - ObjectFormat.macho => return constructLinkerArgsMachO(ctx), - ObjectFormat.wasm => return constructLinkerArgsWasm(ctx), + switch (util.getObjectFormat(ctx.comp.target)) { + .unknown => unreachable, + .coff => return constructLinkerArgsCoff(ctx), + .elf => return constructLinkerArgsElf(ctx), + .macho => return constructLinkerArgsMachO(ctx), + .wasm => return constructLinkerArgsWasm(ctx), } } @@ -145,7 +143,10 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // lj->args.append("-T"); // lj->args.append(g->linker_script); //} - try ctx.args.append(c"--gc-sections"); + try ctx.args.append("--gc-sections"); + if (ctx.comp.link_eh_frame_hdr) { + try ctx.args.append("--eh-frame-hdr"); + } //lj->args.append("-m"); //lj->args.append(getLDMOption(&g->zig_target)); @@ -154,10 +155,10 @@ fn constructLinkerArgsElf(ctx: *Context) !void { //bool shared = !g->is_static && is_lib; //Buf *soname = nullptr; if (ctx.comp.is_static) { - if (ctx.comp.target.isArmOrThumb()) { - try ctx.args.append(c"-Bstatic"); + if (util.isArmOrThumb(ctx.comp.target)) { + try ctx.args.append("-Bstatic"); } else { - try ctx.args.append(c"-static"); + try ctx.args.append("-static"); } } //} else if (shared) { @@ -170,8 +171,8 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); //} - try ctx.args.append(c"-o"); - try ctx.args.append(ctx.out_file_path.ptr()); + try ctx.args.append("-o"); + try ctx.args.append(ctx.out_file_path.toSliceConst()); if (ctx.link_in_crt) { const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o"; @@ -181,52 +182,22 @@ fn constructLinkerArgsElf(ctx: *Context) !void { try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino); } - //for (size_t i = 0; i < g->rpath_list.length; i += 1) { - // Buf *rpath = g->rpath_list.at(i); - // add_rpath(lj, rpath); - //} - //if (g->each_lib_rpath) { - // for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - // const char *lib_dir = g->lib_dirs.at(i); - // for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - // LinkLib *link_lib = g->link_libs_list.at(i); - // if (buf_eql_str(link_lib->name, "c")) { - // continue; - // } - // bool does_exist; - // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); - // if (os_file_exists(test_path, &does_exist) != ErrorNone) { - // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); - // } - // if (does_exist) { - // add_rpath(lj, buf_create_from_str(lib_dir)); - // break; - // } - // } - // } - //} - - //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - // const char *lib_dir = g->lib_dirs.at(i); - // lj->args.append("-L"); - // lj->args.append(lib_dir); - //} - if (ctx.comp.haveLibC()) { - try ctx.args.append(c"-L"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr); + try ctx.args.append("-L"); + // TODO addNullByte should probably return [:0]u8 + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr)); - try ctx.args.append(c"-L"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr); + try ctx.args.append("-L"); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr)); if (!ctx.comp.is_static) { const dl = blk: { if (ctx.libc.dynamic_linker_path) |dl| break :blk dl; - if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl; + if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl; return error.LibCMissingDynamicLinker; }; - try ctx.args.append(c"-dynamic-linker"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr); + try ctx.args.append("-dynamic-linker"); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr)); } } @@ -238,7 +209,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // .o files for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); @@ -272,23 +243,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // libc dep if (ctx.comp.haveLibC()) { if (ctx.comp.is_static) { - try ctx.args.append(c"--start-group"); - try ctx.args.append(c"-lgcc"); - try ctx.args.append(c"-lgcc_eh"); - try ctx.args.append(c"-lc"); - try ctx.args.append(c"-lm"); - try ctx.args.append(c"--end-group"); + try ctx.args.append("--start-group"); + try ctx.args.append("-lgcc"); + try ctx.args.append("-lgcc_eh"); + try ctx.args.append("-lc"); + try ctx.args.append("-lm"); + try ctx.args.append("--end-group"); } else { - try ctx.args.append(c"-lgcc"); - try ctx.args.append(c"--as-needed"); - try ctx.args.append(c"-lgcc_s"); - try ctx.args.append(c"--no-as-needed"); - try ctx.args.append(c"-lc"); - try ctx.args.append(c"-lm"); - try ctx.args.append(c"-lgcc"); - try ctx.args.append(c"--as-needed"); - try ctx.args.append(c"-lgcc_s"); - try ctx.args.append(c"--no-as-needed"); + try ctx.args.append("-lgcc"); + try ctx.args.append("--as-needed"); + try ctx.args.append("-lgcc_s"); + try ctx.args.append("--no-as-needed"); + try ctx.args.append("-lc"); + try ctx.args.append("-lm"); + try ctx.args.append("-lgcc"); + try ctx.args.append("--as-needed"); + try ctx.args.append("-lgcc_s"); + try ctx.args.append("--no-as-needed"); } } @@ -299,289 +270,171 @@ fn constructLinkerArgsElf(ctx: *Context) !void { } if (ctx.comp.target != Target.Native) { - try ctx.args.append(c"--allow-shlib-undefined"); - } - - if (ctx.comp.target.getOs() == .zen) { - try ctx.args.append(c"-e"); - try ctx.args.append(c"_start"); - - try ctx.args.append(c"--image-base=0x10000000"); + try ctx.args.append("--allow-shlib-undefined"); } } fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { - const full_path = try std.fs.path.join(&ctx.arena.allocator, [_][]const u8{ dirname, basename }); + const full_path = try std.fs.path.join(&ctx.arena.allocator, &[_][]const u8{ dirname, basename }); const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); - try ctx.args.append(full_path_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr)); } fn constructLinkerArgsCoff(ctx: *Context) !void { - try ctx.args.append(c"-NOLOGO"); + try ctx.args.append("-NOLOGO"); if (!ctx.comp.strip) { - try ctx.args.append(c"-DEBUG"); + try ctx.args.append("-DEBUG"); } switch (ctx.comp.target.getArch()) { - builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"), - builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"), - builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"), + .i386 => try ctx.args.append("-MACHINE:X86"), + .x86_64 => try ctx.args.append("-MACHINE:X64"), + .aarch64 => try ctx.args.append("-MACHINE:ARM"), else => return error.UnsupportedLinkArchitecture, } - if (ctx.comp.windows_subsystem_windows) { - try ctx.args.append(c"/SUBSYSTEM:windows"); - } else if (ctx.comp.windows_subsystem_console) { - try ctx.args.append(c"/SUBSYSTEM:console"); - } + const is_library = ctx.comp.kind == .Lib; - const is_library = ctx.comp.kind == Compilation.Kind.Lib; - - const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst()); - try ctx.args.append(out_arg.ptr); + const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.toSliceConst()}); + try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr)); if (ctx.comp.haveLibC()) { - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr); - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr); - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); } if (ctx.link_in_crt) { const lib_str = if (ctx.comp.is_static) "lib" else ""; - const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else ""; + const d_str = if (ctx.comp.build_mode == .Debug) "d" else ""; if (ctx.comp.is_static) { - const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str); - try ctx.args.append(cmt_lib_name.ptr); + const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str}); + try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr)); } else { - const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str); - try ctx.args.append(msvcrt_lib_name.ptr); + const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str}); + try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr)); } - const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str); - try ctx.args.append(vcruntime_lib_name.ptr); + const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{ + lib_str, + d_str, + }); + try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr)); - const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str); - try ctx.args.append(crt_lib_name.ptr); + const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str }); + try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr)); // Visual C++ 2015 Conformance Changes // https://msdn.microsoft.com/en-us/library/bb531344.aspx - try ctx.args.append(c"legacy_stdio_definitions.lib"); + try ctx.args.append("legacy_stdio_definitions.lib"); // msvcrt depends on kernel32 - try ctx.args.append(c"kernel32.lib"); + try ctx.args.append("kernel32.lib"); } else { - try ctx.args.append(c"-NODEFAULTLIB"); + try ctx.args.append("-NODEFAULTLIB"); if (!is_library) { - try ctx.args.append(c"-ENTRY:WinMainCRTStartup"); - // TODO - //if (g->have_winmain) { - // lj->args.append("-ENTRY:WinMain"); - //} else { - // lj->args.append("-ENTRY:WinMainCRTStartup"); - //} + try ctx.args.append("-ENTRY:WinMainCRTStartup"); } } if (is_library and !ctx.comp.is_static) { - try ctx.args.append(c"-DLL"); + try ctx.args.append("-DLL"); } - //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - // const char *lib_dir = g->lib_dirs.at(i); - // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); - //} - for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); switch (ctx.comp.kind) { - Compilation.Kind.Exe, Compilation.Kind.Lib => { + .Exe, .Lib => { if (!ctx.comp.haveLibC()) { @panic("TODO"); - //Buf *builtin_o_path = build_o(g, "builtin"); - //lj->args.append(buf_ptr(builtin_o_path)); } - - // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage - // TODO - //Buf *compiler_rt_o_path = build_compiler_rt(g); - //lj->args.append(buf_ptr(compiler_rt_o_path)); }, - Compilation.Kind.Obj => {}, + .Obj => {}, } - - //Buf *def_contents = buf_alloc(); - //ZigList gen_lib_args = {0}; - //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - // LinkLib *link_lib = g->link_libs_list.at(lib_i); - // if (buf_eql_str(link_lib->name, "c")) { - // continue; - // } - // if (link_lib->provided_explicitly) { - // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { - // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - // lj->args.append(buf_ptr(arg)); - // } - // else { - // lj->args.append(buf_ptr(link_lib->name)); - // } - // } else { - // buf_resize(def_contents, 0); - // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); - // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { - // Buf *symbol_name = link_lib->symbols.at(exp_i); - // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); - // } - // buf_appendf(def_contents, "\n"); - - // Buf *def_path = buf_alloc(); - // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); - // os_write_file(def_path, def_contents); - - // Buf *generated_lib_path = buf_alloc(); - // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); - - // gen_lib_args.resize(0); - // gen_lib_args.append("link"); - - // coff_append_machine_arg(g, &gen_lib_args); - // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); - // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); - // Buf diag = BUF_INIT; - // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { - // fprintf(stderr, "%s\n", buf_ptr(&diag)); - // exit(1); - // } - // lj->args.append(buf_ptr(generated_lib_path)); - // } - //} } fn constructLinkerArgsMachO(ctx: *Context) !void { - try ctx.args.append(c"-demangle"); + try ctx.args.append("-demangle"); if (ctx.comp.linker_rdynamic) { - try ctx.args.append(c"-export_dynamic"); + try ctx.args.append("-export_dynamic"); } - const is_lib = ctx.comp.kind == Compilation.Kind.Lib; + const is_lib = ctx.comp.kind == .Lib; const shared = !ctx.comp.is_static and is_lib; if (ctx.comp.is_static) { - try ctx.args.append(c"-static"); + try ctx.args.append("-static"); } else { - try ctx.args.append(c"-dynamic"); + try ctx.args.append("-dynamic"); } - //if (is_lib) { - // if (!g->is_static) { - // lj->args.append("-dylib"); - - // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); - // lj->args.append("-compatibility_version"); - // lj->args.append(buf_ptr(compat_vers)); - - // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - // g->version_major, g->version_minor, g->version_patch); - // lj->args.append("-current_version"); - // lj->args.append(buf_ptr(cur_vers)); - - // // TODO getting an error when running an executable when doing this rpath thing - // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", - // // buf_ptr(g->root_out_name), g->version_major); - // //lj->args.append("-install_name"); - // //lj->args.append(buf_ptr(dylib_install_name)); - - // if (buf_len(&lj->out_file) == 0) { - // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", - // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); - // } - // } - //} - - try ctx.args.append(c"-arch"); - const darwin_arch_str = try std.cstr.addNullByte( - &ctx.arena.allocator, - ctx.comp.target.getDarwinArchString(), - ); - try ctx.args.append(darwin_arch_str.ptr); + try ctx.args.append("-arch"); + try ctx.args.append(util.getDarwinArchString(ctx.comp.target)); const platform = try DarwinPlatform.get(ctx.comp); switch (platform.kind) { - DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"), - DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"), - DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"), + .MacOS => try ctx.args.append("-macosx_version_min"), + .IPhoneOS => try ctx.args.append("-iphoneos_version_min"), + .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"), } - const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); - try ctx.args.append(ver_str.ptr); + const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{ + platform.major, + platform.minor, + platform.micro, + }); + try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr)); - if (ctx.comp.kind == Compilation.Kind.Exe) { + if (ctx.comp.kind == .Exe) { if (ctx.comp.is_static) { - try ctx.args.append(c"-no_pie"); + try ctx.args.append("-no_pie"); } else { - try ctx.args.append(c"-pie"); + try ctx.args.append("-pie"); } } - try ctx.args.append(c"-o"); - try ctx.args.append(ctx.out_file_path.ptr()); - - //for (size_t i = 0; i < g->rpath_list.length; i += 1) { - // Buf *rpath = g->rpath_list.at(i); - // add_rpath(lj, rpath); - //} - //add_rpath(lj, &lj->out_file); + try ctx.args.append("-o"); + try ctx.args.append(ctx.out_file_path.toSliceConst()); if (shared) { - try ctx.args.append(c"-headerpad_max_install_names"); + try ctx.args.append("-headerpad_max_install_names"); } else if (ctx.comp.is_static) { - try ctx.args.append(c"-lcrt0.o"); + try ctx.args.append("-lcrt0.o"); } else { switch (platform.kind) { - DarwinPlatform.Kind.MacOS => { + .MacOS => { if (platform.versionLessThan(10, 5)) { - try ctx.args.append(c"-lcrt1.o"); + try ctx.args.append("-lcrt1.o"); } else if (platform.versionLessThan(10, 6)) { - try ctx.args.append(c"-lcrt1.10.5.o"); + try ctx.args.append("-lcrt1.10.5.o"); } else if (platform.versionLessThan(10, 8)) { - try ctx.args.append(c"-lcrt1.10.6.o"); + try ctx.args.append("-lcrt1.10.6.o"); } }, - DarwinPlatform.Kind.IPhoneOS => { - if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { + .IPhoneOS => { + if (ctx.comp.target.getArch() == .aarch64) { // iOS does not need any crt1 files for arm64 } else if (platform.versionLessThan(3, 1)) { - try ctx.args.append(c"-lcrt1.o"); + try ctx.args.append("-lcrt1.o"); } else if (platform.versionLessThan(6, 0)) { - try ctx.args.append(c"-lcrt1.3.1.o"); + try ctx.args.append("-lcrt1.3.1.o"); } }, - DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed + .IPhoneOSSimulator => {}, // no crt1.o needed } } - //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - // const char *lib_dir = g->lib_dirs.at(i); - // lj->args.append("-L"); - // lj->args.append(lib_dir); - //} - for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); - //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce - //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { - // Buf *compiler_rt_o_path = build_compiler_rt(g); - // lj->args.append(buf_ptr(compiler_rt_o_path)); - //} - if (ctx.comp.target == Target.Native) { for (ctx.comp.link_libs_list.toSliceConst()) |lib| { if (mem.eql(u8, lib.name, "c")) { @@ -589,36 +442,31 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { // to make syscalls because the syscall numbers are not documented // and change between versions. // so we always link against libSystem - try ctx.args.append(c"-lSystem"); + try ctx.args.append("-lSystem"); } else { if (mem.indexOfScalar(u8, lib.name, '/') == null) { - const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); - try ctx.args.append(arg.ptr); + const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); + try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } else { const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); - try ctx.args.append(arg.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } } } } else { - try ctx.args.append(c"-undefined"); - try ctx.args.append(c"dynamic_lookup"); + try ctx.args.append("-undefined"); + try ctx.args.append("dynamic_lookup"); } - if (platform.kind == DarwinPlatform.Kind.MacOS) { + if (platform.kind == .MacOS) { if (platform.versionLessThan(10, 5)) { - try ctx.args.append(c"-lgcc_s.10.4"); + try ctx.args.append("-lgcc_s.10.4"); } else if (platform.versionLessThan(10, 6)) { - try ctx.args.append(c"-lgcc_s.10.5"); + try ctx.args.append("-lgcc_s.10.5"); } } else { @panic("TODO"); } - - //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { - // lj->args.append("-framework"); - // lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); - //} } fn constructLinkerArgsWasm(ctx: *Context) void { @@ -626,20 +474,19 @@ fn constructLinkerArgsWasm(ctx: *Context) void { } fn addFnObjects(ctx: *Context) !void { - // at this point it's guaranteed nobody else has this lock, so we circumvent it - // and avoid having to be an async function - const fn_link_set = &ctx.comp.fn_link_set.private_data; + const held = ctx.comp.fn_link_set.acquire(); + defer held.release(); - var it = fn_link_set.first; + var it = held.value.first; while (it) |node| { const fn_val = node.data orelse { // handle the tombstone. See Value.Fn.destroy. it = node.next; - fn_link_set.remove(node); + held.value.remove(node); ctx.comp.gpa().destroy(node); continue; }; - try ctx.args.append(fn_val.containing_object.ptr()); + try ctx.args.append(fn_val.containing_object.toSliceConst()); it = node.next; } } @@ -659,17 +506,17 @@ const DarwinPlatform = struct { fn get(comp: *Compilation) !DarwinPlatform { var result: DarwinPlatform = undefined; const ver_str = switch (comp.darwin_version_min) { - Compilation.DarwinVersionMin.MacOS => |ver| blk: { - result.kind = Kind.MacOS; + .MacOS => |ver| blk: { + result.kind = .MacOS; break :blk ver; }, - Compilation.DarwinVersionMin.Ios => |ver| blk: { - result.kind = Kind.IPhoneOS; + .Ios => |ver| blk: { + result.kind = .IPhoneOS; break :blk ver; }, - Compilation.DarwinVersionMin.None => blk: { + .None => blk: { assert(comp.target.getOs() == .macosx); - result.kind = Kind.MacOS; + result.kind = .MacOS; break :blk "10.14"; }, }; @@ -686,11 +533,11 @@ const DarwinPlatform = struct { return error.InvalidDarwinVersionString; } - if (result.kind == Kind.IPhoneOS) { + if (result.kind == .IPhoneOS) { switch (comp.target.getArch()) { - builtin.Arch.i386, - builtin.Arch.x86_64, - => result.kind = Kind.IPhoneOSSimulator, + .i386, + .x86_64, + => result.kind = .IPhoneOSSimulator, else => {}, } } diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 40d414e5f..415996825 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -1,4 +1,3 @@ -const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; @@ -84,7 +83,7 @@ pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; pub const AddGlobal = LLVMAddGlobal; -extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*]const u8) ?*Value; +extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value; pub const ConstStringInContext = LLVMConstStringInContext; extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value; @@ -93,7 +92,7 @@ pub const ConstInt = LLVMConstInt; extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value; pub const BuildLoad = LLVMBuildLoad; -extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*]const u8) ?*Value; +extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*:0]const u8) ?*Value; pub const ConstNull = LLVMConstNull; extern fn LLVMConstNull(Ty: *Type) ?*Value; @@ -111,24 +110,24 @@ pub const CreateEnumAttribute = LLVMCreateEnumAttribute; extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) ?*Attribute; pub const AddFunction = LLVMAddFunction; -extern fn LLVMAddFunction(M: *Module, Name: [*]const u8, FunctionTy: *Type) ?*Value; +extern fn LLVMAddFunction(M: *Module, Name: [*:0]const u8, FunctionTy: *Type) ?*Value; pub const CreateCompileUnit = ZigLLVMCreateCompileUnit; extern fn ZigLLVMCreateCompileUnit( dibuilder: *DIBuilder, lang: c_uint, difile: *DIFile, - producer: [*]const u8, + producer: [*:0]const u8, is_optimized: bool, - flags: [*]const u8, + flags: [*:0]const u8, runtime_version: c_uint, - split_name: [*]const u8, + split_name: [*:0]const u8, dwo_id: u64, emit_debug_info: bool, ) ?*DICompileUnit; pub const CreateFile = ZigLLVMCreateFile; -extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*]const u8, directory: [*]const u8) ?*DIFile; +extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*:0]const u8, directory: [*:0]const u8) ?*DIFile; pub const ArrayType = LLVMArrayType; extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type; @@ -146,7 +145,7 @@ pub const IntTypeInContext = LLVMIntTypeInContext; extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) ?*Type; pub const ModuleCreateWithNameInContext = LLVMModuleCreateWithNameInContext; -extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*]const u8, C: *Context) ?*Module; +extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *Context) ?*Module; pub const VoidTypeInContext = LLVMVoidTypeInContext; extern fn LLVMVoidTypeInContext(C: *Context) ?*Type; @@ -158,7 +157,7 @@ pub const ContextDispose = LLVMContextDispose; extern fn LLVMContextDispose(C: *Context) void; pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData; -extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*]u8; +extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*:0]u8; pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout; extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData; @@ -166,9 +165,9 @@ extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData; pub const CreateTargetMachine = ZigLLVMCreateTargetMachine; extern fn ZigLLVMCreateTargetMachine( T: *Target, - Triple: [*]const u8, - CPU: [*]const u8, - Features: [*]const u8, + Triple: [*:0]const u8, + CPU: [*:0]const u8, + Features: [*:0]const u8, Level: CodeGenOptLevel, Reloc: RelocMode, CodeModel: CodeModel, @@ -176,10 +175,10 @@ extern fn ZigLLVMCreateTargetMachine( ) ?*TargetMachine; pub const GetHostCPUName = LLVMGetHostCPUName; -extern fn LLVMGetHostCPUName() ?[*]u8; +extern fn LLVMGetHostCPUName() ?[*:0]u8; pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; -extern fn ZigLLVMGetNativeFeatures() ?[*]u8; +extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; pub const GetElementType = LLVMGetElementType; extern fn LLVMGetElementType(Ty: *Type) *Type; @@ -191,16 +190,16 @@ pub const BuildStore = LLVMBuildStore; extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value; pub const BuildAlloca = LLVMBuildAlloca; -extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*]const u8) ?*Value; +extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*:0]const u8) ?*Value; pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value; pub const GetTargetFromTriple = LLVMGetTargetFromTriple; -extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: **Target, ErrorMessage: ?*[*]u8) Bool; +extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **Target, ErrorMessage: ?*[*:0]u8) Bool; pub const VerifyModule = LLVMVerifyModule; -extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; +extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*:0]u8) Bool; pub const GetInsertBlock = LLVMGetInsertBlock; extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock; @@ -217,7 +216,7 @@ pub const GetParam = LLVMGetParam; extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value; pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; -extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*]const u8) ?*BasicBlock; +extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*:0]const u8) ?*BasicBlock; pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void; @@ -227,24 +226,24 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; pub const VerifierFailureAction = c.LLVMVerifierFailureAction; -pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone; -pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess; -pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault; -pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive; +pub const CodeGenLevelNone = CodeGenOptLevel.LLVMCodeGenLevelNone; +pub const CodeGenLevelLess = CodeGenOptLevel.LLVMCodeGenLevelLess; +pub const CodeGenLevelDefault = CodeGenOptLevel.LLVMCodeGenLevelDefault; +pub const CodeGenLevelAggressive = CodeGenOptLevel.LLVMCodeGenLevelAggressive; pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; -pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault; -pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic; -pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC; -pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic; +pub const RelocDefault = RelocMode.LLVMRelocDefault; +pub const RelocStatic = RelocMode.LLVMRelocStatic; +pub const RelocPIC = RelocMode.LLVMRelocPIC; +pub const RelocDynamicNoPic = RelocMode.LLVMRelocDynamicNoPic; pub const RelocMode = c.LLVMRelocMode; -pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault; -pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault; -pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall; -pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel; -pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium; -pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge; +pub const CodeModelDefault = CodeModel.LLVMCodeModelDefault; +pub const CodeModelJITDefault = CodeModel.LLVMCodeModelJITDefault; +pub const CodeModelSmall = CodeModel.LLVMCodeModelSmall; +pub const CodeModelKernel = CodeModel.LLVMCodeModelKernel; +pub const CodeModelMedium = CodeModel.LLVMCodeModelMedium; +pub const CodeModelLarge = CodeModel.LLVMCodeModelLarge; pub const CodeModel = c.LLVMCodeModel; pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; @@ -252,23 +251,25 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; pub const EmitOutputType = c.ZigLLVM_EmitOutputType; -pub const CCallConv = c.LLVMCCallConv; -pub const FastCallConv = c.LLVMFastCallConv; -pub const ColdCallConv = c.LLVMColdCallConv; -pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv; -pub const AnyRegCallConv = c.LLVMAnyRegCallConv; -pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv; -pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv; +pub const CCallConv = CallConv.LLVMCCallConv; +pub const FastCallConv = CallConv.LLVMFastCallConv; +pub const ColdCallConv = CallConv.LLVMColdCallConv; +pub const WebKitJSCallConv = CallConv.LLVMWebKitJSCallConv; +pub const AnyRegCallConv = CallConv.LLVMAnyRegCallConv; +pub const X86StdcallCallConv = CallConv.LLVMX86StdcallCallConv; +pub const X86FastcallCallConv = CallConv.LLVMX86FastcallCallConv; pub const CallConv = c.LLVMCallConv; -pub const FnInline = extern enum { +pub const CallAttr = extern enum { Auto, - Always, - Never, + NeverTail, + NeverInline, + AlwaysTail, + AlwaysInline, }; fn removeNullability(comptime T: type) type { - comptime assert(@typeInfo(T).Pointer.size == @import("builtin").TypeInfo.Pointer.Size.C); + comptime assert(@typeInfo(T).Pointer.size == .C); return *T.Child; } @@ -279,14 +280,14 @@ pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; extern fn ZigLLVMTargetMachineEmitToFile( targ_machine_ref: *TargetMachine, module_ref: *Module, - filename: [*]const u8, + filename: [*:0]const u8, output_type: EmitOutputType, - error_message: *[*]u8, + error_message: *[*:0]u8, is_debug: bool, is_small: bool, ) bool; pub const BuildCall = ZigLLVMBuildCall; -extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?*Value; +extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: CallConv, fn_inline: CallAttr, Name: [*:0]const u8) ?*Value; pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 52eda5824..af3fd3a01 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -11,14 +11,11 @@ const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const Buffer = std.Buffer; -const arg = @import("arg.zig"); const c = @import("c.zig"); const introspect = @import("introspect.zig"); -const Args = arg.Args; -const Flag = arg.Flag; const ZigCompiler = @import("compilation.zig").ZigCompiler; const Compilation = @import("compilation.zig").Compilation; -const Target = @import("target.zig").Target; +const Target = std.Target; const errmsg = @import("errmsg.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; @@ -26,6 +23,8 @@ var stderr_file: fs.File = undefined; var stderr: *io.OutStream(fs.File.WriteError) = undefined; var stdout: *io.OutStream(fs.File.WriteError) = undefined; +pub const io_mode = .evented; + pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB const usage = @@ -47,26 +46,19 @@ const usage = const Command = struct { name: []const u8, - exec: fn (*Allocator, []const []const u8) anyerror!void, + exec: async fn (*Allocator, []const []const u8) anyerror!void, }; pub fn main() !void { - // This allocator needs to be thread-safe because we use it for the event.Loop - // which multiplexes async functions onto kernel threads. - // libc allocator is guaranteed to have this property. const allocator = std.heap.c_allocator; - var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = stdout_file.outStream(); - stdout = &stdout_out_stream.stream; + stdout = &std.io.getStdOut().outStream().stream; - stderr_file = try std.io.getStdErr(); - var stderr_out_stream = stderr_file.outStream(); - stderr = &stderr_out_stream.stream; + stderr_file = std.io.getStdErr(); + stderr = &stderr_file.outStream().stream; const args = try process.argsAlloc(allocator); - // TODO I'm getting unreachable code here, which shouldn't happen - //defer process.argsFree(allocator, args); + defer process.argsFree(allocator, args); if (args.len <= 1) { try stderr.write("expected command argument\n\n"); @@ -74,60 +66,33 @@ pub fn main() !void { process.exit(1); } - const commands = [_]Command{ - Command{ - .name = "build-exe", - .exec = cmdBuildExe, - }, - Command{ - .name = "build-lib", - .exec = cmdBuildLib, - }, - Command{ - .name = "build-obj", - .exec = cmdBuildObj, - }, - Command{ - .name = "fmt", - .exec = cmdFmt, - }, - Command{ - .name = "libc", - .exec = cmdLibC, - }, - Command{ - .name = "targets", - .exec = cmdTargets, - }, - Command{ - .name = "version", - .exec = cmdVersion, - }, - Command{ - .name = "zen", - .exec = cmdZen, - }, - - // undocumented commands - Command{ - .name = "help", - .exec = cmdHelp, - }, - Command{ - .name = "internal", - .exec = cmdInternal, - }, - }; - - for (commands) |command| { - if (mem.eql(u8, command.name, args[1])) { - return command.exec(allocator, args[2..]); - } + const cmd = args[1]; + const cmd_args = args[2..]; + if (mem.eql(u8, cmd, "build-exe")) { + return buildOutputType(allocator, cmd_args, .Exe); + } else if (mem.eql(u8, cmd, "build-lib")) { + return buildOutputType(allocator, cmd_args, .Lib); + } else if (mem.eql(u8, cmd, "build-obj")) { + return buildOutputType(allocator, cmd_args, .Obj); + } else if (mem.eql(u8, cmd, "fmt")) { + return cmdFmt(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "libc")) { + return cmdLibC(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "targets")) { + return cmdTargets(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "version")) { + return cmdVersion(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "zen")) { + return cmdZen(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "help")) { + return cmdHelp(allocator, cmd_args); + } else if (mem.eql(u8, cmd, "internal")) { + return cmdInternal(allocator, cmd_args); + } else { + try stderr.print("unknown command: {}\n\n", .{args[1]}); + try stderr.write(usage); + process.exit(1); } - - try stderr.print("unknown command: {}\n\n", args[1]); - try stderr.write(usage); - process.exit(1); } const usage_build_generic = @@ -157,6 +122,7 @@ const usage_build_generic = \\ --static Output will be statically linked \\ --strip Exclude debug symbols \\ -target [name] -- see the targets command + \\ --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker \\ --verbose-tokenize Turn on compiler debug output for tokenization \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view) \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source) @@ -178,8 +144,6 @@ const usage_build_generic = \\ --object [obj] Add object file to build \\ -rdynamic Add all symbols to the dynamic symbol table \\ -rpath [path] Add directory to the runtime library search path - \\ -mconsole (windows) --subsystem console to the linker - \\ -mwindows (windows) --subsystem windows to the linker \\ -framework [name] (darwin) link against framework \\ -mios-version-min [ver] (darwin) set iOS deployment target \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target @@ -190,164 +154,258 @@ const usage_build_generic = \\ ; -const args_build_generic = [_]Flag{ - Flag.Bool("--help"), - Flag.Option("--color", [_][]const u8{ - "auto", - "off", - "on", - }), - Flag.Option("--mode", [_][]const u8{ - "debug", - "release-fast", - "release-safe", - "release-small", - }), - - Flag.ArgMergeN("--assembly", 1), - Flag.Option("--emit", [_][]const u8{ - "asm", - "bin", - "llvm-ir", - }), - Flag.Bool("--enable-timing-info"), - Flag.Arg1("--libc"), - Flag.Arg1("--name"), - Flag.Arg1("--output"), - Flag.Arg1("--output-h"), - // NOTE: Parsed manually after initial check - Flag.ArgN("--pkg-begin", 2), - Flag.Bool("--pkg-end"), - Flag.Bool("--static"), - Flag.Bool("--strip"), - Flag.Arg1("-target"), - Flag.Bool("--verbose-tokenize"), - Flag.Bool("--verbose-ast-tree"), - Flag.Bool("--verbose-ast-fmt"), - Flag.Bool("--verbose-link"), - Flag.Bool("--verbose-ir"), - Flag.Bool("--verbose-llvm-ir"), - Flag.Bool("--verbose-cimport"), - Flag.Arg1("-dirafter"), - Flag.ArgMergeN("-isystem", 1), - Flag.Arg1("-mllvm"), - - Flag.Arg1("--ar-path"), - Flag.Bool("--each-lib-rpath"), - Flag.ArgMergeN("--library", 1), - Flag.ArgMergeN("--forbid-library", 1), - Flag.ArgMergeN("--library-path", 1), - Flag.Arg1("--linker-script"), - Flag.ArgMergeN("--object", 1), - // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path - Flag.Bool("-rdynamic"), - Flag.Arg1("-rpath"), - Flag.Bool("-mconsole"), - Flag.Bool("-mwindows"), - Flag.ArgMergeN("-framework", 1), - Flag.Arg1("-mios-version-min"), - Flag.Arg1("-mmacosx-version-min"), - Flag.Arg1("--ver-major"), - Flag.Arg1("--ver-minor"), - Flag.Arg1("--ver-patch"), -}; - fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void { - var flags = try Args.parse(allocator, args_build_generic, args); - defer flags.deinit(); + var color: errmsg.Color = .Auto; + var build_mode: std.builtin.Mode = .Debug; + var emit_bin = true; + var emit_asm = false; + var emit_llvm_ir = false; + var emit_h = false; + var provided_name: ?[]const u8 = null; + var is_dynamic = false; + var root_src_file: ?[]const u8 = null; + var libc_arg: ?[]const u8 = null; + var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; + var linker_script: ?[]const u8 = null; + var strip = false; + var verbose_tokenize = false; + var verbose_ast_tree = false; + var verbose_ast_fmt = false; + var verbose_link = false; + var verbose_ir = false; + var verbose_llvm_ir = false; + var verbose_cimport = false; + var linker_rdynamic = false; + var link_eh_frame_hdr = false; + var macosx_version_min: ?[]const u8 = null; + var ios_version_min: ?[]const u8 = null; - if (flags.present("help")) { - try stdout.write(usage_build_generic); - process.exit(0); - } + var assembly_files = ArrayList([]const u8).init(allocator); + defer assembly_files.deinit(); - const build_mode = blk: { - if (flags.single("mode")) |mode_flag| { - if (mem.eql(u8, mode_flag, "debug")) { - break :blk builtin.Mode.Debug; - } else if (mem.eql(u8, mode_flag, "release-fast")) { - break :blk builtin.Mode.ReleaseFast; - } else if (mem.eql(u8, mode_flag, "release-safe")) { - break :blk builtin.Mode.ReleaseSafe; - } else if (mem.eql(u8, mode_flag, "release-small")) { - break :blk builtin.Mode.ReleaseSmall; - } else unreachable; - } else { - break :blk builtin.Mode.Debug; - } - }; + var link_objects = ArrayList([]const u8).init(allocator); + defer link_objects.deinit(); - const color = blk: { - if (flags.single("color")) |color_flag| { - if (mem.eql(u8, color_flag, "auto")) { - break :blk errmsg.Color.Auto; - } else if (mem.eql(u8, color_flag, "on")) { - break :blk errmsg.Color.On; - } else if (mem.eql(u8, color_flag, "off")) { - break :blk errmsg.Color.Off; - } else unreachable; - } else { - break :blk errmsg.Color.Auto; - } - }; + var clang_argv_buf = ArrayList([]const u8).init(allocator); + defer clang_argv_buf.deinit(); - const emit_type = blk: { - if (flags.single("emit")) |emit_flag| { - if (mem.eql(u8, emit_flag, "asm")) { - break :blk Compilation.Emit.Assembly; - } else if (mem.eql(u8, emit_flag, "bin")) { - break :blk Compilation.Emit.Binary; - } else if (mem.eql(u8, emit_flag, "llvm-ir")) { - break :blk Compilation.Emit.LlvmIr; - } else unreachable; - } else { - break :blk Compilation.Emit.Binary; - } - }; + var mllvm_flags = ArrayList([]const u8).init(allocator); + defer mllvm_flags.deinit(); var cur_pkg = try CliPkg.init(allocator, "", "", null); defer cur_pkg.deinit(); - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg_name = args[i]; - if (mem.eql(u8, "--pkg-begin", arg_name)) { - // following two arguments guaranteed to exist due to arg parsing - i += 1; - const new_pkg_name = args[i]; - i += 1; - const new_pkg_path = args[i]; + var system_libs = ArrayList([]const u8).init(allocator); + defer system_libs.deinit(); - var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg); - try cur_pkg.children.append(new_cur_pkg); - cur_pkg = new_cur_pkg; - } else if (mem.eql(u8, "--pkg-end", arg_name)) { - if (cur_pkg.parent) |parent| { - cur_pkg = parent; + var c_src_files = ArrayList([]const u8).init(allocator); + defer c_src_files.deinit(); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try stdout.write(usage_build_generic); + process.exit(0); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + try stderr.write("expected [auto|on|off] after --color\n"); + process.exit(1); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--mode")) { + if (i + 1 >= args.len) { + try stderr.write("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n"); + process.exit(1); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "Debug")) { + build_mode = .Debug; + } else if (mem.eql(u8, next_arg, "ReleaseSafe")) { + build_mode = .ReleaseSafe; + } else if (mem.eql(u8, next_arg, "ReleaseFast")) { + build_mode = .ReleaseFast; + } else if (mem.eql(u8, next_arg, "ReleaseSmall")) { + build_mode = .ReleaseSmall; + } else { + try stderr.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--name")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --name\n"); + process.exit(1); + } + i += 1; + provided_name = args[i]; + } else if (mem.eql(u8, arg, "--ver-major")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --ver-major\n"); + process.exit(1); + } + i += 1; + version.major = try std.fmt.parseInt(u32, args[i], 10); + } else if (mem.eql(u8, arg, "--ver-minor")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --ver-minor\n"); + process.exit(1); + } + i += 1; + version.minor = try std.fmt.parseInt(u32, args[i], 10); + } else if (mem.eql(u8, arg, "--ver-patch")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --ver-patch\n"); + process.exit(1); + } + i += 1; + version.patch = try std.fmt.parseInt(u32, args[i], 10); + } else if (mem.eql(u8, arg, "--linker-script")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --linker-script\n"); + process.exit(1); + } + i += 1; + linker_script = args[i]; + } else if (mem.eql(u8, arg, "--libc")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after --libc\n"); + process.exit(1); + } + i += 1; + libc_arg = args[i]; + } else if (mem.eql(u8, arg, "-mllvm")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after -mllvm\n"); + process.exit(1); + } + i += 1; + try clang_argv_buf.append("-mllvm"); + try clang_argv_buf.append(args[i]); + + try mllvm_flags.append(args[i]); + } else if (mem.eql(u8, arg, "-mmacosx-version-min")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after -mmacosx-version-min\n"); + process.exit(1); + } + i += 1; + macosx_version_min = args[i]; + } else if (mem.eql(u8, arg, "-mios-version-min")) { + if (i + 1 >= args.len) { + try stderr.write("expected parameter after -mios-version-min\n"); + process.exit(1); + } + i += 1; + ios_version_min = args[i]; + } else if (mem.eql(u8, arg, "-femit-bin")) { + emit_bin = true; + } else if (mem.eql(u8, arg, "-fno-emit-bin")) { + emit_bin = false; + } else if (mem.eql(u8, arg, "-femit-asm")) { + emit_asm = true; + } else if (mem.eql(u8, arg, "-fno-emit-asm")) { + emit_asm = false; + } else if (mem.eql(u8, arg, "-femit-llvm-ir")) { + emit_llvm_ir = true; + } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { + emit_llvm_ir = false; + } else if (mem.eql(u8, arg, "-dynamic")) { + is_dynamic = true; + } else if (mem.eql(u8, arg, "--strip")) { + strip = true; + } else if (mem.eql(u8, arg, "--verbose-tokenize")) { + verbose_tokenize = true; + } else if (mem.eql(u8, arg, "--verbose-ast-tree")) { + verbose_ast_tree = true; + } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) { + verbose_ast_fmt = true; + } else if (mem.eql(u8, arg, "--verbose-link")) { + verbose_link = true; + } else if (mem.eql(u8, arg, "--verbose-ir")) { + verbose_ir = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { + verbose_llvm_ir = true; + } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { + link_eh_frame_hdr = true; + } else if (mem.eql(u8, arg, "--verbose-cimport")) { + verbose_cimport = true; + } else if (mem.eql(u8, arg, "-rdynamic")) { + linker_rdynamic = true; + } else if (mem.eql(u8, arg, "--pkg-begin")) { + if (i + 2 >= args.len) { + try stderr.write("expected [name] [path] after --pkg-begin\n"); + process.exit(1); + } + i += 1; + const new_pkg_name = args[i]; + i += 1; + const new_pkg_path = args[i]; + + var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg); + try cur_pkg.children.append(new_cur_pkg); + cur_pkg = new_cur_pkg; + } else if (mem.eql(u8, arg, "--pkg-end")) { + if (cur_pkg.parent) |parent| { + cur_pkg = parent; + } else { + try stderr.write("encountered --pkg-end with no matching --pkg-begin\n"); + process.exit(1); + } + } else if (mem.startsWith(u8, arg, "-l")) { + try system_libs.append(arg[2..]); + } else { + try stderr.print("unrecognized parameter: '{}'", .{arg}); + process.exit(1); + } + } else if (mem.endsWith(u8, arg, ".s")) { + try assembly_files.append(arg); + } else if (mem.endsWith(u8, arg, ".o") or + mem.endsWith(u8, arg, ".obj") or + mem.endsWith(u8, arg, ".a") or + mem.endsWith(u8, arg, ".lib")) + { + try link_objects.append(arg); + } else if (mem.endsWith(u8, arg, ".c") or + mem.endsWith(u8, arg, ".cpp")) + { + try c_src_files.append(arg); + } else if (mem.endsWith(u8, arg, ".zig")) { + if (root_src_file) |other| { + try stderr.print("found another zig file '{}' after root source file '{}'", .{ + arg, + other, + }); + process.exit(1); + } else { + root_src_file = arg; + } } else { - try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); - process.exit(1); + try stderr.print("unrecognized file extension of parameter '{}'", .{arg}); } } } if (cur_pkg.parent != null) { - try stderr.print("unmatched --pkg-begin\n"); + try stderr.print("unmatched --pkg-begin\n", .{}); process.exit(1); } - const provided_name = flags.single("name"); - const root_source_file = switch (flags.positionals.len) { - 0 => null, - 1 => flags.positionals.at(0), - else => { - try stderr.print("unexpected extra parameter: {}\n", flags.positionals.at(1)); - process.exit(1); - }, - }; - const root_name = if (provided_name) |n| n else blk: { - if (root_source_file) |file| { + if (root_src_file) |file| { const basename = fs.path.basename(file); var it = mem.separate(basename, "."); break :blk it.next() orelse basename; @@ -357,11 +415,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } }; - const is_static = flags.present("static"); - - const assembly_files = flags.many("assembly"); - const link_objects = flags.many("object"); - if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { + if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) { try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); process.exit(1); } @@ -371,121 +425,95 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co process.exit(1); } - var clang_argv_buf = ArrayList([]const u8).init(allocator); - defer clang_argv_buf.deinit(); - - const mllvm_flags = flags.many("mllvm"); - for (mllvm_flags) |mllvm| { - try clang_argv_buf.append("-mllvm"); - try clang_argv_buf.append(mllvm); - } - try ZigCompiler.setLlvmArgv(allocator, mllvm_flags); + try ZigCompiler.setLlvmArgv(allocator, mllvm_flags.toSliceConst()); const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch process.exit(1); defer allocator.free(zig_lib_dir); var override_libc: LibCInstallation = undefined; - var loop: event.Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var zig_compiler = try ZigCompiler.init(&loop); + var zig_compiler = try ZigCompiler.init(allocator); defer zig_compiler.deinit(); var comp = try Compilation.create( &zig_compiler, root_name, - root_source_file, + root_src_file, Target.Native, out_type, build_mode, - is_static, + !is_dynamic, zig_lib_dir, ); defer comp.destroy(); - if (flags.single("libc")) |libc_path| { - parseLibcPaths(loop.allocator, &override_libc, libc_path); + if (libc_arg) |libc_path| { + parseLibcPaths(allocator, &override_libc, libc_path); comp.override_libc = &override_libc; } - for (flags.many("library")) |lib| { + for (system_libs.toSliceConst()) |lib| { _ = try comp.addLinkLib(lib, true); } - comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); - comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); - comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); - + comp.version = version; comp.is_test = false; - - comp.linker_script = flags.single("linker-script"); - comp.each_lib_rpath = flags.present("each-lib-rpath"); - + comp.linker_script = linker_script; comp.clang_argv = clang_argv_buf.toSliceConst(); + comp.strip = strip; - comp.strip = flags.present("strip"); + comp.verbose_tokenize = verbose_tokenize; + comp.verbose_ast_tree = verbose_ast_tree; + comp.verbose_ast_fmt = verbose_ast_fmt; + comp.verbose_link = verbose_link; + comp.verbose_ir = verbose_ir; + comp.verbose_llvm_ir = verbose_llvm_ir; + comp.verbose_cimport = verbose_cimport; - comp.verbose_tokenize = flags.present("verbose-tokenize"); - comp.verbose_ast_tree = flags.present("verbose-ast-tree"); - comp.verbose_ast_fmt = flags.present("verbose-ast-fmt"); - comp.verbose_link = flags.present("verbose-link"); - comp.verbose_ir = flags.present("verbose-ir"); - comp.verbose_llvm_ir = flags.present("verbose-llvm-ir"); - comp.verbose_cimport = flags.present("verbose-cimport"); + comp.link_eh_frame_hdr = link_eh_frame_hdr; comp.err_color = color; - comp.lib_dirs = flags.many("library-path"); - comp.darwin_frameworks = flags.many("framework"); - comp.rpath_list = flags.many("rpath"); - if (flags.single("output-h")) |output_h| { - comp.out_h_path = output_h; - } + comp.linker_rdynamic = linker_rdynamic; - comp.windows_subsystem_windows = flags.present("mwindows"); - comp.windows_subsystem_console = flags.present("mconsole"); - comp.linker_rdynamic = flags.present("rdynamic"); - - if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) { + if (macosx_version_min != null and ios_version_min != null) { try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n"); process.exit(1); } - if (flags.single("mmacosx-version-min")) |ver| { + if (macosx_version_min) |ver| { comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver }; } - if (flags.single("mios-version-min")) |ver| { + if (ios_version_min) |ver| { comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver }; } - comp.emit_file_type = emit_type; - comp.assembly_files = assembly_files; - comp.link_out_file = flags.single("output"); - comp.link_objects = link_objects; + comp.emit_bin = emit_bin; + comp.emit_asm = emit_asm; + comp.emit_llvm_ir = emit_llvm_ir; + comp.emit_h = emit_h; + comp.assembly_files = assembly_files.toSliceConst(); + comp.link_objects = link_objects.toSliceConst(); comp.start(); - // TODO const process_build_events_handle = try async processBuildEvents(comp, color); - loop.run(); + processBuildEvents(comp, color); } -async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { +fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { var count: usize = 0; - while (true) { - // TODO directly awaiting async should guarantee memory allocation elision - const build_event = await (async comp.events.get() catch unreachable); + while (!comp.cancelled) { + const build_event = comp.events.get(); count += 1; switch (build_event) { - Compilation.Event.Ok => { - stderr.print("Build {} succeeded\n", count) catch process.exit(1); + .Ok => { + stderr.print("Build {} succeeded\n", .{count}) catch process.exit(1); }, - Compilation.Event.Error => |err| { - stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch process.exit(1); + .Error => |err| { + stderr.print("Build {} failed: {}\n", .{ count, @errorName(err) }) catch process.exit(1); }, - Compilation.Event.Fail => |msgs| { - stderr.print("Build {} compile errors:\n", count) catch process.exit(1); + .Fail => |msgs| { + stderr.print("Build {} compile errors:\n", .{count}) catch process.exit(1); for (msgs) |msg| { defer msg.destroy(); msg.printToFile(stderr_file, color) catch process.exit(1); @@ -495,18 +523,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { } } -fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Compilation.Kind.Exe); -} - -fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Compilation.Kind.Lib); -} - -fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Compilation.Kind.Obj); -} - pub const usage_fmt = \\usage: zig fmt [file]... \\ @@ -524,34 +540,22 @@ pub const usage_fmt = \\ ; -pub const args_fmt_spec = [_]Flag{ - Flag.Bool("--help"), - Flag.Bool("--check"), - Flag.Option("--color", [_][]const u8{ - "auto", - "off", - "on", - }), - Flag.Bool("--stdin"), -}; - const Fmt = struct { seen: event.Locked(SeenMap), any_error: bool, color: errmsg.Color, - loop: *event.Loop, + allocator: *Allocator, const SeenMap = std.StringHashMap(void); }; fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { libc.parse(allocator, libc_paths_file, stderr) catch |err| { - stderr.print( - "Unable to parse libc path file '{}': {}.\n" ++ - "Try running `zig libc` to see an example for the native target.\n", + stderr.print("Unable to parse libc path file '{}': {}.\n" ++ + "Try running `zig libc` to see an example for the native target.\n", .{ libc_paths_file, @errorName(err), - ) catch process.exit(1); + }) catch {}; process.exit(1); }; } @@ -565,68 +569,80 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { return; }, else => { - try stderr.print("unexpected extra parameter: {}\n", args[1]); + try stderr.print("unexpected extra parameter: {}\n", .{args[1]}); process.exit(1); }, } - var loop: event.Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var zig_compiler = try ZigCompiler.init(&loop); + var zig_compiler = try ZigCompiler.init(allocator); defer zig_compiler.deinit(); - // TODO const handle = try async findLibCAsync(&zig_compiler); - - loop.run(); -} - -async fn findLibCAsync(zig_compiler: *ZigCompiler) void { - const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| { - stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1); + const libc = zig_compiler.getNativeLibC() catch |err| { + stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {}; process.exit(1); }; libc.render(stdout) catch process.exit(1); } fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { - var flags = try Args.parse(allocator, args_fmt_spec, args); - defer flags.deinit(); + var color: errmsg.Color = .Auto; + var stdin_flag: bool = false; + var check_flag: bool = false; + var input_files = ArrayList([]const u8).init(allocator); - if (flags.present("help")) { - try stdout.write(usage_fmt); - process.exit(0); + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try stdout.write(usage_fmt); + process.exit(0); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + try stderr.write("expected [auto|on|off] after --color\n"); + process.exit(1); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--stdin")) { + stdin_flag = true; + } else if (mem.eql(u8, arg, "--check")) { + check_flag = true; + } else { + try stderr.print("unrecognized parameter: '{}'", .{arg}); + process.exit(1); + } + } else { + try input_files.append(arg); + } + } } - const color = blk: { - if (flags.single("color")) |color_flag| { - if (mem.eql(u8, color_flag, "auto")) { - break :blk errmsg.Color.Auto; - } else if (mem.eql(u8, color_flag, "on")) { - break :blk errmsg.Color.On; - } else if (mem.eql(u8, color_flag, "off")) { - break :blk errmsg.Color.Off; - } else unreachable; - } else { - break :blk errmsg.Color.Auto; - } - }; - - if (flags.present("stdin")) { - if (flags.positionals.len != 0) { + if (stdin_flag) { + if (input_files.len != 0) { try stderr.write("cannot use --stdin with positional arguments\n"); process.exit(1); } - var stdin_file = try io.getStdIn(); + var stdin_file = io.getStdIn(); var stdin = stdin_file.inStream(); const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size); defer allocator.free(source_code); const tree = std.zig.parse(allocator, source_code) catch |err| { - try stderr.print("error parsing stdin: {}\n", err); + try stderr.print("error parsing stdin: {}\n", .{err}); process.exit(1); }; defer tree.deinit(); @@ -641,9 +657,9 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { if (tree.errors.len != 0) { process.exit(1); } - if (flags.present("check")) { + if (check_flag) { const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree); - const code = if (anything_changed) u8(1) else u8(0); + const code: u8 = if (anything_changed) 1 else 0; process.exit(code); } @@ -651,33 +667,26 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { return; } - if (flags.positionals.len == 0) { + if (input_files.len == 0) { try stderr.write("expected at least one source file argument\n"); process.exit(1); } - var loop: event.Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); + var fmt = Fmt{ + .allocator = allocator, + .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)), + .any_error = false, + .color = color, + }; - var result: FmtError!void = undefined; - // TODO const main_handle = try async asyncFmtMainChecked( - // TODO &result, - // TODO &loop, - // TODO &flags, - // TODO color, - // TODO ); - loop.run(); - return result; -} - -async fn asyncFmtMainChecked( - result: *(FmtError!void), - loop: *event.Loop, - flags: *const Args, - color: errmsg.Color, -) void { - result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable); + var group = event.Group(FmtError!void).init(allocator); + for (input_files.toSliceConst()) |file_path| { + try group.call(fmtPath, .{ &fmt, file_path, check_flag }); + } + try group.wait(); + if (fmt.any_error) { + process.exit(1); + } } const FmtError = error{ @@ -702,74 +711,48 @@ const FmtError = error{ CurrentWorkingDirectoryUnlinked, } || fs.File.OpenError; -async fn asyncFmtMain( - loop: *event.Loop, - flags: *const Args, - color: errmsg.Color, -) FmtError!void { - suspend { - resume @handle(); - } - var fmt = Fmt{ - .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)), - .any_error = false, - .color = color, - .loop = loop, - }; - - const check_mode = flags.present("check"); - - var group = event.Group(FmtError!void).init(loop); - for (flags.positionals.toSliceConst()) |file_path| { - try group.call(fmtPath, &fmt, file_path, check_mode); - } - try await (async group.wait() catch unreachable); - if (fmt.any_error) { - process.exit(1); - } -} - async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { - const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref); - defer fmt.loop.allocator.free(file_path); + const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); + defer fmt.allocator.free(file_path); { - const held = await (async fmt.seen.acquire() catch unreachable); + const held = fmt.seen.acquire(); defer held.release(); if (try held.value.put(file_path, {})) |_| return; } - const source_code = (await try async event.fs.readFile( - fmt.loop, + const source_code = event.fs.readFile( + fmt.allocator, file_path, max_src_size, - )) catch |err| switch (err) { + ) catch |err| switch (err) { error.IsDir, error.AccessDenied => { - // TODO make event based (and dir.next()) - var dir = try fs.Dir.open(fmt.loop.allocator, file_path); + var dir = try fs.cwd().openDirList(file_path); defer dir.close(); - var group = event.Group(FmtError!void).init(fmt.loop); - while (try dir.next()) |entry| { - if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.loop.allocator, [_][]const u8{ file_path, entry.name }); - try group.call(fmtPath, fmt, full_path, check_mode); + var group = event.Group(FmtError!void).init(fmt.allocator); + var it = dir.iterate(); + while (try it.next()) |entry| { + if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name }); + @panic("TODO https://github.com/ziglang/zig/issues/3777"); + // try group.call(fmtPath, .{fmt, full_path, check_mode}); } } - return await (async group.wait() catch unreachable); + return group.wait(); }, else => { // TODO lock stderr printing - try stderr.print("unable to open '{}': {}\n", file_path, err); + try stderr.print("unable to open '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }, }; - defer fmt.loop.allocator.free(source_code); + defer fmt.allocator.free(source_code); - const tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| { - try stderr.print("error parsing file '{}': {}\n", file_path, err); + const tree = std.zig.parse(fmt.allocator, source_code) catch |err| { + try stderr.print("error parsing file '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }; @@ -777,8 +760,8 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { - const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, tree, file_path); - defer fmt.loop.allocator.destroy(msg); + const msg = try errmsg.Msg.createFromParseError(fmt.allocator, parse_error, tree, file_path); + defer fmt.allocator.destroy(msg); try msg.printToFile(stderr_file, fmt.color); } @@ -788,19 +771,19 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro } if (check_mode) { - const anything_changed = try std.zig.render(fmt.loop.allocator, io.null_out_stream, tree); + const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree); if (anything_changed) { - try stderr.print("{}\n", file_path); + try stderr.print("{}\n", .{file_path}); fmt.any_error = true; } } else { // TODO make this evented - const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path); + const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); defer baf.destroy(); - const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), tree); + const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree); if (anything_changed) { - try stderr.print("{}\n", file_path); + try stderr.print("{}\n", .{file_path}); try baf.finish(); } } @@ -817,7 +800,7 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { // NOTE: Cannot use empty string, see #918. comptime const native_str = if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n"; - try stdout.print(" {}{}", arch_tag, native_str); + try stdout.print(" {}{}", .{ arch_tag, native_str }); } } try stdout.write("\n"); @@ -825,12 +808,12 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { try stdout.write("Operating Systems:\n"); { comptime var i: usize = 0; - inline while (i < @memberCount(builtin.Os)) : (i += 1) { - comptime const os_tag = @memberName(builtin.Os, i); + inline while (i < @memberCount(Target.Os)) : (i += 1) { + comptime const os_tag = @memberName(Target.Os, i); // NOTE: Cannot use empty string, see #918. comptime const native_str = if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n"; - try stdout.print(" {}{}", os_tag, native_str); + try stdout.print(" {}{}", .{ os_tag, native_str }); } } try stdout.write("\n"); @@ -838,22 +821,20 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { try stdout.write("C ABIs:\n"); { comptime var i: usize = 0; - inline while (i < @memberCount(builtin.Abi)) : (i += 1) { - comptime const abi_tag = @memberName(builtin.Abi, i); + inline while (i < @memberCount(Target.Abi)) : (i += 1) { + comptime const abi_tag = @memberName(Target.Abi, i); // NOTE: Cannot use empty string, see #918. comptime const native_str = if (comptime mem.eql(u8, abi_tag, @tagName(builtin.abi))) " (native)\n" else "\n"; - try stdout.print(" {}{}", abi_tag, native_str); + try stdout.print(" {}{}", .{ abi_tag, native_str }); } } } fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { - try stdout.print("{}\n", std.mem.toSliceConst(u8, c.ZIG_VERSION_STRING)); + try stdout.print("{}\n", .{std.mem.toSliceConst(u8, c.ZIG_VERSION_STRING)}); } -const args_test_spec = [_]Flag{Flag.Bool("--help")}; - fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { try stdout.write(usage); } @@ -899,14 +880,16 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { .exec = cmdInternalBuildInfo, }}; - for (sub_commands) |sub_command| { + inline for (sub_commands) |sub_command| { if (mem.eql(u8, sub_command.name, args[0])) { - try sub_command.exec(allocator, args[1..]); - return; + var frame = try allocator.create(@Frame(sub_command.exec)); + defer allocator.destroy(frame); + frame.* = async sub_command.exec(allocator, args[1..]); + return await frame; } } - try stderr.print("unknown sub command: {}\n\n", args[0]); + try stderr.print("unknown sub command: {}\n\n", .{args[0]}); try stderr.write(usage_internal); } @@ -914,23 +897,19 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { try stdout.print( \\ZIG_CMAKE_BINARY_DIR {} \\ZIG_CXX_COMPILER {} - \\ZIG_LLVM_CONFIG_EXE {} \\ZIG_LLD_INCLUDE_PATH {} \\ZIG_LLD_LIBRARIES {} - \\ZIG_STD_FILES {} - \\ZIG_C_HEADER_FILES {} + \\ZIG_LLVM_CONFIG_EXE {} \\ZIG_DIA_GUIDS_LIB {} \\ - , + , .{ std.mem.toSliceConst(u8, c.ZIG_CMAKE_BINARY_DIR), std.mem.toSliceConst(u8, c.ZIG_CXX_COMPILER), - std.mem.toSliceConst(u8, c.ZIG_LLVM_CONFIG_EXE), std.mem.toSliceConst(u8, c.ZIG_LLD_INCLUDE_PATH), std.mem.toSliceConst(u8, c.ZIG_LLD_LIBRARIES), - std.mem.toSliceConst(u8, c.ZIG_STD_FILES), - std.mem.toSliceConst(u8, c.ZIG_C_HEADER_FILES), + std.mem.toSliceConst(u8, c.ZIG_LLVM_CONFIG_EXE), std.mem.toSliceConst(u8, c.ZIG_DIA_GUIDS_LIB), - ); + }); } const CliPkg = struct { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 4ff3e303a..c294bf8b7 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const Allocator = mem.Allocator; const Decl = @import("decl.zig").Decl; const Compilation = @import("compilation.zig").Compilation; @@ -28,15 +27,15 @@ pub const Scope = struct { if (base.ref_count.decr() == 1) { if (base.parent) |parent| parent.deref(comp); switch (base.id) { - Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), - Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), - Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), - Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), - Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), - Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), - Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), - Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp), - Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp), + .Root => @fieldParentPtr(Root, "base", base).destroy(comp), + .Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), + .Block => @fieldParentPtr(Block, "base", base).destroy(comp), + .FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), + .CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), + .Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), + .DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), + .Var => @fieldParentPtr(Var, "base", base).destroy(comp), + .AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp), } } } @@ -46,7 +45,7 @@ pub const Scope = struct { while (scope.parent) |parent| { scope = parent; } - assert(scope.id == Id.Root); + assert(scope.id == .Root); return @fieldParentPtr(Root, "base", scope); } @@ -54,17 +53,17 @@ pub const Scope = struct { var scope = base; while (true) { switch (scope.id) { - Id.FnDef => return @fieldParentPtr(FnDef, "base", scope), - Id.Root, Id.Decls => return null, + .FnDef => return @fieldParentPtr(FnDef, "base", scope), + .Root, .Decls => return null, - Id.Block, - Id.Defer, - Id.DeferExpr, - Id.CompTime, - Id.Var, + .Block, + .Defer, + .DeferExpr, + .CompTime, + .Var, => scope = scope.parent.?, - Id.AstTree => unreachable, + .AstTree => unreachable, } } } @@ -73,20 +72,20 @@ pub const Scope = struct { var scope = base; while (true) { switch (scope.id) { - Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), + .DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), - Id.FnDef, - Id.Decls, + .FnDef, + .Decls, => return null, - Id.Block, - Id.Defer, - Id.CompTime, - Id.Root, - Id.Var, + .Block, + .Defer, + .CompTime, + .Root, + .Var, => scope = scope.parent orelse return null, - Id.AstTree => unreachable, + .AstTree => unreachable, } } } @@ -123,7 +122,7 @@ pub const Scope = struct { const self = try comp.gpa().create(Root); self.* = Root{ .base = Scope{ - .id = Id.Root, + .id = .Root, .parent = null, .ref_count = std.atomic.Int(usize).init(1), }, @@ -155,7 +154,7 @@ pub const Scope = struct { .base = undefined, .tree = tree, }; - self.base.init(Id.AstTree, &root_scope.base); + self.base.init(.AstTree, &root_scope.base); return self; } @@ -184,9 +183,9 @@ pub const Scope = struct { const self = try comp.gpa().create(Decls); self.* = Decls{ .base = undefined, - .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), + .table = event.RwLocked(Decl.Table).init(Decl.Table.init(comp.gpa())), }; - self.base.init(Id.Decls, parent); + self.base.init(.Decls, parent); return self; } @@ -219,15 +218,15 @@ pub const Scope = struct { fn get(self: Safety, comp: *Compilation) bool { return switch (self) { - Safety.Auto => switch (comp.build_mode) { - builtin.Mode.Debug, - builtin.Mode.ReleaseSafe, + .Auto => switch (comp.build_mode) { + .Debug, + .ReleaseSafe, => true, - builtin.Mode.ReleaseFast, - builtin.Mode.ReleaseSmall, + .ReleaseFast, + .ReleaseSmall, => false, }, - @TagType(Safety).Manual => |man| man.enabled, + .Manual => |man| man.enabled, }; } }; @@ -243,7 +242,7 @@ pub const Scope = struct { .is_comptime = undefined, .safety = Safety.Auto, }; - self.base.init(Id.Block, parent); + self.base.init(.Block, parent); return self; } @@ -266,7 +265,7 @@ pub const Scope = struct { .base = undefined, .fn_val = null, }; - self.base.init(Id.FnDef, parent); + self.base.init(.FnDef, parent); return self; } @@ -282,7 +281,7 @@ pub const Scope = struct { pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { const self = try comp.gpa().create(CompTime); self.* = CompTime{ .base = undefined }; - self.base.init(Id.CompTime, parent); + self.base.init(.CompTime, parent); return self; } @@ -314,7 +313,7 @@ pub const Scope = struct { .defer_expr_scope = defer_expr_scope, .kind = kind, }; - self.base.init(Id.Defer, parent); + self.base.init(.Defer, parent); defer_expr_scope.base.ref(); return self; } @@ -338,7 +337,7 @@ pub const Scope = struct { .expr_node = expr_node, .reported_err = false, }; - self.base.init(Id.DeferExpr, parent); + self.base.init(.DeferExpr, parent); return self; } @@ -404,14 +403,14 @@ pub const Scope = struct { .src_node = src_node, .data = undefined, }; - self.base.init(Id.Var, parent); + self.base.init(.Var, parent); return self; } pub fn destroy(self: *Var, comp: *Compilation) void { switch (self.data) { - Data.Param => {}, - Data.Const => |value| value.deref(comp), + .Param => {}, + .Const => |value| value.deref(comp), } comp.gpa().destroy(self); } diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index 2f48f2f45..ec683e4ba 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -1,7 +1,6 @@ // This is Zig code that is used by both stage1 and stage2. // The prototypes in src/userland.h must match these definitions. -const builtin = @import("builtin"); const std = @import("std"); const io = std.io; const mem = std.mem; @@ -10,10 +9,7 @@ const process = std.process; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const Buffer = std.Buffer; -const arg = @import("arg.zig"); const self_hosted_main = @import("main.zig"); -const Args = arg.Args; -const Flag = arg.Flag; const errmsg = @import("errmsg.zig"); const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; @@ -28,7 +24,7 @@ comptime { // ABI warning export fn stage2_zen(ptr: *[*]const u8, len: *usize) void { const info_zen = @import("main.zig").info_zen; - ptr.* = &info_zen; + ptr.* = info_zen; len.* = info_zen.len; } @@ -37,12 +33,6 @@ export fn stage2_panic(ptr: [*]const u8, len: usize) void { @panic(ptr[0..len]); } -// ABI warning -const TranslateMode = extern enum { - import, - translate, -}; - // ABI warning const Error = extern enum { None, @@ -103,14 +93,10 @@ export fn stage2_translate_c( out_errors_len: *usize, args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, - mode: TranslateMode, - resources_path: [*]const u8, + resources_path: [*:0]const u8, ) Error { - var errors: []translate_c.ClangErrMsg = undefined; - out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, switch (mode) { - .import => translate_c.Mode.import, - .translate => translate_c.Mode.translate, - }, &errors, resources_path) catch |err| switch (err) { + var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0]; + out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { error.SemanticAnalyzeFail => { out_errors_ptr.* = errors.ptr; out_errors_len.* = errors.len; @@ -128,6 +114,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { const c_out_stream = &std.io.COutStream.init(output_file).stream; _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) { + error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode error.SystemResources => return Error.SystemResources, error.OperationAborted => return Error.OperationAborted, error.BrokenPipe => return Error.BrokenPipe, @@ -144,19 +131,19 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { // TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377, // we use a blocking implementation. -export fn stage2_fmt(argc: c_int, argv: [*]const [*]const u8) c_int { +export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int { if (std.debug.runtime_safety) { fmtMain(argc, argv) catch unreachable; } else { fmtMain(argc, argv) catch |e| { - std.debug.warn("{}\n", @errorName(e)); + std.debug.warn("{}\n", .{@errorName(e)}); return -1; }; } return 0; } -fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void { +fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { const allocator = std.heap.c_allocator; var args_list = std.ArrayList([]const u8).init(allocator); const argc_usize = @intCast(usize, argc); @@ -165,51 +152,70 @@ fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void { try args_list.append(std.mem.toSliceConst(u8, argv[arg_i])); } - var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = stdout_file.outStream(); - stdout = &stdout_out_stream.stream; + stdout = &std.io.getStdOut().outStream().stream; + stderr_file = std.io.getStdErr(); + stderr = &stderr_file.outStream().stream; - stderr_file = try std.io.getStdErr(); - var stderr_out_stream = stderr_file.outStream(); - stderr = &stderr_out_stream.stream; + const args = args_list.toSliceConst()[2..]; - const args = args_list.toSliceConst(); - var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args[2..]); - defer flags.deinit(); + var color: errmsg.Color = .Auto; + var stdin_flag: bool = false; + var check_flag: bool = false; + var input_files = ArrayList([]const u8).init(allocator); - if (flags.present("help")) { - try stdout.write(self_hosted_main.usage_fmt); - process.exit(0); + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try stdout.write(self_hosted_main.usage_fmt); + process.exit(0); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + try stderr.write("expected [auto|on|off] after --color\n"); + process.exit(1); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--stdin")) { + stdin_flag = true; + } else if (mem.eql(u8, arg, "--check")) { + check_flag = true; + } else { + try stderr.print("unrecognized parameter: '{}'", .{arg}); + process.exit(1); + } + } else { + try input_files.append(arg); + } + } } - const color = blk: { - if (flags.single("color")) |color_flag| { - if (mem.eql(u8, color_flag, "auto")) { - break :blk errmsg.Color.Auto; - } else if (mem.eql(u8, color_flag, "on")) { - break :blk errmsg.Color.On; - } else if (mem.eql(u8, color_flag, "off")) { - break :blk errmsg.Color.Off; - } else unreachable; - } else { - break :blk errmsg.Color.Auto; - } - }; - - if (flags.present("stdin")) { - if (flags.positionals.len != 0) { + if (stdin_flag) { + if (input_files.len != 0) { try stderr.write("cannot use --stdin with positional arguments\n"); process.exit(1); } - var stdin_file = try io.getStdIn(); + const stdin_file = io.getStdIn(); var stdin = stdin_file.inStream(); const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); defer allocator.free(source_code); const tree = std.zig.parse(allocator, source_code) catch |err| { - try stderr.print("error parsing stdin: {}\n", err); + try stderr.print("error parsing stdin: {}\n", .{err}); process.exit(1); }; defer tree.deinit(); @@ -221,9 +227,9 @@ fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void { if (tree.errors.len != 0) { process.exit(1); } - if (flags.present("check")) { + if (check_flag) { const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree); - const code = if (anything_changed) u8(1) else u8(0); + const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); process.exit(code); } @@ -231,7 +237,7 @@ fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void { return; } - if (flags.positionals.len == 0) { + if (input_files.len == 0) { try stderr.write("expected at least one source file argument\n"); process.exit(1); } @@ -243,10 +249,8 @@ fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void { .allocator = allocator, }; - const check_mode = flags.present("check"); - - for (flags.positionals.toSliceConst()) |file_path| { - try fmtPath(&fmt, file_path, check_mode); + for (input_files.toSliceConst()) |file_path| { + try fmtPath(&fmt, file_path, check_flag); } if (fmt.any_error) { process.exit(1); @@ -274,21 +278,21 @@ const FmtError = error{ FileBusy, } || fs.File.OpenError; -fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { - const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); - defer fmt.allocator.free(file_path); - - if (try fmt.seen.put(file_path, {})) |_| return; +fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { + if (fmt.seen.exists(file_path)) return; + try fmt.seen.put(file_path); const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { error.IsDir, error.AccessDenied => { // TODO make event based (and dir.next()) - var dir = try fs.Dir.open(fmt.allocator, file_path); + var dir = try fs.cwd().openDirList(file_path); defer dir.close(); - while (try dir.next()) |entry| { - if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); + var dir_it = dir.iterate(); + + while (try dir_it.next()) |entry| { + if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name }); try fmtPath(fmt, full_path, check_mode); } } @@ -296,7 +300,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void }, else => { // TODO lock stderr printing - try stderr.print("unable to open '{}': {}\n", file_path, err); + try stderr.print("unable to open '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }, @@ -304,7 +308,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void defer fmt.allocator.free(source_code); const tree = std.zig.parse(fmt.allocator, source_code) catch |err| { - try stderr.print("error parsing file '{}': {}\n", file_path, err); + try stderr.print("error parsing file '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }; @@ -322,7 +326,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void if (check_mode) { const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree); if (anything_changed) { - try stderr.print("{}\n", file_path); + try stderr.print("{}\n", .{file_path}); fmt.any_error = true; } } else { @@ -331,7 +335,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree); if (anything_changed) { - try stderr.print("{}\n", file_path); + try stderr.print("{}\n", .{file_path}); try baf.finish(); } } @@ -343,7 +347,7 @@ const Fmt = struct { color: errmsg.Color, allocator: *mem.Allocator, - const SeenMap = std.StringHashMap(void); + const SeenMap = std.BufSet; }; fn printErrMsgToFile( @@ -355,9 +359,9 @@ fn printErrMsgToFile( color: errmsg.Color, ) !void { const color_on = switch (color) { - errmsg.Color.Auto => file.isTty(), - errmsg.Color.On => true, - errmsg.Color.Off => false, + .Auto => file.isTty(), + .On => true, + .Off => false, }; const lok_token = parse_error.loc(); const span = errmsg.Span{ @@ -376,7 +380,7 @@ fn printErrMsgToFile( const text = text_buf.toOwnedSlice(); const stream = &file.outStream().stream; - try stream.print("{}:{}:{}: error: {}\n", path, start_loc.line + 1, start_loc.column + 1, text); + try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); if (!color_on) return; @@ -422,18 +426,18 @@ export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextRes const textz = std.Buffer.init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text"); return stage2_DepNextResult{ .type_id = switch (token.id) { - .target => stage2_DepNextResult.TypeId.target, - .prereq => stage2_DepNextResult.TypeId.prereq, + .target => .target, + .prereq => .prereq, }, .textz = textz.toSlice().ptr, }; } -export const stage2_DepTokenizer = extern struct { +const stage2_DepTokenizer = extern struct { handle: *DepTokenizer, }; -export const stage2_DepNextResult = extern struct { +const stage2_DepNextResult = extern struct { type_id: TypeId, // when type_id == error --> error text @@ -442,7 +446,7 @@ export const stage2_DepNextResult = extern struct { // when type_id == prereq --> prereq pathname textz: [*]const u8, - export const TypeId = extern enum { + const TypeId = extern enum { error_, null_, target, @@ -456,3 +460,70 @@ export fn stage2_attach_segfault_handler() void { std.debug.attachSegfaultHandler(); } } + +// ABI warning +export fn stage2_progress_create() *std.Progress { + const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); + ptr.* = std.Progress{}; + return ptr; +} + +// ABI warning +export fn stage2_progress_destroy(progress: *std.Progress) void { + std.heap.c_allocator.destroy(progress); +} + +// ABI warning +export fn stage2_progress_start_root( + progress: *std.Progress, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + return progress.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ) catch @panic("timer unsupported"); +} + +// ABI warning +export fn stage2_progress_disable_tty(progress: *std.Progress) void { + progress.terminal = null; +} + +// ABI warning +export fn stage2_progress_start( + node: *std.Progress.Node, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); + child_node.* = node.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ); + child_node.activate(); + return child_node; +} + +// ABI warning +export fn stage2_progress_end(node: *std.Progress.Node) void { + node.end(); + if (&node.context.root != node) { + std.heap.c_allocator.destroy(node); + } +} + +// ABI warning +export fn stage2_progress_complete_one(node: *std.Progress.Node) void { + node.completeOne(); +} + +// ABI warning +export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { + node.completed_items = done_count; + node.estimated_total_items = total_count; + node.activate(); + node.context.maybeRefresh(); +} diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig deleted file mode 100644 index 15f09e8da..000000000 --- a/src-self-hosted/target.zig +++ /dev/null @@ -1,438 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const llvm = @import("llvm.zig"); -const CInt = @import("c_int.zig").CInt; - -pub const FloatAbi = enum { - Hard, - Soft, - SoftFp, -}; - -pub const Target = union(enum) { - Native, - Cross: Cross, - - pub const Cross = struct { - arch: builtin.Arch, - os: builtin.Os, - abi: builtin.Abi, - object_format: builtin.ObjectFormat, - }; - - pub fn objFileExt(self: Target) []const u8 { - return switch (self.getObjectFormat()) { - builtin.ObjectFormat.coff => ".obj", - else => ".o", - }; - } - - pub fn exeFileExt(self: Target) []const u8 { - return switch (self.getOs()) { - builtin.Os.windows => ".exe", - else => "", - }; - } - - pub fn libFileExt(self: Target, is_static: bool) []const u8 { - return switch (self.getOs()) { - builtin.Os.windows => if (is_static) ".lib" else ".dll", - else => if (is_static) ".a" else ".so", - }; - } - - pub fn getOs(self: Target) builtin.Os { - return switch (self) { - Target.Native => builtin.os, - @TagType(Target).Cross => |t| t.os, - }; - } - - pub fn getArch(self: Target) builtin.Arch { - switch (self) { - Target.Native => return builtin.arch, - @TagType(Target).Cross => |t| return t.arch, - } - } - - pub fn getAbi(self: Target) builtin.Abi { - return switch (self) { - Target.Native => builtin.abi, - @TagType(Target).Cross => |t| t.abi, - }; - } - - pub fn getObjectFormat(self: Target) builtin.ObjectFormat { - return switch (self) { - Target.Native => builtin.object_format, - @TagType(Target).Cross => |t| t.object_format, - }; - } - - pub fn isWasm(self: Target) bool { - return switch (self.getArch()) { - builtin.Arch.wasm32, builtin.Arch.wasm64 => true, - else => false, - }; - } - - pub fn isDarwin(self: Target) bool { - return switch (self.getOs()) { - builtin.Os.ios, builtin.Os.macosx => true, - else => false, - }; - } - - pub fn isWindows(self: Target) bool { - return switch (self.getOs()) { - builtin.Os.windows => true, - else => false, - }; - } - - /// TODO expose the arch and subarch separately - pub fn isArmOrThumb(self: Target) bool { - return switch (self.getArch()) { - builtin.Arch.arm, - builtin.Arch.armeb, - builtin.Arch.aarch64, - builtin.Arch.aarch64_be, - builtin.Arch.thumb, - builtin.Arch.thumbeb, - => true, - else => false, - }; - } - - pub fn initializeAll() void { - llvm.InitializeAllTargets(); - llvm.InitializeAllTargetInfos(); - llvm.InitializeAllTargetMCs(); - llvm.InitializeAllAsmPrinters(); - llvm.InitializeAllAsmParsers(); - } - - pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer { - var result = try std.Buffer.initSize(allocator, 0); - errdefer result.deinit(); - - // LLVM WebAssembly output support requires the target to be activated at - // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. - // - // LLVM determines the output format based on the abi suffix, - // defaulting to an object based on the architecture. The default format in - // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to - // explicitly set this ourself in order for it to work. - // - // This is fixed in LLVM 7 and you will be able to get wasm output by - // using the target triple `wasm32-unknown-unknown-unknown`. - const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi()); - - var out = &std.io.BufferOutStream.init(&result).stream; - try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); - - return result; - } - - pub fn is64bit(self: Target) bool { - return self.getArchPtrBitWidth() == 64; - } - - pub fn getArchPtrBitWidth(self: Target) u32 { - switch (self.getArch()) { - builtin.Arch.avr, - builtin.Arch.msp430, - => return 16, - - builtin.Arch.arc, - builtin.Arch.arm, - builtin.Arch.armeb, - builtin.Arch.hexagon, - builtin.Arch.le32, - builtin.Arch.mips, - builtin.Arch.mipsel, - builtin.Arch.powerpc, - builtin.Arch.r600, - builtin.Arch.riscv32, - builtin.Arch.sparc, - builtin.Arch.sparcel, - builtin.Arch.tce, - builtin.Arch.tcele, - builtin.Arch.thumb, - builtin.Arch.thumbeb, - builtin.Arch.i386, - builtin.Arch.xcore, - builtin.Arch.nvptx, - builtin.Arch.amdil, - builtin.Arch.hsail, - builtin.Arch.spir, - builtin.Arch.kalimba, - builtin.Arch.shave, - builtin.Arch.lanai, - builtin.Arch.wasm32, - builtin.Arch.renderscript32, - => return 32, - - builtin.Arch.aarch64, - builtin.Arch.aarch64_be, - builtin.Arch.mips64, - builtin.Arch.mips64el, - builtin.Arch.powerpc64, - builtin.Arch.powerpc64le, - builtin.Arch.riscv64, - builtin.Arch.x86_64, - builtin.Arch.nvptx64, - builtin.Arch.le64, - builtin.Arch.amdil64, - builtin.Arch.hsail64, - builtin.Arch.spir64, - builtin.Arch.wasm64, - builtin.Arch.renderscript64, - builtin.Arch.amdgcn, - builtin.Arch.bpfel, - builtin.Arch.bpfeb, - builtin.Arch.sparcv9, - builtin.Arch.s390x, - => return 64, - } - } - - pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getAbi()) { - builtin.Abi.gnueabihf, - builtin.Abi.eabihf, - builtin.Abi.musleabihf, - => FloatAbi.Hard, - else => FloatAbi.Soft, - }; - } - - pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { - const env = self.getAbi(); - const arch = self.getArch(); - const os = self.getOs(); - switch (os) { - builtin.Os.freebsd => { - return "/libexec/ld-elf.so.1"; - }, - builtin.Os.linux => { - switch (env) { - builtin.Abi.android => { - if (self.is64bit()) { - return "/system/bin/linker64"; - } else { - return "/system/bin/linker"; - } - }, - builtin.Abi.gnux32 => { - if (arch == builtin.Arch.x86_64) { - return "/libx32/ld-linux-x32.so.2"; - } - }, - builtin.Abi.musl, - builtin.Abi.musleabi, - builtin.Abi.musleabihf, - => { - if (arch == builtin.Arch.x86_64) { - return "/lib/ld-musl-x86_64.so.1"; - } - }, - else => {}, - } - switch (arch) { - builtin.Arch.i386, - builtin.Arch.sparc, - builtin.Arch.sparcel, - => return "/lib/ld-linux.so.2", - - builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1", - - builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", - - builtin.Arch.arm, - builtin.Arch.thumb, - => return switch (self.getFloatAbi()) { - FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", - else => return "/lib/ld-linux.so.3", - }, - - builtin.Arch.armeb, - builtin.Arch.thumbeb, - => return switch (self.getFloatAbi()) { - FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", - else => return "/lib/ld-linux.so.3", - }, - - builtin.Arch.mips, - builtin.Arch.mipsel, - builtin.Arch.mips64, - builtin.Arch.mips64el, - => return null, - - builtin.Arch.powerpc => return "/lib/ld.so.1", - builtin.Arch.powerpc64 => return "/lib64/ld64.so.2", - builtin.Arch.powerpc64le => return "/lib64/ld64.so.2", - builtin.Arch.s390x => return "/lib64/ld64.so.1", - builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2", - builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2", - - builtin.Arch.arc, - builtin.Arch.avr, - builtin.Arch.bpfel, - builtin.Arch.bpfeb, - builtin.Arch.hexagon, - builtin.Arch.msp430, - builtin.Arch.r600, - builtin.Arch.amdgcn, - builtin.Arch.riscv32, - builtin.Arch.riscv64, - builtin.Arch.tce, - builtin.Arch.tcele, - builtin.Arch.xcore, - builtin.Arch.nvptx, - builtin.Arch.nvptx64, - builtin.Arch.le32, - builtin.Arch.le64, - builtin.Arch.amdil, - builtin.Arch.amdil64, - builtin.Arch.hsail, - builtin.Arch.hsail64, - builtin.Arch.spir, - builtin.Arch.spir64, - builtin.Arch.kalimba, - builtin.Arch.shave, - builtin.Arch.lanai, - builtin.Arch.wasm32, - builtin.Arch.wasm64, - builtin.Arch.renderscript32, - builtin.Arch.renderscript64, - => return null, - } - }, - else => return null, - } - } - - pub fn llvmTargetFromTriple(triple: std.Buffer) !*llvm.Target { - var result: *llvm.Target = undefined; - var err_msg: [*]u8 = undefined; - if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) { - std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg); - return error.UnsupportedTarget; - } - return result; - } - - pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 { - const arch = self.getArch(); - switch (self.getOs()) { - builtin.Os.freestanding => switch (self.getArch()) { - builtin.Arch.msp430 => switch (id) { - CInt.Id.Short, - CInt.Id.UShort, - CInt.Id.Int, - CInt.Id.UInt, - => return 16, - CInt.Id.Long, - CInt.Id.ULong, - => return 32, - CInt.Id.LongLong, - CInt.Id.ULongLong, - => return 64, - }, - else => switch (id) { - CInt.Id.Short, - CInt.Id.UShort, - => return 16, - CInt.Id.Int, - CInt.Id.UInt, - => return 32, - CInt.Id.Long, - CInt.Id.ULong, - => return self.getArchPtrBitWidth(), - CInt.Id.LongLong, - CInt.Id.ULongLong, - => return 64, - }, - }, - - builtin.Os.linux, - builtin.Os.macosx, - builtin.Os.freebsd, - builtin.Os.openbsd, - builtin.Os.zen, - => switch (id) { - CInt.Id.Short, - CInt.Id.UShort, - => return 16, - CInt.Id.Int, - CInt.Id.UInt, - => return 32, - CInt.Id.Long, - CInt.Id.ULong, - => return self.getArchPtrBitWidth(), - CInt.Id.LongLong, - CInt.Id.ULongLong, - => return 64, - }, - - builtin.Os.windows, builtin.Os.uefi => switch (id) { - CInt.Id.Short, - CInt.Id.UShort, - => return 16, - CInt.Id.Int, - CInt.Id.UInt, - => return 32, - CInt.Id.Long, - CInt.Id.ULong, - CInt.Id.LongLong, - CInt.Id.ULongLong, - => return 64, - }, - - builtin.Os.ananas, - builtin.Os.cloudabi, - builtin.Os.dragonfly, - builtin.Os.fuchsia, - builtin.Os.ios, - builtin.Os.kfreebsd, - builtin.Os.lv2, - builtin.Os.netbsd, - builtin.Os.solaris, - builtin.Os.haiku, - builtin.Os.minix, - builtin.Os.rtems, - builtin.Os.nacl, - builtin.Os.cnk, - builtin.Os.aix, - builtin.Os.cuda, - builtin.Os.nvcl, - builtin.Os.amdhsa, - builtin.Os.ps4, - builtin.Os.elfiamcu, - builtin.Os.tvos, - builtin.Os.watchos, - builtin.Os.mesa3d, - builtin.Os.contiki, - builtin.Os.amdpal, - builtin.Os.hermit, - builtin.Os.hurd, - builtin.Os.wasi, - => @panic("TODO specify the C integer type sizes for this OS"), - } - } - - pub fn getDarwinArchString(self: Target) []const u8 { - const arch = self.getArch(); - switch (arch) { - builtin.Arch.aarch64 => return "arm64", - builtin.Arch.thumb, - builtin.Arch.arm, - => return "arm", - builtin.Arch.powerpc => return "ppc", - builtin.Arch.powerpc64 => return "ppc64", - builtin.Arch.powerpc64le => return "ppc64le", - else => return @tagName(arch), - } - } -}; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 526518ca4..8c322e5fb 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -1,7 +1,6 @@ const std = @import("std"); const mem = std.mem; -const builtin = @import("builtin"); -const Target = @import("target.zig").Target; +const Target = std.Target; const Compilation = @import("compilation.zig").Compilation; const introspect = @import("introspect.zig"); const testing = std.testing; @@ -11,20 +10,26 @@ const ZigCompiler = @import("compilation.zig").ZigCompiler; var ctx: TestContext = undefined; test "stage2" { + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; + + // TODO https://github.com/ziglang/zig/issues/1364 + // TODO https://github.com/ziglang/zig/issues/3117 + if (true) return error.SkipZigTest; + try ctx.init(); defer ctx.deinit(); - try @import("../test/stage2/compile_errors.zig").addCases(&ctx); - try @import("../test/stage2/compare_output.zig").addCases(&ctx); + try @import("stage2_tests").addCases(&ctx); try ctx.run(); } const file1 = "1.zig"; -const allocator = std.heap.c_allocator; +// TODO https://github.com/ziglang/zig/issues/3783 +const allocator = std.heap.page_allocator; pub const TestContext = struct { - loop: std.event.Loop, zig_compiler: ZigCompiler, zig_lib_dir: []u8, file_index: std.atomic.Int(usize), @@ -36,47 +41,37 @@ pub const TestContext = struct { fn init(self: *TestContext) !void { self.* = TestContext{ .any_err = {}, - .loop = undefined, .zig_compiler = undefined, .zig_lib_dir = undefined, .group = undefined, .file_index = std.atomic.Int(usize).init(0), }; - try self.loop.initSingleThreaded(allocator); - errdefer self.loop.deinit(); - - self.zig_compiler = try ZigCompiler.init(&self.loop); + self.zig_compiler = try ZigCompiler.init(allocator); errdefer self.zig_compiler.deinit(); - self.group = std.event.Group(anyerror!void).init(&self.loop); - errdefer self.group.deinit(); + self.group = std.event.Group(anyerror!void).init(allocator); + errdefer self.group.wait() catch {}; self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); errdefer allocator.free(self.zig_lib_dir); try std.fs.makePath(allocator, tmp_dir_name); - errdefer std.fs.deleteTree(allocator, tmp_dir_name) catch {}; + errdefer std.fs.deleteTree(tmp_dir_name) catch {}; } fn deinit(self: *TestContext) void { - std.fs.deleteTree(allocator, tmp_dir_name) catch {}; + std.fs.deleteTree(tmp_dir_name) catch {}; allocator.free(self.zig_lib_dir); self.zig_compiler.deinit(); - self.loop.deinit(); } fn run(self: *TestContext) !void { - const handle = try self.loop.call(waitForGroup, self); - defer cancel handle; - self.loop.run(); + std.event.Loop.startCpuBoundOperation(); + self.any_err = self.group.wait(); return self.any_err; } - async fn waitForGroup(self: *TestContext) void { - self.any_err = await (async self.group.wait() catch unreachable); - } - fn testCompileError( self: *TestContext, source: []const u8, @@ -86,8 +81,8 @@ pub const TestContext = struct { msg: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); - const file1_path = try std.fs.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 }); + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); if (std.fs.path.dirname(file1_path)) |dirname| { try std.fs.makePath(allocator, dirname); @@ -100,9 +95,9 @@ pub const TestContext = struct { &self.zig_compiler, "test", file1_path, - Target.Native, - Compilation.Kind.Obj, - builtin.Mode.Debug, + .Native, + .Obj, + .Debug, true, // is_static self.zig_lib_dir, ); @@ -119,10 +114,10 @@ pub const TestContext = struct { expected_output: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); - const file1_path = try std.fs.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 }); + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); - const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); + const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); if (std.fs.path.dirname(file1_path)) |dirname| { try std.fs.makePath(allocator, dirname); } @@ -134,9 +129,9 @@ pub const TestContext = struct { &self.zig_compiler, "test", file1_path, - Target.Native, - Compilation.Kind.Exe, - builtin.Mode.Debug, + .Native, + .Exe, + .Debug, false, self.zig_lib_dir, ); @@ -153,16 +148,13 @@ pub const TestContext = struct { comp: *Compilation, exe_file: []const u8, expected_output: []const u8, - ) !void { - // TODO this should not be necessary - const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file); - + ) anyerror!void { defer comp.destroy(); - const build_event = await (async comp.events.get() catch unreachable); + const build_event = comp.events.get(); switch (build_event) { - Compilation.Event.Ok => { - const argv = []const []const u8{exe_file_2}; + .Ok => { + const argv = [_][]const u8{exe_file}; // TODO use event loop const child = try std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); switch (child.term) { @@ -179,13 +171,13 @@ pub const TestContext = struct { return error.OutputMismatch; } }, - Compilation.Event.Error => |err| return err, - Compilation.Event.Fail => |msgs| { - var stderr = try std.io.getStdErr(); + .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err, + .Fail => |msgs| { + const stderr = std.io.getStdErr(); try stderr.write("build incorrectly failed:\n"); for (msgs) |msg| { defer msg.destroy(); - try msg.printToFile(stderr, errmsg.Color.Auto); + try msg.printToFile(stderr, .Auto); } }, } @@ -198,18 +190,18 @@ pub const TestContext = struct { line: usize, column: usize, text: []const u8, - ) !void { + ) anyerror!void { defer comp.destroy(); - const build_event = await (async comp.events.get() catch unreachable); + const build_event = comp.events.get(); switch (build_event) { - Compilation.Event.Ok => { + .Ok => { @panic("build incorrectly succeeded"); }, - Compilation.Event.Error => |err| { + .Error => |err| { @panic("build incorrectly failed"); }, - Compilation.Event.Fail => |msgs| { + .Fail => |msgs| { testing.expect(msgs.len != 0); for (msgs) |msg| { if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) { @@ -222,21 +214,20 @@ pub const TestContext = struct { } } } - std.debug.warn( - "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", + std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{ source, path, line, column, text, - ); - std.debug.warn("\n====found:========\n"); - var stderr = try std.io.getStdErr(); + }); + std.debug.warn("\n====found:========\n", .{}); + const stderr = std.io.getStdErr(); for (msgs) |msg| { defer msg.destroy(); try msg.printToFile(stderr, errmsg.Color.Auto); } - std.debug.warn("============\n"); + std.debug.warn("============\n", .{}); return error.TestFailed; }, } diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 6a91b8e7b..b45d0f61f 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1,20 +1,17 @@ -// This is the userland implementation of translate-c which will be used by both stage1 -// and stage2. Currently the only way it is used is with `zig translate-c-2`. +// This is the userland implementation of translate-c which is used by both stage1 +// and stage2. const std = @import("std"); -const builtin = @import("builtin"); const assert = std.debug.assert; const ast = std.zig.ast; const Token = std.zig.Token; usingnamespace @import("clang.zig"); +const ctok = @import("c_tokenizer.zig"); +const CToken = ctok.CToken; +const mem = std.mem; +const math = std.math; -pub const Mode = enum { - import, - translate, -}; - -// TODO merge with Type.Fn.CallingConvention -const CallingConvention = builtin.TypeInfo.CallingConvention; +const CallingConvention = std.builtin.CallingConvention; pub const ClangErrMsg = Stage2ErrorMsg; @@ -22,7 +19,7 @@ pub const Error = error{OutOfMemory}; const TypeError = Error || error{UnsupportedType}; const TransError = TypeError || error{UnsupportedTranslation}; -const DeclTable = std.HashMap(usize, void, addrHash, addrEql); +const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql); fn addrHash(x: usize) u32 { switch (@typeInfo(usize).Int.bits) { @@ -38,106 +35,226 @@ fn addrEql(a: usize, b: usize) bool { return a == b; } +const SymbolTable = std.StringHashMap(*ast.Node); +const AliasList = std.SegmentedList(struct { + alias: []const u8, + name: []const u8, +}, 4); + const Scope = struct { id: Id, parent: ?*Scope, const Id = enum { Switch, - Var, Block, Root, - While, - }; - const Switch = struct { - base: Scope, + Condition, + Loop, }; - const Var = struct { + const Switch = struct { base: Scope, - c_name: []const u8, - zig_name: []const u8, + pending_block: *ast.Node.Block, + cases: *ast.Node.Switch.CaseList, + has_default: bool = false, }; const Block = struct { base: Scope, block_node: *ast.Node.Block, + variables: AliasList, + label: ?[]const u8, + mangle_count: u32 = 0, - /// Don't forget to set rbrace token later - fn create(c: *Context, parent: *Scope, lbrace_tok: ast.TokenIndex) !*Block { + /// Don't forget to set rbrace token and block_node later + fn init(c: *Context, parent: *Scope, label: ?[]const u8) !*Block { const block = try c.a().create(Block); - block.* = Block{ - .base = Scope{ - .id = Id.Block, + block.* = .{ + .base = .{ + .id = .Block, .parent = parent, }, - .block_node = try c.a().create(ast.Node.Block), - }; - block.block_node.* = ast.Node.Block{ - .base = ast.Node{ .id = ast.Node.Id.Block }, - .label = null, - .lbrace = lbrace_tok, - .statements = ast.Node.Block.StatementList.init(c.a()), - .rbrace = undefined, + .block_node = undefined, + .variables = AliasList.init(c.a()), + .label = label, }; return block; } + + /// Given the desired name, return a name that does not shadow anything from outer scopes. + /// Inserts the returned name into the scope. + fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 { + var proposed_name = name; + while (scope.contains(proposed_name)) { + scope.mangle_count += 1; + proposed_name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, scope.mangle_count }); + } + try scope.variables.push(.{ .name = name, .alias = proposed_name }); + return proposed_name; + } + + fn getAlias(scope: *Block, name: []const u8) []const u8 { + var it = scope.variables.iterator(0); + while (it.next()) |p| { + if (mem.eql(u8, p.name, name)) + return p.alias; + } + return scope.base.parent.?.getAlias(name); + } + + fn localContains(scope: *Block, name: []const u8) bool { + var it = scope.variables.iterator(0); + while (it.next()) |p| { + if (mem.eql(u8, p.name, name)) + return true; + } + return false; + } + + fn contains(scope: *Block, name: []const u8) bool { + if (scope.localContains(name)) + return true; + return scope.base.parent.?.contains(name); + } }; const Root = struct { base: Scope, + sym_table: SymbolTable, + macro_table: SymbolTable, + context: *Context, + + fn init(c: *Context) Root { + return .{ + .base = .{ + .id = .Root, + .parent = null, + }, + .sym_table = SymbolTable.init(c.a()), + .macro_table = SymbolTable.init(c.a()), + .context = c, + }; + } + + /// Check if the global scope contains this name, without looking into the "future", e.g. + /// ignore the preprocessed decl and macro names. + fn containsNow(scope: *Root, name: []const u8) bool { + return isZigPrimitiveType(name) or + scope.sym_table.contains(name) or + scope.macro_table.contains(name); + } + + /// Check if the global scope contains the name, includes all decls that haven't been translated yet. + fn contains(scope: *Root, name: []const u8) bool { + return scope.containsNow(name) or scope.context.global_names.contains(name); + } }; - const While = struct { - base: Scope, - }; + fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => unreachable, + .Block => return @fieldParentPtr(Block, "base", scope), + .Condition => { + // comma operator used + return try Block.init(c, scope, "blk"); + }, + else => scope = scope.parent.?, + } + } + } + + fn getAlias(scope: *Scope, name: []const u8) []const u8 { + return switch (scope.id) { + .Root => return name, + .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), + .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), + }; + } + + fn contains(scope: *Scope, name: []const u8) bool { + return switch (scope.id) { + .Root => @fieldParentPtr(Root, "base", scope).contains(name), + .Block => @fieldParentPtr(Block, "base", scope).contains(name), + .Switch, .Loop, .Condition => scope.parent.?.contains(name), + }; + } + + fn getBreakableScope(inner: *Scope) *Scope { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => unreachable, + .Switch => return scope, + .Loop => return scope, + else => scope = scope.parent.?, + } + } + } + + fn getSwitch(inner: *Scope) *Scope.Switch { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => unreachable, + .Switch => return @fieldParentPtr(Switch, "base", scope), + else => scope = scope.parent.?, + } + } + } }; -const TransResult = struct { - node: *ast.Node, - node_scope: *Scope, - child_scope: *Scope, -}; - -const Context = struct { +pub const Context = struct { tree: *ast.Tree, source_buffer: *std.Buffer, err: Error, source_manager: *ZigClangSourceManager, decl_table: DeclTable, + alias_list: AliasList, global_scope: *Scope.Root, - mode: Mode, - ptr_params: std.BufSet, clang_context: *ZigClangASTContext, + mangle_count: u32 = 0, - fn a(c: *Context) *std.mem.Allocator { + /// This one is different than the root scope's name table. This contains + /// a list of names that we found by visiting all the top level decls without + /// translating them. The other maps are updated as we translate; this one is updated + /// up front in a pre-processing step. + global_names: std.StringHashMap(void), + + fn getMangle(c: *Context) u32 { + c.mangle_count += 1; + return c.mangle_count; + } + + fn a(c: *Context) *mem.Allocator { return &c.tree.arena_allocator.allocator; } /// Convert a null-terminated C string to a slice allocated in the arena - fn str(c: *Context, s: [*]const u8) ![]u8 { - return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s)); + fn str(c: *Context, s: [*:0]const u8) ![]u8 { + return mem.dupe(c.a(), u8, mem.toSliceConst(u8, s)); } /// Convert a clang source location to a file:line:column string fn locStr(c: *Context, loc: ZigClangSourceLocation) ![]u8 { const spelling_loc = ZigClangSourceManager_getSpellingLoc(c.source_manager, loc); const filename_c = ZigClangSourceManager_getFilename(c.source_manager, spelling_loc); - const filename = if (filename_c) |s| try c.str(s) else ([]const u8)("(no file)"); + const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)"); const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc); const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc); - return std.fmt.allocPrint(c.a(), "{}:{}:{}", filename, line, column); + return std.fmt.allocPrint(c.a(), "{}:{}:{}", .{ filename, line, column }); } }; pub fn translate( - backing_allocator: *std.mem.Allocator, + backing_allocator: *mem.Allocator, args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, - mode: Mode, errors: *[]ClangErrMsg, - resources_path: [*]const u8, + resources_path: [*:0]const u8, ) !*ast.Tree { const ast_unit = ZigClangLoadFromCommandLine( args_begin, @@ -151,18 +268,23 @@ pub fn translate( }; defer ZigClangASTUnit_delete(ast_unit); - var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); - errdefer tree_arena.deinit(); + const tree = blk: { + var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); + errdefer tree_arena.deinit(); - const tree = try tree_arena.allocator.create(ast.Tree); - tree.* = ast.Tree{ - .source = undefined, // need to use Buffer.toOwnedSlice later - .root_node = undefined, - .arena_allocator = tree_arena, - .tokens = undefined, // can't reference the allocator yet - .errors = undefined, // can't reference the allocator yet + const tree = try tree_arena.allocator.create(ast.Tree); + tree.* = ast.Tree{ + .source = undefined, // need to use Buffer.toOwnedSlice later + .root_node = undefined, + .arena_allocator = tree_arena, + .tokens = undefined, // can't reference the allocator yet + .errors = undefined, // can't reference the allocator yet + .generated = true, + }; + break :blk tree; }; const arena = &tree.arena_allocator.allocator; // now we can reference the allocator + errdefer tree.arena_allocator.deinit(); tree.tokens = ast.Tree.TokenList.init(arena); tree.errors = ast.Tree.ErrorList.init(arena); @@ -170,7 +292,6 @@ pub fn translate( tree.root_node.* = ast.Node.Root{ .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), - .doc_comments = null, // initialized with the eof token at the end .eof_token = undefined, }; @@ -183,36 +304,75 @@ pub fn translate( .source_manager = ZigClangASTUnit_getSourceManager(ast_unit), .err = undefined, .decl_table = DeclTable.init(arena), + .alias_list = AliasList.init(arena), .global_scope = try arena.create(Scope.Root), - .mode = mode, - .ptr_params = std.BufSet.init(arena), .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?, + .global_names = std.StringHashMap(void).init(arena), }; - context.global_scope.* = Scope.Root{ - .base = Scope{ - .id = Scope.Id.Root, - .parent = null, - }, - }; + context.global_scope.* = Scope.Root.init(&context); + + try prepopulateGlobalNameTable(ast_unit, &context); if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) { return context.err; } + try transPreprocessorEntities(&context, ast_unit); + + try addMacros(&context); + var it = context.alias_list.iterator(0); + while (it.next()) |alias| { + if (!context.global_scope.sym_table.contains(alias.alias)) { + try createAlias(&context, alias); + } + } + tree.root_node.eof_token = try appendToken(&context, .Eof, ""); tree.source = source_buffer.toOwnedSlice(); if (false) { - std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", tree.source); + std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", .{tree.source}); var i: usize = 0; while (i < tree.tokens.len) : (i += 1) { const token = tree.tokens.at(i); - std.debug.warn("{}\n", token); + std.debug.warn("{}\n", .{token}); } } return tree; } -extern fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) bool { +fn prepopulateGlobalNameTable(ast_unit: *ZigClangASTUnit, c: *Context) !void { + if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, declVisitorNamesOnlyC)) { + return c.err; + } + + // TODO if we see #undef, delete it from the table + var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(ast_unit); + const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(ast_unit); + + while (it.I != it_end.I) : (it.I += 1) { + const entity = ZigClangPreprocessingRecord_iterator_deref(it); + switch (ZigClangPreprocessedEntity_getKind(entity)) { + .MacroDefinitionKind => { + const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity); + const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro); + const name = try c.str(raw_name); + _ = try c.global_names.put(name, {}); + }, + else => {}, + } + } +} + +fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool { + const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); + declVisitorNamesOnly(c, decl) catch |err| { + c.err = err; + return false; + }; + return true; +} + +fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool { const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); declVisitor(c, decl) catch |err| { c.err = err; @@ -221,59 +381,83 @@ extern fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) bool { return true; } +fn declVisitorNamesOnly(c: *Context, decl: *const ZigClangDecl) Error!void { + if (ZigClangDecl_castToNamedDecl(decl)) |named_decl| { + const decl_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(named_decl)); + _ = try c.global_names.put(decl_name, {}); + } +} + fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { switch (ZigClangDecl_getKind(decl)) { .Function => { return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl)); }, .Typedef => { - try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for typedefs"); + _ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl), true); }, .Enum => { - try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for enums"); + _ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl)); }, .Record => { - try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for structs"); + _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl)); }, .Var => { - try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for variables"); + return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl)); + }, + .Empty => { + // Do nothing }, else => { const decl_name = try c.str(ZigClangDecl_getDeclKindName(decl)); - try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", decl_name); + try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", .{decl_name}); }, } } fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { - if (try c.decl_table.put(@ptrToInt(fn_decl), {})) |_| return; // Avoid processing this decl twice + const fn_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, fn_decl))); + if (c.global_scope.sym_table.contains(fn_name)) + return; // Avoid processing this decl twice + + // Skip this declaration if a proper definition exists + if (!ZigClangFunctionDecl_isThisDeclarationADefinition(fn_decl)) { + if (ZigClangFunctionDecl_getDefinition(fn_decl)) |def| + return visitFnDecl(c, def); + } + const rp = makeRestorePoint(c); - const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); - const fn_qt = ZigClangFunctionDecl_getType(fn_decl); - const fn_type = ZigClangQualType_getTypePtr(fn_qt); - var scope = &c.global_scope.base; const has_body = ZigClangFunctionDecl_hasBody(fn_decl); const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); const decl_ctx = FnDeclContext{ .fn_name = fn_name, .has_body = has_body, .storage_class = storage_class, - .scope = &scope, .is_export = switch (storage_class) { - .None => has_body and c.mode != .import, + .None => has_body and !ZigClangFunctionDecl_isInlineSpecified(fn_decl), .Extern, .Static => false, - .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"), + .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}), .Auto => unreachable, // Not legal on functions .Register => unreachable, // Not legal on functions + else => unreachable, }, }; + + var fn_qt = ZigClangFunctionDecl_getType(fn_decl); + var fn_type = ZigClangQualType_getTypePtr(fn_qt); + if (ZigClangType_getTypeClass(fn_type) == .Attributed) { + const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type); + fn_qt = ZigClangAttributedType_getEquivalentType(attr_type); + fn_type = ZigClangQualType_getTypePtr(fn_qt); + } + const proto_node = switch (ZigClangType_getTypeClass(fn_type)) { .FunctionProto => blk: { const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type); - break :blk transFnProto(rp, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { + break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { - return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); + return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); }, error.OutOfMemory => |e| return e, }; @@ -282,12 +466,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type); break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { - return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); + return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); }, error.OutOfMemory => |e| return e, }; }, - else => unreachable, + else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}), }; if (!decl_ctx.has_body) { @@ -297,18 +481,500 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { // actual function definition with body const body_stmt = ZigClangFunctionDecl_getBody(fn_decl); - const result = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { + const block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null); + var scope = &block_scope.base; + const block_node = try transCreateNodeBlock(rp.c, null); + block_scope.block_node = block_node; + + var it = proto_node.params.iterator(0); + while (it.next()) |p| { + const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*); + const param_name = if (param.name_token) |name_tok| + tokenSlice(c, name_tok) + else if (param.var_args_token != null) { + assert(it.next() == null); + _ = proto_node.params.pop(); + break; + } else + return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}); + + const mangled_param_name = try block_scope.makeMangledName(c, param_name); + + const arg_name = blk: { + const bare_arg_name = try std.fmt.allocPrint(c.a(), "arg_{}", .{mangled_param_name}); + break :blk try block_scope.makeMangledName(c, bare_arg_name); + }; + + const node = try transCreateNodeVarDecl(c, false, false, mangled_param_name); + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = try transCreateNodeIdentifier(c, arg_name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + try block_node.statements.push(&node.base); + param.name_token = try appendIdentifier(c, arg_name); + _ = try appendToken(c, .Colon, ":"); + } + + transCompoundStmtInline(rp, &block_scope.base, @ptrCast(*const ZigClangCompoundStmt, body_stmt), block_node) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, - => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function"), + => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), }; - assert(result.node.id == ast.Node.Id.Block); - proto_node.body_node = result.node; - + block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + proto_node.body_node = &block_node.base; return addTopLevelDecl(c, fn_name, &proto_node.base); } +fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { + const var_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, var_decl))); + if (c.global_scope.sym_table.contains(var_name)) + return; // Avoid processing this decl twice + const rp = makeRestorePoint(c); + const visib_tok = try appendToken(c, .Keyword_pub, "pub"); + + const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) + null + else + try appendToken(c, .Keyword_threadlocal, "threadlocal"); + + const scope = &c.global_scope.base; + + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "_{}", .{var_name}) else var_name; + const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); + + const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); + const storage_class = ZigClangVarDecl_getStorageClass(var_decl); + const is_const = ZigClangQualType_isConstQualified(qual_type); + + const extern_tok = if (storage_class == .Extern) + try appendToken(c, .Keyword_extern, "extern") + else if (storage_class != .Static) + try appendToken(c, .Keyword_export, "export") + else + null; + + const mut_tok = if (is_const) + try appendToken(c, .Keyword_const, "const") + else + try appendToken(c, .Keyword_var, "var"); + + const name_tok = try appendIdentifier(c, checked_name); + + _ = try appendToken(c, .Colon, ":"); + const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) { + error.UnsupportedType => { + return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); + }, + error.OutOfMemory => |e| return e, + }; + + var eq_tok: ast.TokenIndex = undefined; + var init_node: ?*ast.Node = null; + + // If the initialization expression is not present, initialize with undefined. + // If it is an integer literal, we can skip the @as since it will be redundant + // with the variable type. + if (ZigClangVarDecl_hasInit(var_decl)) { + eq_tok = try appendToken(c, .Equal, "="); + init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { + error.UnsupportedTranslation, + error.UnsupportedType, + => { + return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{}); + }, + error.OutOfMemory => |e| return e, + } + else + try transCreateNodeUndefinedLiteral(c); + } else if (storage_class != .Extern) { + return failDecl(c, var_decl_loc, checked_name, "non-extern variable has no initializer", .{}); + } + + const linksection_expr = blk: { + var str_len: usize = undefined; + if (ZigClangVarDecl_getSectionAttribute(var_decl, &str_len)) |str_ptr| { + _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); + _ = try appendToken(rp.c, .LParen, "("); + const expr = try transCreateNodeStringLiteral( + rp.c, + try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + ); + _ = try appendToken(rp.c, .RParen, ")"); + + break :blk expr; + } + break :blk null; + }; + + const align_expr = blk: { + const alignment = ZigClangVarDecl_getAlignedAttribute(var_decl, rp.c.clang_context); + if (alignment != 0) { + _ = try appendToken(rp.c, .Keyword_linksection, "align"); + _ = try appendToken(rp.c, .LParen, "("); + // Clang reports the alignment in bits + const expr = try transCreateNodeInt(rp.c, alignment / 8); + _ = try appendToken(rp.c, .RParen, ")"); + + break :blk expr; + } + break :blk null; + }; + + const node = try c.a().create(ast.Node.VarDecl); + node.* = ast.Node.VarDecl{ + .doc_comments = null, + .visib_token = visib_tok, + .thread_local_token = thread_local_token, + .name_token = name_tok, + .eq_token = eq_tok, + .mut_token = mut_tok, + .comptime_token = null, + .extern_export_token = extern_tok, + .lib_name = null, + .type_node = type_node, + .align_node = align_expr, + .section_node = linksection_expr, + .init_node = init_node, + .semicolon_token = try appendToken(c, .Semicolon, ";"), + }; + return addTopLevelDecl(c, checked_name, &node.base); +} + +fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node { + _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name); + return transCreateNodeIdentifier(c, builtin_name); +} + +fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv| + return transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice + const rp = makeRestorePoint(c); + + const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl))); + + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.a(), "_{}", .{typedef_name}) else typedef_name; + + if (mem.eql(u8, checked_name, "uint8_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u8") + else if (mem.eql(u8, checked_name, "int8_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i8") + else if (mem.eql(u8, checked_name, "uint16_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u16") + else if (mem.eql(u8, checked_name, "int16_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i16") + else if (mem.eql(u8, checked_name, "uint32_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u32") + else if (mem.eql(u8, checked_name, "int32_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i32") + else if (mem.eql(u8, checked_name, "uint64_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u64") + else if (mem.eql(u8, checked_name, "int64_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i64") + else if (mem.eql(u8, checked_name, "intptr_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "isize") + else if (mem.eql(u8, checked_name, "uintptr_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "usize") + else if (mem.eql(u8, checked_name, "ssize_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "isize") + else if (mem.eql(u8, checked_name, "size_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "usize"); + + if (!top_level_visit) { + return transCreateNodeIdentifier(c, checked_name); + } + + _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name); + const visib_tok = try appendToken(c, .Keyword_pub, "pub"); + const const_tok = try appendToken(c, .Keyword_const, "const"); + const node = try transCreateNodeVarDecl(c, true, true, checked_name); + node.eq_token = try appendToken(c, .Equal, "="); + + const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); + const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl); + node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, typedef_loc, checked_name, "unable to resolve typedef child type", .{}); + return null; + }, + error.OutOfMemory => |e| return e, + }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + try addTopLevelDecl(c, checked_name, &node.base); + return transCreateNodeIdentifier(c, checked_name); +} + +fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv| + return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice + const record_loc = ZigClangRecordDecl_getLocation(record_decl); + + var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl))); + var is_unnamed = false; + // Record declarations such as `struct {...} x` have no name but they're not + // anonymous hence here isAnonymousStructOrUnion is not needed + if (bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + is_unnamed = true; + } + + var container_kind_name: []const u8 = undefined; + var container_kind: std.zig.Token.Id = undefined; + if (ZigClangRecordDecl_isUnion(record_decl)) { + container_kind_name = "union"; + container_kind = .Keyword_union; + } else if (ZigClangRecordDecl_isStruct(record_decl)) { + container_kind_name = "struct"; + container_kind = .Keyword_struct; + } else { + try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name}); + return null; + } + + const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); + _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); + + const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); + + node.eq_token = try appendToken(c, .Equal, "="); + + var semicolon: ast.TokenIndex = undefined; + node.init_node = blk: { + const rp = makeRestorePoint(c); + const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse { + const opaque = try transCreateNodeOpaqueType(c); + semicolon = try appendToken(c, .Semicolon, ";"); + break :blk opaque; + }; + + const layout_tok = try if (ZigClangRecordDecl_getPackedAttribute(record_decl)) + appendToken(c, .Keyword_packed, "packed") + else + appendToken(c, .Keyword_extern, "extern"); + const container_tok = try appendToken(c, container_kind, container_kind_name); + const lbrace_token = try appendToken(c, .LBrace, "{"); + + const container_node = try c.a().create(ast.Node.ContainerDecl); + container_node.* = .{ + .layout_token = layout_tok, + .kind_token = container_tok, + .init_arg_expr = .None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), + .lbrace_token = lbrace_token, + .rbrace_token = undefined, + }; + + var it = ZigClangRecordDecl_field_begin(record_def); + const end_it = ZigClangRecordDecl_field_end(record_def); + while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) { + const field_decl = ZigClangRecordDecl_field_iterator_deref(it); + const field_loc = ZigClangFieldDecl_getLocation(field_decl); + + if (ZigClangFieldDecl_isBitField(field_decl)) { + const opaque = try transCreateNodeOpaqueType(c); + semicolon = try appendToken(c, .Semicolon, ";"); + try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk opaque; + } + + var is_anon = false; + var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); + if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { + raw_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + is_anon = true; + } + const field_name = try appendIdentifier(c, raw_name); + _ = try appendToken(c, .Colon, ":"); + const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name}); + return null; + }, + else => |e| return e, + }; + + const field_node = try c.a().create(ast.Node.ContainerField); + field_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .name_token = field_name, + .type_expr = field_type, + .value_expr = null, + .align_expr = null, + }; + + if (is_anon) { + _ = try c.decl_table.put( + @ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl)), + raw_name, + ); + } + + try container_node.fields_and_decls.push(&field_node.base); + _ = try appendToken(c, .Comma, ","); + } + container_node.rbrace_token = try appendToken(c, .RBrace, "}"); + semicolon = try appendToken(c, .Semicolon, ";"); + break :blk &container_node.base; + }; + node.semicolon_token = semicolon; + + try addTopLevelDecl(c, name, &node.base); + if (!is_unnamed) + try c.alias_list.push(.{ .alias = bare_name, .name = name }); + return transCreateNodeIdentifier(c, name); +} + +fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name| + return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice + const rp = makeRestorePoint(c); + const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); + + var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_decl))); + var is_unnamed = false; + if (bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + is_unnamed = true; + } + + const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); + _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); + const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); + node.eq_token = try appendToken(c, .Equal, "="); + + node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { + var pure_enum = true; + var it = ZigClangEnumDecl_enumerator_begin(enum_def); + var end_it = ZigClangEnumDecl_enumerator_end(enum_def); + while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { + const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); + if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| { + pure_enum = false; + break; + } + } + + const extern_tok = try appendToken(c, .Keyword_extern, "extern"); + const container_tok = try appendToken(c, .Keyword_enum, "enum"); + + const container_node = try c.a().create(ast.Node.ContainerDecl); + container_node.* = .{ + .layout_token = extern_tok, + .kind_token = container_tok, + .init_arg_expr = .None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), + .lbrace_token = undefined, + .rbrace_token = undefined, + }; + + const int_type = ZigClangEnumDecl_getIntegerType(enum_decl); + // The underlying type may be null in case of forward-declared enum + // types, while that's not ISO-C compliant many compilers allow this and + // default to the usual integer type used for all the enums. + + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (int_type.ptr != null and + !isCBuiltinType(int_type, .UInt) and + !isCBuiltinType(int_type, .Int)) + { + _ = try appendToken(c, .LParen, "("); + container_node.init_arg_expr = .{ + .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); + return null; + }, + else => |e| return e, + }, + }; + _ = try appendToken(c, .RParen, ")"); + } + + container_node.lbrace_token = try appendToken(c, .LBrace, "{"); + + it = ZigClangEnumDecl_enumerator_begin(enum_def); + end_it = ZigClangEnumDecl_enumerator_end(enum_def); + while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { + const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); + + const enum_val_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_const))); + + const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) + enum_val_name[bare_name.len..] + else + enum_val_name; + + const field_name_tok = try appendIdentifier(c, field_name); + + const int_node = if (!pure_enum) blk: { + _ = try appendToken(c, .Colon, "="); + break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const)); + } else + null; + + const field_node = try c.a().create(ast.Node.ContainerField); + field_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .name_token = field_name_tok, + .type_expr = null, + .value_expr = int_node, + .align_expr = null, + }; + + try container_node.fields_and_decls.push(&field_node.base); + _ = try appendToken(c, .Comma, ","); + + // In C each enum value is in the global namespace. So we put them there too. + // At this point we can rely on the enum emitting successfully. + const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name); + tld_node.eq_token = try appendToken(c, .Equal, "="); + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); + const enum_ident = try transCreateNodeIdentifier(c, name); + const period_tok = try appendToken(c, .Period, "."); + const field_ident = try transCreateNodeIdentifier(c, field_name); + const field_access_node = try c.a().create(ast.Node.InfixOp); + field_access_node.* = .{ + .op_token = period_tok, + .lhs = enum_ident, + .op = .Period, + .rhs = field_ident, + }; + try cast_node.params.push(&field_access_node.base); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + tld_node.init_node = &cast_node.base; + tld_node.semicolon_token = try appendToken(c, .Semicolon, ";"); + try addTopLevelDecl(c, field_name, &tld_node.base); + } + container_node.rbrace_token = try appendToken(c, .RBrace, "}"); + + break :blk &container_node.base; + } else + try transCreateNodeOpaqueType(c); + + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + + try addTopLevelDecl(c, name, &node.base); + if (!is_unnamed) + try c.alias_list.push(.{ .alias = bare_name, .name = name }); + return transCreateNodeIdentifier(c, name); +} + +fn createAlias(c: *Context, alias: var) !void { + const node = try transCreateNodeVarDecl(c, true, true, alias.alias); + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = try transCreateNodeIdentifier(c, alias.name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + return addTopLevelDecl(c, alias.alias, &node.base); +} + const ResultUsed = enum { used, unused, @@ -325,7 +991,7 @@ fn transStmt( stmt: *const ZigClangStmt, result_used: ResultUsed, lrvalue: LRValue, -) !TransResult { +) TransError!*ast.Node { const sc = ZigClangStmt_getStmtClass(stmt); switch (sc) { .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used), @@ -334,16 +1000,72 @@ fn transStmt( .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)), .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue), .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used), - .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used), + .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used, .with_as), .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)), .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used), + .ParenExprClass => { + const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue); + if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); + const node = try rp.c.a().create(ast.Node.GroupedExpression); + node.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = expr, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return maybeSuppressResult(rp, scope, result_used, &node.base); + }, + .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used), + .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), + .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)), + .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), + .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), + .NullStmtClass => { + const block = try transCreateNodeBlock(rp.c, null); + block.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block.base; + }, + .ContinueStmtClass => return try transCreateNodeContinue(rp.c), + .BreakStmtClass => return transBreak(rp, scope), + .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), + .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), + .ConditionalOperatorClass => { + return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used); + }, + .BinaryConditionalOperatorClass => { + return transBinaryConditionalOperator(rp, scope, @ptrCast(*const ZigClangBinaryConditionalOperator, stmt), result_used); + }, + .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)), + .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)), + .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), + .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), + .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), + .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used, .with_as), + .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used), + .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used), + .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used), + .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used), + .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used), + .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used), + .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used), + .OpaqueValueExprClass => { + const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?; + const expr = try transExpr(rp, scope, source_expr, .used, lrvalue); + if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); + const node = try rp.c.a().create(ast.Node.GroupedExpression); + node.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = expr, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return maybeSuppressResult(rp, scope, result_used, &node.base); + }, else => { return revertAndWarn( rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(stmt), "TODO implement translation of stmt class {}", - @tagName(sc), + .{@tagName(sc)}, ); }, } @@ -354,68 +1076,162 @@ fn transBinaryOperator( scope: *Scope, stmt: *const ZigClangBinaryOperator, result_used: ResultUsed, -) TransError!TransResult { +) TransError!*ast.Node { const op = ZigClangBinaryOperator_getOpcode(stmt); const qt = ZigClangBinaryOperator_getType(stmt); + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.InfixOp.Op = undefined; switch (op) { - .PtrMemD, .PtrMemI, .Cmp => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - op, - ), - .Assign => return TransResult{ - .node = &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, - .child_scope = scope, - .node_scope = scope, + .Assign => return transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)), + .Comma => { + const block_scope = try scope.findBlockScope(rp.c); + const expr = block_scope.base.parent == scope; + const lparen = if (expr) blk: { + const l = try appendToken(rp.c, .LParen, "("); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + break :blk l; + } else undefined; + + const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value); + try block_scope.block_node.statements.push(lhs); + + const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + if (expr) { + _ = try appendToken(rp.c, .Semicolon, ";"); + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = rhs; + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &block_scope.block_node.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); + } else { + return maybeSuppressResult(rp, scope, result_used, rhs); + } }, + .Div => { + if (!cIsUnsignedInteger(qt)) { + // signed integer division uses @divTrunc + const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); + try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + try div_trunc_node.params.push(rhs); + div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); + } + }, + .Rem => { + if (!cIsUnsignedInteger(qt)) { + // signed integer division uses @rem + const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); + try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + try rem_node.params.push(rhs); + rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &rem_node.base); + } + }, + .Shl => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .Shr => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .LAnd => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .LOr => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true); + return maybeSuppressResult(rp, scope, result_used, node); + }, + else => {}, + } + const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); + switch (op) { .Add => { - const node = if (cIsUnsignedInteger(qt)) - try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true) - else - try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .PlusPercent, "+%"); + op_id = .AddWrap; + } else { + op_token = try appendToken(rp.c, .Plus, "+"); + op_id = .Add; + } }, .Sub => { - const node = if (cIsUnsignedInteger(qt)) - try transCreateNodeInfixOp(rp, scope, stmt, .SubWrap, .MinusPercent, "-%", true) - else - try transCreateNodeInfixOp(rp, scope, stmt, .Sub, .Minus, "-", true); - return maybeSuppressResult(rp, scope, result_used, TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }); + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .MinusPercent, "-%"); + op_id = .SubWrap; + } else { + op_token = try appendToken(rp.c, .Minus, "-"); + op_id = .Sub; + } }, - .Mul, - .Div, - .Rem, - .Shl, - .Shr, - .LT, - .GT, - .LE, - .GE, - .EQ, - .NE, - .And, - .Xor, - .Or, - .LAnd, - .LOr, - .Comma, - => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - op, - ), + .Mul => { + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .AsteriskPercent, "*%"); + op_id = .MulWrap; + } else { + op_token = try appendToken(rp.c, .Asterisk, "*"); + op_id = .Mul; + } + }, + .Div => { + // unsigned/float division uses the operator + op_id = .Div; + op_token = try appendToken(rp.c, .Slash, "/"); + }, + .Rem => { + // unsigned/float division uses the operator + op_id = .Mod; + op_token = try appendToken(rp.c, .Percent, "%"); + }, + .LT => { + op_id = .LessThan; + op_token = try appendToken(rp.c, .AngleBracketLeft, "<"); + }, + .GT => { + op_id = .GreaterThan; + op_token = try appendToken(rp.c, .AngleBracketRight, ">"); + }, + .LE => { + op_id = .LessOrEqual; + op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<="); + }, + .GE => { + op_id = .GreaterOrEqual; + op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">="); + }, + .EQ => { + op_id = .EqualEqual; + op_token = try appendToken(rp.c, .EqualEqual, "=="); + }, + .NE => { + op_id = .BangEqual; + op_token = try appendToken(rp.c, .BangEqual, "!="); + }, + .And => { + op_id = .BitAnd; + op_token = try appendToken(rp.c, .Ampersand, "&"); + }, + .Xor => { + op_id = .BitXor; + op_token = try appendToken(rp.c, .Caret, "^"); + }, + .Or => { + op_id = .BitOr; + op_token = try appendToken(rp.c, .Pipe, "|"); + }, + .Assign, .MulAssign, .DivAssign, .RemAssign, @@ -427,7 +1243,11 @@ fn transBinaryOperator( .XorAssign, .OrAssign, => unreachable, + else => unreachable, } + + const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + return transCreateNodeInfixOp(rp, scope, lhs_node, op_id, op_token, rhs_node, result_used, true); } fn transCompoundStmtInline( @@ -435,33 +1255,22 @@ fn transCompoundStmtInline( parent_scope: *Scope, stmt: *const ZigClangCompoundStmt, block_node: *ast.Node.Block, -) TransError!TransResult { +) TransError!void { var it = ZigClangCompoundStmt_body_begin(stmt); const end_it = ZigClangCompoundStmt_body_end(stmt); - var scope = parent_scope; while (it != end_it) : (it += 1) { - const result = try transStmt(rp, parent_scope, it.*, .unused, .r_value); - scope = result.child_scope; - if (result.node != &block_node.base) - try block_node.statements.push(result.node); + const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value); + if (result != &block_node.base) + try block_node.statements.push(result); } - return TransResult{ - .node = &block_node.base, - .child_scope = scope, - .node_scope = scope, - }; } -fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) !TransResult { - const lbrace_tok = try appendToken(rp.c, .LBrace, "{"); - const block_scope = try Scope.Block.create(rp.c, scope, lbrace_tok); - const inline_result = try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); +fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { + const block_scope = try Scope.Block.init(rp.c, scope, null); + block_scope.block_node = try transCreateNodeBlock(rp.c, null); + try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - return TransResult{ - .node = &block_scope.block_node.base, - .node_scope = inline_result.node_scope, - .child_scope = inline_result.child_scope, - }; + return &block_scope.block_node.base; } fn transCStyleCastExprClass( @@ -470,7 +1279,7 @@ fn transCStyleCastExprClass( stmt: *const ZigClangCStyleCastExpr, result_used: ResultUsed, lrvalue: LRValue, -) !TransResult { +) TransError!*ast.Node { const sub_expr = ZigClangCStyleCastExpr_getSubExpr(stmt); const cast_node = (try transCCast( rp, @@ -478,104 +1287,62 @@ fn transCStyleCastExprClass( ZigClangCStyleCastExpr_getBeginLoc(stmt), ZigClangCStyleCastExpr_getType(stmt), ZigClangExpr_getType(sub_expr), - (try transExpr(rp, scope, sub_expr, .used, lrvalue)).node, + try transExpr(rp, scope, sub_expr, .used, lrvalue), )); - const cast_res = TransResult{ - .node = cast_node, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, cast_res); + return maybeSuppressResult(rp, scope, result_used, cast_node); } -fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDeclStmt) !TransResult { +fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node { const c = rp.c; - const block_scope = findBlockScope(parent_scope); - var scope = parent_scope; + const block_scope = scope.findBlockScope(c) catch unreachable; var it = ZigClangDeclStmt_decl_begin(stmt); const end_it = ZigClangDeclStmt_decl_end(stmt); while (it != end_it) : (it += 1) { - switch (ZigClangDecl_getKind(it.*)) { + switch (ZigClangDecl_getKind(it[0])) { .Var => { - const var_decl = @ptrCast(*const ZigClangVarDecl, it.*); + const var_decl = @ptrCast(*const ZigClangVarDecl, it[0]); const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) null else try appendToken(c, .Keyword_threadlocal, "threadlocal"); - const qual_type = ZigClangVarDecl_getType(var_decl); - const mut_token = if (ZigClangQualType_isConstQualified(qual_type)) - try appendToken(c, .Keyword_const, "const") - else - try appendToken(c, .Keyword_var, "var"); - const c_name = try c.str(ZigClangDecl_getName_bytes_begin( - @ptrCast(*const ZigClangDecl, var_decl), + const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); + const name = try c.str(ZigClangNamedDecl_getName_bytes_begin( + @ptrCast(*const ZigClangNamedDecl, var_decl), )); - const name_token = try appendToken(c, .Identifier, c_name); + const mangled_name = try block_scope.makeMangledName(c, name); + const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), mangled_name); - const var_scope = try c.a().create(Scope.Var); - var_scope.* = Scope.Var{ - .base = Scope{ .id = .Var, .parent = scope }, - .c_name = c_name, - .zig_name = c_name, // TODO: getWantedName - }; - scope = &var_scope.base; - - const colon_token = try appendToken(c, .Colon, ":"); + _ = try appendToken(c, .Colon, ":"); const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)); - const type_node = try transQualType(rp, qual_type, loc); + node.type_node = try transQualType(rp, qual_type, loc); - const eq_token = try appendToken(c, .Equal, "="); - const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| - (try transExpr(rp, scope, expr, .used, .r_value)).node - else blk: { - const undefined_token = try appendToken(c, .Keyword_undefined, "undefined"); - const undefined_node = try rp.c.a().create(ast.Node.UndefinedLiteral); - undefined_node.* = ast.Node.UndefinedLiteral{ - .base = ast.Node{ .id = .UndefinedLiteral }, - .token = undefined_token, - }; - break :blk &undefined_node.base; - }; - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .base = ast.Node{ .id = .VarDecl }, - .doc_comments = null, - .visib_token = null, - .thread_local_token = thread_local_token, - .name_token = name_token, - .eq_token = eq_token, - .mut_token = mut_token, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = type_node, - .align_node = null, // TODO ?*Node, - .section_node = null, - .init_node = init_node, - .semicolon_token = semicolon_token, - }; + node.eq_token = try appendToken(c, .Equal, "="); + var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + try transExprCoercing(rp, scope, expr, .used, .r_value) + else + try transCreateNodeUndefinedLiteral(c); + if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(init_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + init_node = &builtin_node.base; + } + node.init_node = init_node; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); }, - else => |kind| return revertAndWarn( rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO implement translation of DeclStmt kind {}", - @tagName(kind), + .{@tagName(kind)}, ), } } - - return TransResult{ - .node = &block_scope.block_node.base, - .node_scope = scope, - .child_scope = scope, - }; + return &block_scope.block_node.base; } fn transDeclRefExpr( @@ -583,17 +1350,11 @@ fn transDeclRefExpr( scope: *Scope, expr: *const ZigClangDeclRefExpr, lrvalue: LRValue, -) !TransResult { +) TransError!*ast.Node { const value_decl = ZigClangDeclRefExpr_getDecl(expr); - const c_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); - const zig_name = transLookupZigIdentifier(scope, c_name); - if (lrvalue == .l_value) try rp.c.ptr_params.put(zig_name); - const node = try appendIdentifier(rp.c, zig_name); - return TransResult{ - .node = node, - .node_scope = scope, - .child_scope = scope, - }; + const name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, value_decl))); + const mangled_name = scope.getAlias(name); + return transCreateNodeIdentifier(rp.c, mangled_name); } fn transImplicitCastExpr( @@ -601,81 +1362,284 @@ fn transImplicitCastExpr( scope: *Scope, expr: *const ZigClangImplicitCastExpr, result_used: ResultUsed, -) !TransResult { +) TransError!*ast.Node { const c = rp.c; const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr); - const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value); + const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); + const src_type = getExprQualType(c, sub_expr); switch (ZigClangImplicitCastExpr_getCastKind(expr)) { - .BitCast => { - const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); - const src_type = getExprQualType(c, sub_expr); - return TransResult{ - .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), - .node_scope = scope, - .child_scope = scope, - }; + .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { + const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); + return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, - .IntegralCast => { - const dest_type = ZigClangExpr_getType(@ptrCast(*const ZigClangExpr, expr)); - const src_type = ZigClangExpr_getType(sub_expr); - return TransResult{ - .node = try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node.node), - .node_scope = scope, - .child_scope = scope, - }; - }, - .FunctionToPointerDecay, .ArrayToPointerDecay => { + .LValueToRValue, .NoOp, .FunctionToPointerDecay => { + const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); return maybeSuppressResult(rp, scope, result_used, sub_expr_node); }, - .LValueToRValue => { - return transExpr(rp, scope, sub_expr, .used, .r_value); + .ArrayToPointerDecay => { + if (exprIsStringLiteral(sub_expr)) { + const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); + return maybeSuppressResult(rp, scope, result_used, sub_expr_node); + } + + const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value); + + return maybeSuppressResult(rp, scope, result_used, &prefix_op.base); + }, + .NullToPointer => { + return try transCreateNodeNullLiteral(rp.c); + }, + .PointerToBoolean => { + // @ptrToInt(val) != 0 + const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt"); + try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value)); + ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false); + }, + .IntegralToBoolean => { + const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); + + // The expression is already a boolean one, return it as-is + if (isBoolRes(sub_expr_node)) + return sub_expr_node; + + // val != 0 + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false); }, else => |kind| return revertAndWarn( rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, expr)), "TODO implement translation of CastKind {}", - @tagName(kind), + .{@tagName(kind)}, ), } } +fn transBoolExpr( + rp: RestorePoint, + scope: *Scope, + expr: *const ZigClangExpr, + used: ResultUsed, + lrvalue: LRValue, + grouped: bool, +) TransError!*ast.Node { + const lparen = if (grouped) + try appendToken(rp.c, .LParen, "(") + else + undefined; + var res = try transExpr(rp, scope, expr, used, lrvalue); + + if (isBoolRes(res)) { + if (!grouped and res.id == .GroupedExpression) { + const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res); + res = group.expr; + // get zig fmt to work properly + tokenSlice(rp.c, group.lparen)[0] = ')'; + } + return res; + } + + const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, expr)); + const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used); + + if (grouped) { + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = node, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); + } else { + return maybeSuppressResult(rp, scope, used, node); + } +} + +fn exprIsBooleanType(expr: *const ZigClangExpr) bool { + return qualTypeIsBoolean(ZigClangExpr_getType(expr)); +} + +fn exprIsStringLiteral(expr: *const ZigClangExpr) bool { + switch (ZigClangExpr_getStmtClass(expr)) { + .StringLiteralClass => return true, + .PredefinedExprClass => return true, + .UnaryOperatorClass => { + const op_expr = ZigClangUnaryOperator_getSubExpr(@ptrCast(*const ZigClangUnaryOperator, expr)); + return exprIsStringLiteral(op_expr); + }, + else => return false, + } +} + +fn isBoolRes(res: *ast.Node) bool { + switch (res.id) { + .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) { + .BoolOr, + .BoolAnd, + .EqualEqual, + .BangEqual, + .LessThan, + .GreaterThan, + .LessOrEqual, + .GreaterOrEqual, + => return true, + + else => {}, + }, + .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) { + .BoolNot => return true, + + else => {}, + }, + .BoolLiteral => return true, + .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), + else => {}, + } + return false; +} + +fn finishBoolExpr( + rp: RestorePoint, + scope: *Scope, + loc: ZigClangSourceLocation, + ty: *const ZigClangType, + node: *ast.Node, + used: ResultUsed, +) TransError!*ast.Node { + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Bool => return node, + .Char_U, + .UChar, + .Char_S, + .SChar, + .UShort, + .UInt, + .ULong, + .ULongLong, + .Short, + .Int, + .Long, + .LongLong, + .UInt128, + .Int128, + .Float, + .Double, + .Float128, + .LongDouble, + .WChar_U, + .Char8, + .Char16, + .Char32, + .WChar_S, + .Float16, + => { + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + }, + .NullPtr => { + const op_token = try appendToken(rp.c, .EqualEqual, "=="); + const rhs_node = try transCreateNodeNullLiteral(rp.c); + return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false); + }, + else => {}, + } + }, + .Pointer => { + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeNullLiteral(rp.c); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used); + }, + .Enum => { + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + }, + .Elaborated => { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); + const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used); + }, + else => {}, + } + return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{}); +} + +const SuppressCast = enum { + with_as, + no_as, +}; fn transIntegerLiteral( rp: RestorePoint, scope: *Scope, expr: *const ZigClangIntegerLiteral, result_used: ResultUsed, -) !TransResult { + suppress_as: SuppressCast, +) TransError!*ast.Node { var eval_result: ZigClangExprEvalResult = undefined; if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) { const loc = ZigClangIntegerLiteral_getBeginLoc(expr); - return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal"); + return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); } - const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val)); - const res = TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, res); + + if (suppress_as == .no_as) { + const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val)); + return maybeSuppressResult(rp, scope, result_used, int_lit_node); + } + + // Integer literals in C have types, and this can matter for several reasons. + // For example, this is valid C: + // unsigned char y = 256; + // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted + // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code: + // var y = @bitCast(u8, @truncate(i8, @as(c_int, 256))); + // Ideally in translate-c we could flatten this out to simply: + // var y: u8 = 0; + // But the first step is to be correct, and the next step is to make the output more elegant. + + // @as(T, x) + const expr_base = @ptrCast(*const ZigClangExpr, expr); + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base)); + try as_node.params.push(ty_node); + _ = try appendToken(rp.c, .Comma, ","); + + const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val)); + try as_node.params.push(int_lit_node); + + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &as_node.base); } fn transReturnStmt( rp: RestorePoint, scope: *Scope, expr: *const ZigClangReturnStmt, -) !TransResult { +) TransError!*ast.Node { const node = try transCreateNodeReturnExpr(rp.c); if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| { - const ret_node = node.cast(ast.Node.ControlFlowExpression).?; - ret_node.rhs = (try transExpr(rp, scope, val_expr, .used, .r_value)).node; + node.rhs = try transExprCoercing(rp, scope, val_expr, .used, .r_value); } _ = try appendToken(rp.c, .Semicolon, ";"); - return TransResult{ - .node = node, - .child_scope = scope, - .node_scope = scope, - }; + return &node.base; } fn transStringLiteral( @@ -683,7 +1647,7 @@ fn transStringLiteral( scope: *Scope, stmt: *const ZigClangStringLiteral, result_used: ResultUsed, -) !TransResult { +) TransError!*ast.Node { const kind = ZigClangStringLiteral_getKind(stmt); switch (kind) { .Ascii, .UTF8 => { @@ -695,32 +1659,26 @@ fn transStringLiteral( len = 0; for (str) |c| len += escapeChar(c, &char_buf).len; - const buf = try rp.c.a().alloc(u8, len + "c\"\"".len); - buf[0] = 'c'; - buf[1] = '"'; - writeEscapedString(buf[2..], str); + const buf = try rp.c.a().alloc(u8, len + "\"\"".len); + buf[0] = '"'; + writeEscapedString(buf[1..], str); buf[buf.len - 1] = '"'; const token = try appendToken(rp.c, .StringLiteral, buf); const node = try rp.c.a().create(ast.Node.StringLiteral); - node.* = ast.Node.StringLiteral{ - .base = ast.Node{ .id = .StringLiteral }, + node.* = .{ .token = token, }; - const res = TransResult{ - .node = &node.base, - .child_scope = scope, - .node_scope = scope, - }; - return maybeSuppressResult(rp, scope, result_used, res); + return maybeSuppressResult(rp, scope, result_used, &node.base); }, .UTF16, .UTF32, .Wide => return revertAndWarn( rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO: support string literal kind {}", - kind, + .{kind}, ), + else => unreachable, } } @@ -736,25 +1694,29 @@ fn writeEscapedString(buf: []u8, s: []const u8) void { var i: usize = 0; for (s) |c| { const escaped = escapeChar(c, &char_buf); - std.mem.copy(u8, buf[i..], escaped); + mem.copy(u8, buf[i..], escaped); i += escaped.len; } } // Returns either a string literal or a slice of `buf`. fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { - // TODO: https://github.com/ziglang/zig/issues/2749 - const escaped = switch (c) { - // Printable ASCII except for ' " \ - ' ', '!', '#'...'&', '('...'[', ']'...'~' => ([_]u8{c})[0..], - '\'', '\"', '\\' => ([_]u8{ '\\', c })[0..], - '\n' => return "\\n"[0..], - '\r' => return "\\r"[0..], - '\t' => return "\\t"[0..], - else => return std.fmt.bufPrint(char_buf[0..], "\\x{x:2}", c) catch unreachable, + return switch (c) { + '\"' => "\\\""[0..], + '\'' => "\\'"[0..], + '\\' => "\\\\"[0..], + '\n' => "\\n"[0..], + '\r' => "\\r"[0..], + '\t' => "\\t"[0..], + else => { + // Handle the remaining escapes Zig doesn't support by turning them + // into their respective hex representation + if (std.ascii.isCntrl(c)) + return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable + else + return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; + }, }; - std.mem.copy(u8, char_buf, escaped); - return char_buf[0..escaped.len]; } fn transCCast( @@ -769,16 +1731,59 @@ fn transCCast( if (ZigClangQualType_eq(dst_type, src_type)) return expr; if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) return transCPtrCast(rp, loc, dst_type, src_type, expr); - if (cIsUnsignedInteger(dst_type) and qualTypeIsPtr(src_type)) { - const cast_node = try transCreateNodeFnCall(rp.c, try transQualType(rp, dst_type, loc)); + if (cIsInteger(dst_type) and cIsInteger(src_type)) { + // 1. Extend or truncate without changing signed-ness. + // 2. Bit-cast to correct signed-ness + + // @bitCast(dest_type, intermediate_value) + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast"); + try cast_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + + switch (cIntTypeCmp(dst_type, src_type)) { + .lt => { + // @truncate(SameSignSmallerInt, src_type) + const trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@truncate"); + const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type)); + try trunc_node.params.push(ty_node); + _ = try appendToken(rp.c, .Comma, ","); + try trunc_node.params.push(expr); + trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + try cast_node.params.push(&trunc_node.base); + }, + .gt => { + // @as(SameSignBiggerInt, src_type) + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type)); + try as_node.params.push(ty_node); + _ = try appendToken(rp.c, .Comma, ","); + try as_node.params.push(expr); + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + try cast_node.params.push(&as_node.base); + }, + .eq => { + try cast_node.params.push(expr); + }, + } + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &cast_node.base; + } + if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { + // @intCast(dest_type, @ptrToInt(val)) + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + try cast_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt"); try builtin_node.params.push(expr); builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - try cast_node.op.Call.params.push(&builtin_node.base); - cast_node.rtoken = try appendToken(rp.c, .RParen, ")"); + try cast_node.params.push(&builtin_node.base); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &cast_node.base; } - if (cIsUnsignedInteger(src_type) and qualTypeIsPtr(dst_type)) { + if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { + // @intToPtr(dest_type, val) const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); try builtin_node.params.push(try transQualType(rp, dst_type, loc)); _ = try appendToken(rp.c, .Comma, ","); @@ -786,12 +1791,83 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } - // TODO: maybe widen to increase size - // TODO: maybe bitcast to change sign - // TODO: maybe truncate to reduce size - const cast_node = try transCreateNodeFnCall(rp.c, try transQualType(rp, dst_type, loc)); - try cast_node.op.Call.params.push(expr); - cast_node.rtoken = try appendToken(rp.c, .RParen, ")"); + if (cIsFloating(src_type) and cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatCast"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (cIsFloating(src_type) and !cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatToInt"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (!cIsFloating(src_type) and cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToFloat"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (ZigClangType_isBooleanType(qualTypeCanon(src_type)) and + !ZigClangType_isBooleanType(qualTypeCanon(dst_type))) + { + // @boolToInt returns either a comptime_int or a u1 + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const inner_cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + try inner_cast_node.params.push(try transCreateNodeIdentifier(rp.c, "u1")); + _ = try appendToken(rp.c, .Comma, ","); + try inner_cast_node.params.push(&builtin_node.base); + inner_cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + try cast_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + + if (cIsSignedInteger(dst_type)) { + const bitcast_node = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast"); + try bitcast_node.params.push(try transCreateNodeIdentifier(rp.c, "i1")); + _ = try appendToken(rp.c, .Comma, ","); + try bitcast_node.params.push(&inner_cast_node.base); + bitcast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + try cast_node.params.push(&bitcast_node.base); + } else { + try cast_node.params.push(&inner_cast_node.base); + } + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + return &cast_node.base; + } + if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) == .Enum) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(src_type)) == .Enum and + ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) != .Enum) + { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + try cast_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(expr); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &cast_node.base; } @@ -801,25 +1877,1187 @@ fn transExpr( expr: *const ZigClangExpr, used: ResultUsed, lrvalue: LRValue, -) TransError!TransResult { +) TransError!*ast.Node { return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue); } -fn findBlockScope(inner: *Scope) *Scope.Block { - var scope = inner; - while (true) : (scope = scope.parent orelse unreachable) { - if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); +/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore +/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. +fn transExprCoercing( + rp: RestorePoint, + scope: *Scope, + expr: *const ZigClangExpr, + used: ResultUsed, + lrvalue: LRValue, +) TransError!*ast.Node { + switch (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr))) { + .IntegerLiteralClass => { + return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, expr), .used, .no_as); + }, + .CharacterLiteralClass => { + return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, expr), .used, .no_as); + }, + .UnaryOperatorClass => { + const un_expr = @ptrCast(*const ZigClangUnaryOperator, expr); + if (ZigClangUnaryOperator_getOpcode(un_expr) == .Extension) { + return transExprCoercing(rp, scope, ZigClangUnaryOperator_getSubExpr(un_expr), used, lrvalue); + } + }, + else => {}, + } + return transExpr(rp, scope, expr, .used, .r_value); +} + +fn transInitListExprRecord( + rp: RestorePoint, + scope: *Scope, + loc: ZigClangSourceLocation, + expr: *const ZigClangInitListExpr, + ty: *const ZigClangType, + used: ResultUsed, +) TransError!*ast.Node { + var is_union_type = false; + // Unions and Structs are both represented as RecordDecl + const record_ty = ZigClangType_getAsRecordType(ty) orelse + blk: { + is_union_type = true; + break :blk ZigClangType_getAsUnionType(ty); + } orelse unreachable; + const record_decl = ZigClangRecordType_getDecl(record_ty); + const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse + unreachable; + + const ty_node = try transType(rp, ty, loc); + const init_count = ZigClangInitListExpr_getNumInits(expr); + var init_node = try transCreateNodeStructInitializer(rp.c, ty_node); + + var init_i: c_uint = 0; + var it = ZigClangRecordDecl_field_begin(record_def); + const end_it = ZigClangRecordDecl_field_end(record_def); + while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) { + const field_decl = ZigClangRecordDecl_field_iterator_deref(it); + + // The initializer for a union type has a single entry only + if (is_union_type and field_decl != ZigClangInitListExpr_getInitializedFieldInUnion(expr)) { + continue; + } + + assert(init_i < init_count); + const elem_expr = ZigClangInitListExpr_getInit(expr, init_i); + init_i += 1; + + // Generate the field assignment expression: + // .field_name = expr + const period_tok = try appendToken(rp.c, .Period, "."); + + var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); + if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { + const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; + raw_name = try mem.dupe(rp.c.a(), u8, name.value); + } + const field_name_tok = try appendIdentifier(rp.c, raw_name); + + _ = try appendToken(rp.c, .Equal, "="); + + const field_init_node = try rp.c.a().create(ast.Node.FieldInitializer); + field_init_node.* = .{ + .period_token = period_tok, + .name_token = field_name_tok, + .expr = try transExpr(rp, scope, elem_expr, .used, .r_value), + }; + + try init_node.op.StructInitializer.push(&field_init_node.base); + _ = try appendToken(rp.c, .Comma, ","); + } + + init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); + + return &init_node.base; +} + +fn transInitListExprArray( + rp: RestorePoint, + scope: *Scope, + loc: ZigClangSourceLocation, + expr: *const ZigClangInitListExpr, + ty: *const ZigClangType, + used: ResultUsed, +) TransError!*ast.Node { + const arr_type = ZigClangType_getAsArrayTypeUnsafe(ty); + const child_qt = ZigClangArrayType_getElementType(arr_type); + const init_count = ZigClangInitListExpr_getNumInits(expr); + const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, ty); + const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty); + const all_count = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize)); + const leftover_count = all_count - init_count; + + var init_node: *ast.Node.SuffixOp = undefined; + var cat_tok: ast.TokenIndex = undefined; + if (init_count != 0) { + const dot_tok = try appendToken(rp.c, .Period, "."); + init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok); + var i: c_uint = 0; + while (i < init_count) : (i += 1) { + const elem_expr = ZigClangInitListExpr_getInit(expr, i); + try init_node.op.ArrayInitializer.push(try transExpr(rp, scope, elem_expr, .used, .r_value)); + _ = try appendToken(rp.c, .Comma, ","); + } + init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); + if (leftover_count == 0) { + return &init_node.base; + } + cat_tok = try appendToken(rp.c, .PlusPlus, "++"); + } + + const dot_tok = try appendToken(rp.c, .Period, "."); + var filler_init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok); + const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr); + try filler_init_node.op.ArrayInitializer.push(try transExpr(rp, scope, filler_val_expr, .used, .r_value)); + filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); + + const rhs_node = if (leftover_count == 1) + &filler_init_node.base + else blk: { + const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**"); + const mul_node = try rp.c.a().create(ast.Node.InfixOp); + mul_node.* = .{ + .op_token = mul_tok, + .lhs = &filler_init_node.base, + .op = .ArrayMult, + .rhs = try transCreateNodeInt(rp.c, leftover_count), + }; + break :blk &mul_node.base; + }; + + if (init_count == 0) { + return rhs_node; + } + + const cat_node = try rp.c.a().create(ast.Node.InfixOp); + cat_node.* = .{ + .op_token = cat_tok, + .lhs = &init_node.base, + .op = .ArrayCat, + .rhs = rhs_node, + }; + return &cat_node.base; +} + +fn transInitListExpr( + rp: RestorePoint, + scope: *Scope, + expr: *const ZigClangInitListExpr, + used: ResultUsed, +) TransError!*ast.Node { + const qt = getExprQualType(rp.c, @ptrCast(*const ZigClangExpr, expr)); + var qual_type = ZigClangQualType_getTypePtr(qt); + const source_loc = ZigClangExpr_getBeginLoc(@ptrCast(*const ZigClangExpr, expr)); + + if (ZigClangType_isRecordType(qual_type)) { + return transInitListExprRecord( + rp, + scope, + source_loc, + expr, + qual_type, + used, + ); + } else if (ZigClangType_isArrayType(qual_type)) { + return transInitListExprArray( + rp, + scope, + source_loc, + expr, + qual_type, + used, + ); + } else { + const type_name = rp.c.str(ZigClangType_getTypeClassName(qual_type)); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{}'", .{type_name}); } } -fn transLookupZigIdentifier(inner: *Scope, c_name: []const u8) []const u8 { - var scope = inner; - while (true) : (scope = scope.parent orelse return c_name) { - if (scope.id == .Var) { - const var_scope = @ptrCast(*const Scope.Var, scope); - if (std.mem.eql(u8, var_scope.c_name, c_name)) return var_scope.zig_name; +fn transZeroInitExpr( + rp: RestorePoint, + scope: *Scope, + source_loc: ZigClangSourceLocation, + ty: *const ZigClangType, +) TransError!*ast.Node { + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => blk: { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Bool => return try transCreateNodeBoolLiteral(rp.c, false), + .Char_U, + .UChar, + .Char_S, + .Char8, + .SChar, + .UShort, + .UInt, + .ULong, + .ULongLong, + .Short, + .Int, + .Long, + .LongLong, + .UInt128, + .Int128, + .Float, + .Double, + .Float128, + .Float16, + .LongDouble, + => return transCreateNodeInt(rp.c, 0), + else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), + } + }, + .Pointer => return transCreateNodeNullLiteral(rp.c), + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + return transZeroInitExpr( + rp, + scope, + source_loc, + ZigClangQualType_getTypePtr( + ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl), + ), + ); + }, + else => {}, + } + + return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); +} + +fn transImplicitValueInitExpr( + rp: RestorePoint, + scope: *Scope, + expr: *const ZigClangExpr, + used: ResultUsed, +) TransError!*ast.Node { + const source_loc = ZigClangExpr_getBeginLoc(expr); + const qt = getExprQualType(rp.c, expr); + const ty = ZigClangQualType_getTypePtr(qt); + return transZeroInitExpr(rp, scope, source_loc, ty); +} + +fn transIfStmt( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangIfStmt, +) TransError!*ast.Node { + // if (c) t + // if (c) t else e + const if_node = try transCreateNodeIf(rp.c); + + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + if_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value); + + if (ZigClangIfStmt_getElse(stmt)) |expr| { + if_node.@"else" = try transCreateNodeElse(rp.c); + if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value); + } + _ = try appendToken(rp.c, .Semicolon, ";"); + return &if_node.base; +} + +fn transWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangWhileStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; + while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value); + return &while_node.base; +} + +fn transDoWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangDoStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); + _ = try appendToken(rp.c, .RParen, ")"); + var new = false; + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; + + const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { + // there's already a block in C, so we'll append our condition to it. + // c: do { + // c: a; + // c: b; + // c: } while(c); + // zig: while (true) { + // zig: a; + // zig: b; + // zig: if (!cond) break; + // zig: } + const body = (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; + // if this is used as an expression in Zig it needs to be immediately followed by a semicolon + _ = try appendToken(rp.c, .Semicolon, ";"); + break :blk body; + } else blk: { + // the C statement is without a block, so we need to create a block to contain it. + // c: do + // c: a; + // c: while(c); + // zig: while (true) { + // zig: a; + // zig: if (!cond) break; + // zig: } + new = true; + const block = try transCreateNodeBlock(rp.c, null); + try block.statements.push(try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); + break :blk block; + }; + + // if (!cond) break; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); + _ = try appendToken(rp.c, .RParen, ")"); + if_node.condition = &prefix_op.base; + if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; + _ = try appendToken(rp.c, .Semicolon, ";"); + + try body_node.statements.push(&if_node.base); + if (new) + body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + while_node.body = &body_node.base; + return &while_node.base; +} + +fn transForLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangForStmt, +) TransError!*ast.Node { + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; + + var block_scope: ?*Scope.Block = null; + if (ZigClangForStmt_getInit(stmt)) |init| { + block_scope = try Scope.Block.init(rp.c, scope, null); + const block = try transCreateNodeBlock(rp.c, null); + block_scope.?.block_node = block; + loop_scope.parent = &block_scope.?.base; + const result = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value); + if (result != &block.base) + try block.statements.push(result); + } + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + + const while_node = try transCreateNodeWhile(rp.c); + while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond| + try transBoolExpr(rp, &cond_scope, cond, .used, .r_value, false) + else + try transCreateNodeBoolLiteral(rp.c, true); + _ = try appendToken(rp.c, .RParen, ")"); + + if (ZigClangForStmt_getInc(stmt)) |incr| { + _ = try appendToken(rp.c, .Colon, ":"); + _ = try appendToken(rp.c, .LParen, "("); + while_node.continue_expr = try transExpr(rp, &cond_scope, incr, .unused, .r_value); + _ = try appendToken(rp.c, .RParen, ")"); + } + + while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value); + if (block_scope != null) { + try block_scope.?.block_node.statements.push(&while_node.base); + block_scope.?.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block_scope.?.block_node.base; + } else + return &while_node.base; +} + +fn transSwitch( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangSwitchStmt, +) TransError!*ast.Node { + const switch_node = try transCreateNodeSwitch(rp.c); + var switch_scope = Scope.Switch{ + .base = .{ + .id = .Switch, + .parent = scope, + }, + .cases = &switch_node.cases, + .pending_block = undefined, + }; + + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + switch_node.expr = try transExpr(rp, &cond_scope, ZigClangSwitchStmt_getCond(stmt), .used, .r_value); + _ = try appendToken(rp.c, .RParen, ")"); + _ = try appendToken(rp.c, .LBrace, "{"); + switch_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + + const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null); + // tmp block that all statements will go before being picked up by a case or default + const block = try transCreateNodeBlock(rp.c, null); + block_scope.block_node = block; + + const switch_block = try transCreateNodeBlock(rp.c, null); + try switch_block.statements.push(&switch_node.base); + switch_scope.pending_block = switch_block; + + const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value); + _ = try appendToken(rp.c, .Semicolon, ";"); + + // take all pending statements + var it = last.cast(ast.Node.Block).?.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + + switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch"); + _ = try appendToken(rp.c, .Colon, ":"); + if (!switch_scope.has_default) { + const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); + else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_node.cases.push(&else_prong.base); + } + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &switch_scope.pending_block.base; +} + +fn transCase( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCaseStmt, +) TransError!*ast.Node { + const block_scope = scope.findBlockScope(rp.c) catch unreachable; + const switch_scope = scope.getSwitch(); + const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { + const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + const ellips = try appendToken(rp.c, .Ellipsis3, "..."); + const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + + const node = try rp.c.a().create(ast.Node.InfixOp); + node.* = .{ + .op_token = ellips, + .lhs = lhs_node, + .op = .Range, + .rhs = rhs_node, + }; + break :blk &node.base; + } else + try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + + const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); + switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_scope.cases.push(&switch_prong.base); + + const block = try transCreateNodeBlock(rp.c, null); + switch_scope.pending_block.label = try appendIdentifier(rp.c, label); + _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + try block.statements.push(&switch_scope.pending_block.base); + + // take all pending statements + var it = block_scope.block_node.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + block_scope.block_node.statements.shrink(0); + + switch_scope.pending_block = block; + + return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value); +} + +fn transDefault( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangDefaultStmt, +) TransError!*ast.Node { + const block_scope = scope.findBlockScope(rp.c) catch unreachable; + const switch_scope = scope.getSwitch(); + const label = "__default"; + switch_scope.has_default = true; + _ = try appendToken(rp.c, .Semicolon, ";"); + + const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); + else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_scope.cases.push(&else_prong.base); + + const block = try transCreateNodeBlock(rp.c, null); + switch_scope.pending_block.label = try appendIdentifier(rp.c, label); + _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + try block.statements.push(&switch_scope.pending_block.base); + + // take all pending statements + var it = block_scope.block_node.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + block_scope.block_node.statements.shrink(0); + + switch_scope.pending_block = block; + return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value); +} + +fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node { + var result: ZigClangExprEvalResult = undefined; + if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context)) + return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{}); + + var val_node: ?*ast.Node = null; + switch (ZigClangAPValue_getKind(&result.Val)) { + .Int => { + // See comment in `transIntegerLiteral` for why this code is here. + // @as(T, x) + const expr_base = @ptrCast(*const ZigClangExpr, expr); + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base)); + try as_node.params.push(ty_node); + _ = try appendToken(rp.c, .Comma, ","); + + const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val)); + try as_node.params.push(int_lit_node); + + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + return maybeSuppressResult(rp, scope, used, &as_node.base); + }, + else => { + return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "unsupported constant expression kind", .{}); + }, + } +} + +fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node { + return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used); +} + +fn transCharLiteral( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCharacterLiteral, + result_used: ResultUsed, + suppress_as: SuppressCast, +) TransError!*ast.Node { + const kind = ZigClangCharacterLiteral_getKind(stmt); + const int_lit_node = switch (kind) { + .Ascii, .UTF8 => blk: { + const val = ZigClangCharacterLiteral_getValue(stmt); + if (kind == .Ascii) { + // C has a somewhat obscure feature called multi-character character + // constant + if (val > 255) + break :blk try transCreateNodeInt(rp.c, val); + } + var char_buf: [4]u8 = undefined; + const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)}); + const node = try rp.c.a().create(ast.Node.CharLiteral); + node.* = .{ + .token = token, + }; + break :blk &node.base; + }, + .UTF16, .UTF32, .Wide => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), + "TODO: support character literal kind {}", + .{kind}, + ), + else => unreachable, + }; + if (suppress_as == .no_as) { + return maybeSuppressResult(rp, scope, result_used, int_lit_node); + } + // See comment in `transIntegerLiteral` for why this code is here. + // @as(T, x) + const expr_base = @ptrCast(*const ZigClangExpr, stmt); + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base)); + try as_node.params.push(ty_node); + _ = try appendToken(rp.c, .Comma, ","); + + try as_node.params.push(int_lit_node); + + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &as_node.base); +} + +fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node { + const comp = ZigClangStmtExpr_getSubStmt(stmt); + if (used == .unused) { + return transCompoundStmt(rp, scope, comp); + } + const lparen = try appendToken(rp.c, .LParen, "("); + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + const block = try transCreateNodeBlock(rp.c, "blk"); + block_scope.block_node = block; + + var it = ZigClangCompoundStmt_body_begin(comp); + const end_it = ZigClangCompoundStmt_body_end(comp); + while (it != end_it - 1) : (it += 1) { + const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); + if (result != &block.base) + try block.statements.push(result); + } + const break_node = try transCreateNodeBreak(rp.c, "blk"); + break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); + _ = try appendToken(rp.c, .Semicolon, ";"); + try block.statements.push(&break_node.base); + block.rbrace = try appendToken(rp.c, .RBrace, "}"); + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &block.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); +} + +fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node { + var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value); + + if (ZigClangMemberExpr_isArrow(stmt)) { + container_node = try transCreateNodePtrDeref(rp.c, container_node); + } + + const member_decl = ZigClangMemberExpr_getMemberDecl(stmt); + const name = blk: { + const decl_kind = ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, member_decl)); + // If we're referring to a anonymous struct/enum find the bogus name + // we've assigned to it during the RecordDecl translation + if (decl_kind == .Field) { + const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl); + if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { + const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; + break :blk try mem.dupe(rp.c.a(), u8, name.value); + } + } + const decl = @ptrCast(*const ZigClangNamedDecl, member_decl); + break :blk try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(decl)); + }; + + const node = try transCreateNodeFieldAccess(rp.c, container_node, name); + return maybeSuppressResult(rp, scope, result_used, node); +} + +fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node { + var base_stmt = ZigClangArraySubscriptExpr_getBase(stmt); + + // Unwrap the base statement if it's an array decayed to a bare pointer type + // so that we index the array itself + if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, base_stmt)) == .ImplicitCastExprClass) { + const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, base_stmt); + + if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .ArrayToPointerDecay) { + base_stmt = ZigClangImplicitCastExpr_getSubExpr(implicit_cast); } } + + const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value); + const node = try transCreateNodeArrayAccess(rp.c, container_node); + + // cast if the index is long long or signed + const subscr_expr = ZigClangArraySubscriptExpr_getIdx(stmt); + const qt = getExprQualType(rp.c, subscr_expr); + const is_longlong = cIsLongLongInteger(qt); + const is_signed = cIsSignedInteger(qt); + + if (is_longlong or is_signed) { + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + // check if long long first so that signed long long doesn't just become unsigned long long + var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false); + try cast_node.params.push(typeid_node); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(try transExpr(rp, scope, subscr_expr, .used, .r_value)); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + node.rtoken = try appendToken(rp.c, .RBrace, "]"); + node.op.ArrayAccess = &cast_node.base; + } else { + node.op.ArrayAccess = try transExpr(rp, scope, subscr_expr, .used, .r_value); + node.rtoken = try appendToken(rp.c, .RBrace, "]"); + } + return maybeSuppressResult(rp, scope, result_used, &node.base); +} + +fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node { + const callee = ZigClangCallExpr_getCallee(stmt); + var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value); + + var is_ptr = false; + const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr); + + const fn_expr = if (is_ptr and fn_ty != null) blk: { + if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) { + const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee); + + if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) { + const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast); + if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) { + const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr); + const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref); + if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) { + break :blk raw_fn_expr; + } + } + } + } + break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr); + } else + raw_fn_expr; + const node = try transCreateNodeFnCall(rp.c, fn_expr); + + const num_args = ZigClangCallExpr_getNumArgs(stmt); + const args = ZigClangCallExpr_getArgs(stmt); + var i: usize = 0; + while (i < num_args) : (i += 1) { + if (i != 0) { + _ = try appendToken(rp.c, .Comma, ","); + } + const arg = try transExpr(rp, scope, args[i], .used, .r_value); + try node.op.Call.params.push(arg); + } + node.rtoken = try appendToken(rp.c, .RParen, ")"); + + if (fn_ty) |ty| { + const canon = ZigClangQualType_getCanonicalType(ty.getReturnType()); + const ret_ty = ZigClangQualType_getTypePtr(canon); + if (ZigClangType_isVoidType(ret_ty)) { + _ = try appendToken(rp.c, .Semicolon, ";"); + return &node.base; + } + } + + return maybeSuppressResult(rp, scope, result_used, &node.base); +} + +const ClangFunctionType = union(enum) { + Proto: *const ZigClangFunctionProtoType, + NoProto: *const ZigClangFunctionType, + + fn getReturnType(self: @This()) ZigClangQualType { + switch (@as(@TagType(@This()), self)) { + .Proto => return ZigClangFunctionProtoType_getReturnType(self.Proto), + .NoProto => return ZigClangFunctionType_getReturnType(self.NoProto), + } + } +}; + +fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?ClangFunctionType { + const canon = ZigClangQualType_getCanonicalType(qt); + var ty = ZigClangQualType_getTypePtr(canon); + is_ptr.* = false; + + if (ZigClangType_getTypeClass(ty) == .Pointer) { + is_ptr.* = true; + const child_qt = ZigClangType_getPointeeType(ty); + ty = ZigClangQualType_getTypePtr(child_qt); + } + if (ZigClangType_getTypeClass(ty) == .FunctionProto) { + return ClangFunctionType{ .Proto = @ptrCast(*const ZigClangFunctionProtoType, ty) }; + } + if (ZigClangType_getTypeClass(ty) == .FunctionNoProto) { + return ClangFunctionType{ .NoProto = @ptrCast(*const ZigClangFunctionType, ty) }; + } + return null; +} + +fn transUnaryExprOrTypeTraitExpr( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangUnaryExprOrTypeTraitExpr, + result_used: ResultUsed, +) TransError!*ast.Node { + const type_node = try transQualType( + rp, + ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt), + ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt), + ); + + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@sizeOf"); + try builtin_node.params.push(type_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &builtin_node.base); +} + +fn qualTypeHasWrappingOverflow(qt: ZigClangQualType) bool { + if (cIsUnsignedInteger(qt)) { + // unsigned integer overflow wraps around. + return true; + } else { + // float, signed integer, and pointer overflow is undefined behavior. + return false; + } +} + +fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node { + const op_expr = ZigClangUnaryOperator_getSubExpr(stmt); + switch (ZigClangUnaryOperator_getOpcode(stmt)) { + .PostInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt))) + return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + else + return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + .PostDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt))) + return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + else + return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + .PreInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt))) + return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + else + return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + .PreDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt))) + return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + else + return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + .AddrOf => { + const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); + return &op_node.base; + }, + .Deref => { + const value_node = try transExpr(rp, scope, op_expr, used, .r_value); + var is_ptr = false; + const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr); + if (fn_ty != null and is_ptr) + return value_node; + const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node); + return transCreateNodePtrDeref(rp.c, unwrapped); + }, + .Plus => return transExpr(rp, scope, op_expr, used, .r_value), + .Minus => { + if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) { + const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-"); + op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + return &op_node.base; + } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) { + // we gotta emit 0 -% x + const zero = try transCreateNodeInt(rp.c, 0); + const token = try appendToken(rp.c, .MinusPercent, "-%"); + const expr = try transExpr(rp, scope, op_expr, .used, .r_value); + return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true); + } else + return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{}); + }, + .Not => { + const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); + op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + return &op_node.base; + }, + .LNot => { + const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); + return &op_node.base; + }, + .Extension => { + return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value); + }, + else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}), + } +} + +fn transCreatePreCrement( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangUnaryOperator, + op: ast.Node.InfixOp.Op, + op_tok_id: std.zig.Token.Id, + bytes: []const u8, + used: ResultUsed, +) TransError!*ast.Node { + const op_expr = ZigClangUnaryOperator_getSubExpr(stmt); + + if (used == .unused) { + // common case + // c: ++expr + // zig: expr += 1 + const expr = try transExpr(rp, scope, op_expr, .used, .r_value); + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); + return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); + } + // worst case + // c: ++expr + // zig: (blk: { + // zig: const _ref = &expr; + // zig: _ref.* += 1; + // zig: break :blk _ref.* + // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try block_scope.makeMangledName(rp.c, "ref"); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + node.init_node = &rhs_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + _ = try appendToken(rp.c, .Semicolon, ";"); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = ref_node; + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; +} + +fn transCreatePostCrement( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangUnaryOperator, + op: ast.Node.InfixOp.Op, + op_tok_id: std.zig.Token.Id, + bytes: []const u8, + used: ResultUsed, +) TransError!*ast.Node { + const op_expr = ZigClangUnaryOperator_getSubExpr(stmt); + + if (used == .unused) { + // common case + // c: ++expr + // zig: expr += 1 + const expr = try transExpr(rp, scope, op_expr, .used, .r_value); + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); + return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); + } + // worst case + // c: expr++ + // zig: (blk: { + // zig: const _ref = &expr; + // zig: const _tmp = _ref.*; + // zig: _ref.* += 1; + // zig: break :blk _tmp + // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try block_scope.makeMangledName(rp.c, "ref"); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + node.init_node = &rhs_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const tmp = try block_scope.makeMangledName(rp.c, "tmp"); + const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp); + tmp_node.eq_token = try appendToken(rp.c, .Equal, "="); + tmp_node.init_node = ref_node; + tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&tmp_node.base); + + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + _ = try appendToken(rp.c, .Semicolon, ";"); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); + try block_scope.block_node.statements.push(&break_node.base); + _ = try appendToken(rp.c, .Semicolon, ";"); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; +} + +fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { + switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) { + .MulAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used), + .AddAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used), + .SubAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used), + .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used), + .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used), + .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used), + .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used), + .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used), + else => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangCompoundAssignOperator_getBeginLoc(stmt), + "unsupported C translation {}", + .{ZigClangCompoundAssignOperator_getOpcode(stmt)}, + ), + } +} + +fn transCreateCompoundAssign( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCompoundAssignOperator, + assign_op: ast.Node.InfixOp.Op, + assign_tok_id: std.zig.Token.Id, + assign_bytes: []const u8, + bin_op: ast.Node.InfixOp.Op, + bin_tok_id: std.zig.Token.Id, + bin_bytes: []const u8, + used: ResultUsed, +) TransError!*ast.Node { + const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight; + const lhs = ZigClangCompoundAssignOperator_getLHS(stmt); + const rhs = ZigClangCompoundAssignOperator_getRHS(stmt); + const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt); + if (used == .unused) { + // common case + // c: lhs += rhs + // zig: lhs += rhs + const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); + const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes); + var rhs_node = if (is_shift) + try transExprCoercing(rp, scope, rhs, .used, .r_value) + else + try transExpr(rp, scope, rhs, .used, .r_value); + + if (is_shift) { + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); + try cast_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(rhs_node); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &cast_node.base; + } + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); + return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false); + } + // worst case + // c: lhs += rhs + // zig: (blk: { + // zig: const _ref = &lhs; + // zig: _ref.* = _ref.* + rhs; + // zig: break :blk _ref.* + // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try block_scope.makeMangledName(rp.c, "ref"); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); + node.init_node = &addr_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (is_shift) { + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); + try cast_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(rhs_node); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &cast_node.base; + } + const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); + + _ = try appendToken(rp.c, .Semicolon, ";"); + + const eq_token = try appendToken(rp.c, .Equal, "="); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = ref_node; + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; } fn transCPtrCast( @@ -831,84 +3069,368 @@ fn transCPtrCast( ) !*ast.Node { const ty = ZigClangQualType_getTypePtr(dst_type); const child_type = ZigClangType_getPointeeType(ty); + const src_ty = ZigClangQualType_getTypePtr(src_type); + const src_child_type = ZigClangType_getPointeeType(src_ty); - // Implicit downcasting from higher to lower alignment values is forbidden, - // use @alignCast to side-step this problem - const ptrcast_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast"); - const dst_type_node = try transType(rp, ty, loc); - try ptrcast_node.params.push(dst_type_node); - _ = try appendToken(rp.c, .Comma, ","); - - if (ZigClangType_isVoidType(qualTypeCanon(child_type))) { - // void has 1-byte alignment, so @alignCast is not needed - try ptrcast_node.params.push(expr); - } else { - const aligncast_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignCast"); - const alignof_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignOf"); - const child_type_node = try transQualType(rp, child_type, loc); - try alignof_node.params.push(child_type_node); - alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - try aligncast_node.params.push(&alignof_node.base); + if ((ZigClangQualType_isConstQualified(src_child_type) and + !ZigClangQualType_isConstQualified(child_type)) or + (ZigClangQualType_isVolatileQualified(src_child_type) and + !ZigClangQualType_isVolatileQualified(child_type))) + { + // Casting away const or volatile requires us to use @intToPtr + const inttoptr_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); + const dst_type_node = try transType(rp, ty, loc); + try inttoptr_node.params.push(dst_type_node); _ = try appendToken(rp.c, .Comma, ","); - try aligncast_node.params.push(expr); - aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - try ptrcast_node.params.push(&aligncast_node.base); - } - ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &ptrcast_node.base; + const ptrtoint_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt"); + try ptrtoint_node.params.push(expr); + ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + try inttoptr_node.params.push(&ptrtoint_node.base); + inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &inttoptr_node.base; + } else { + // Implicit downcasting from higher to lower alignment values is forbidden, + // use @alignCast to side-step this problem + const ptrcast_node = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast"); + const dst_type_node = try transType(rp, ty, loc); + try ptrcast_node.params.push(dst_type_node); + _ = try appendToken(rp.c, .Comma, ","); + + if (ZigClangType_isVoidType(qualTypeCanon(child_type))) { + // void has 1-byte alignment, so @alignCast is not needed + try ptrcast_node.params.push(expr); + } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) { + // For opaque types a ptrCast is enough + try ptrcast_node.params.push(expr); + } else { + const aligncast_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignCast"); + const alignof_node = try transCreateNodeBuiltinFnCall(rp.c, "@alignOf"); + const child_type_node = try transQualType(rp, child_type, loc); + try alignof_node.params.push(child_type_node); + alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + try aligncast_node.params.push(&alignof_node.base); + _ = try appendToken(rp.c, .Comma, ","); + try aligncast_node.params.push(expr); + aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + try ptrcast_node.params.push(&aligncast_node.base); + } + ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + return &ptrcast_node.base; + } +} + +fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { + const break_scope = scope.getBreakableScope(); + const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) + "__switch" + else + null); + _ = try appendToken(rp.c, .Semicolon, ";"); + return &br.base; +} + +fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node { + // TODO use something more accurate + const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt); + const node = try rp.c.a().create(ast.Node.FloatLiteral); + node.* = .{ + .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}), + }; + return maybeSuppressResult(rp, scope, used, &node.base); +} + +fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangBinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node { + // GNU extension of the ternary operator where the middle expression is + // omitted, the conditition itself is returned if it evaluates to true + const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt); + const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt); + const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt); + const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt); + + // c: (cond_expr)?:(false_expr) + // zig: (blk: { + // const _cond_temp = (cond_expr); + // break :blk if (_cond_temp) _cond_temp else (false_expr); + // }) + const lparen = try appendToken(rp.c, .LParen, "("); + + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + + const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp"); + const tmp_var = try transCreateNodeVarDecl(rp.c, false, true, mangled_name); + tmp_var.eq_token = try appendToken(rp.c, .Equal, "="); + tmp_var.init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value); + tmp_var.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&tmp_var.base); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope{ + .parent = &block_scope.base, + .id = .Condition, + }; + const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name); + + const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, cond_expr)); + const cond_node = try finishBoolExpr(rp, &block_scope.base, ZigClangExpr_getBeginLoc(cond_expr), ty, tmp_var_node, used); + if_node.condition = cond_node; + _ = try appendToken(rp.c, .RParen, ")"); + + if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name); + if_node.@"else" = try transCreateNodeElse(rp.c); + if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value); + _ = try appendToken(rp.c, .Semicolon, ";"); + + break_node.rhs = &if_node.base; + _ = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); +} + +fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node { + const grouped = scope.id == .Condition; + const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, + }; + + const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt); + const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt); + const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt); + const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt); + + if_node.condition = try transBoolExpr(rp, &cond_scope, cond_expr, .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value); + + if_node.@"else" = try transCreateNodeElse(rp.c); + if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value); + + if (grouped) { + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &if_node.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); + } else { + return maybeSuppressResult(rp, scope, used, &if_node.base); + } } fn maybeSuppressResult( rp: RestorePoint, scope: *Scope, used: ResultUsed, - result: TransResult, -) !TransResult { + result: *ast.Node, +) TransError!*ast.Node { if (used == .used) return result; - // NOTE: This is backwards, but the semicolon must immediately follow the node. - _ = try appendToken(rp.c, .Semicolon, ";"); - const lhs = try appendIdentifier(rp.c, "_"); + if (scope.id != .Condition) { + // NOTE: This is backwards, but the semicolon must immediately follow the node. + _ = try appendToken(rp.c, .Semicolon, ";"); + } else { // TODO is there a way to avoid this hack? + // this parenthesis must come immediately following the node + _ = try appendToken(rp.c, .RParen, ")"); + // these need to come before _ + _ = try appendToken(rp.c, .Colon, ":"); + _ = try appendToken(rp.c, .LParen, "("); + } + const lhs = try transCreateNodeIdentifier(rp.c, "_"); const op_token = try appendToken(rp.c, .Equal, "="); const op_node = try rp.c.a().create(ast.Node.InfixOp); - op_node.* = ast.Node.InfixOp{ - .base = ast.Node{ .id = .InfixOp }, + op_node.* = .{ .op_token = op_token, .lhs = lhs, .op = .Assign, - .rhs = result.node, - }; - return TransResult{ - .node = &op_node.base, - .child_scope = scope, - .node_scope = scope, + .rhs = result, }; + return &op_node.base; } fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { try c.tree.root_node.decls.push(decl_node); + _ = try c.global_scope.sym_table.put(name, decl_node); } fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node { return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc); } +/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. +/// Asserts the type is an integer. +fn transQualTypeIntWidthOf(c: *Context, ty: ZigClangQualType, is_signed: bool) TypeError!*ast.Node { + return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed); +} + +/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness. +/// Asserts the type is an integer. +fn transTypeIntWidthOf(c: *Context, ty: *const ZigClangType, is_signed: bool) TypeError!*ast.Node { + assert(ZigClangType_getTypeClass(ty) == .Builtin); + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + return transCreateNodeIdentifier(c, switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", + .UShort, .Short => if (is_signed) "c_short" else "c_ushort", + .UInt, .Int => if (is_signed) "c_int" else "c_uint", + .ULong, .Long => if (is_signed) "c_long" else "c_ulong", + .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong", + .UInt128, .Int128 => if (is_signed) "i128" else "u128", + .Char16 => if (is_signed) "i16" else "u16", + .Char32 => if (is_signed) "i32" else "u32", + else => unreachable, // only call this function when it has already been determined the type is int + }); +} + +fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool { + const c_type = qualTypeCanon(qt); + if (ZigClangType_getTypeClass(c_type) != .Builtin) + return false; + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type); + return ZigClangBuiltinType_getKind(builtin_ty) == kind; +} + fn qualTypeIsPtr(qt: ZigClangQualType) bool { return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer; } +fn qualTypeIsBoolean(qt: ZigClangQualType) bool { + return ZigClangType_isBooleanType(qualTypeCanon(qt)); +} + +fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 { + const ty = ZigClangQualType_getTypePtr(qt); + + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Char_U, + .UChar, + .Char_S, + .SChar, + => return 8, + .UInt128, + .Int128, + => return 128, + else => return 0, + } + + unreachable; + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + const type_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl))); + + if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) { + return 8; + } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) { + return 16; + } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) { + return 32; + } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) { + return 64; + } else { + return 0; + } + }, + else => return 0, + } + + unreachable; +} + +fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node { + const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc); + + if (int_bit_width != 0) { + // we can perform the log2 now. + const cast_bit_width = math.log2_int(u64, int_bit_width); + const node = try rp.c.a().create(ast.Node.IntegerLiteral); + node.* = ast.Node.IntegerLiteral{ + .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), + }; + return &node.base; + } + + const zig_type_node = try transQualType(rp, qt, source_loc); + + // @import("std").math.Log2Int(c_long); + // + // FnCall + // FieldAccess + // FieldAccess + // FnCall (.builtin = true) + // Symbol "import" + // StringLiteral "std" + // Symbol "math" + // Symbol "Log2Int" + // Symbol (var from above) + + const import_fn_call = try transCreateNodeBuiltinFnCall(rp.c, "@import"); + const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); + const std_node = try rp.c.a().create(ast.Node.StringLiteral); + std_node.* = ast.Node.StringLiteral{ + .token = std_token, + }; + try import_fn_call.params.push(&std_node.base); + import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); + const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int"); + const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access); + try @fieldParentPtr(ast.Node.SuffixOp, "base", &log2int_fn_call.base).op.Call.params.push(zig_type_node); + log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); + + return &log2int_fn_call.base; +} + fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool { const ty = ZigClangQualType_getTypePtr(qt); - if (ZigClangType_getTypeClass(ty) == .Paren) { - const paren_type = @ptrCast(*const ZigClangParenType, ty); - const inner_type = ZigClangParenType_getInnerType(paren_type); - return ZigClangQualType_getTypeClass(inner_type) == .FunctionProto; + + switch (ZigClangType_getTypeClass(ty)) { + .FunctionProto, .FunctionNoProto => return true, + .Elaborated => { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); + return qualTypeChildIsFnProto(ZigClangElaboratedType_getNamedType(elaborated_ty)); + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + return qualTypeChildIsFnProto(ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl)); + }, + .Paren => { + const paren_type = @ptrCast(*const ZigClangParenType, ty); + const inner_type = ZigClangParenType_getInnerType(paren_type); + switch (ZigClangQualType_getTypeClass(inner_type)) { + .FunctionProto, .FunctionNoProto => return true, + else => return false, + } + }, + .Attributed => { + const attr_type = @ptrCast(*const ZigClangAttributedType, ty); + return qualTypeChildIsFnProto(ZigClangAttributedType_getEquivalentType(attr_type)); + }, + else => return false, } - if (ZigClangType_getTypeClass(ty) == .Attributed) { - const attr_type = @ptrCast(*const ZigClangAttributedType, ty); - return qualTypeChildIsFnProto(ZigClangAttributedType_getEquivalentType(attr_type)); - } - return false; } fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType { @@ -942,7 +3464,18 @@ fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocatio .Record => { const record_ty = @ptrCast(*const ZigClangRecordType, ty); const record_decl = ZigClangRecordType_getDecl(record_ty); - return (ZigClangRecordDecl_getDefinition(record_decl) == null); + const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse + return true; + var it = ZigClangRecordDecl_field_begin(record_def); + const end_it = ZigClangRecordDecl_field_end(record_def); + while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) { + const field_decl = ZigClangRecordDecl_field_iterator_deref(it); + + if (ZigClangFieldDecl_isBitField(field_decl)) { + return true; + } + } + return false; }, .Elaborated => { const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); @@ -959,6 +3492,10 @@ fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocatio } } +fn cIsInteger(qt: ZigClangQualType) bool { + return cIsSignedInteger(qt) or cIsUnsignedInteger(qt); +} + fn cIsUnsignedInteger(qt: ZigClangQualType) bool { const c_type = qualTypeCanon(qt); if (ZigClangType_getTypeClass(c_type) != .Builtin) return false; @@ -978,49 +3515,140 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool { }; } +fn cIntTypeToIndex(qt: ZigClangQualType) u8 { + const c_type = qualTypeCanon(qt); + assert(ZigClangType_getTypeClass(c_type) == .Builtin); + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type); + return switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1, + .WChar_U, .WChar_S => 2, + .UShort, .Short, .Char16 => 3, + .UInt, .Int, .Char32 => 4, + .ULong, .Long => 5, + .ULongLong, .LongLong => 6, + .UInt128, .Int128 => 7, + else => unreachable, + }; +} + +fn cIntTypeCmp(a: ZigClangQualType, b: ZigClangQualType) math.Order { + const a_index = cIntTypeToIndex(a); + const b_index = cIntTypeToIndex(b); + return math.order(a_index, b_index); +} + +fn cIsSignedInteger(qt: ZigClangQualType) bool { + const c_type = qualTypeCanon(qt); + if (ZigClangType_getTypeClass(c_type) != .Builtin) return false; + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type); + return switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .SChar, + .Short, + .Int, + .Long, + .LongLong, + .Int128, + .WChar_S, + => true, + else => false, + }; +} + +fn cIsFloating(qt: ZigClangQualType) bool { + const c_type = qualTypeCanon(qt); + if (ZigClangType_getTypeClass(c_type) != .Builtin) return false; + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type); + return switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Float, + .Double, + .Float128, + .LongDouble, + => true, + else => false, + }; +} + +fn cIsLongLongInteger(qt: ZigClangQualType) bool { + const c_type = qualTypeCanon(qt); + if (ZigClangType_getTypeClass(c_type) != .Builtin) return false; + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type); + return switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .LongLong, .ULongLong, .Int128, .UInt128 => true, + else => false, + }; +} fn transCreateNodeAssign( rp: RestorePoint, scope: *Scope, result_used: ResultUsed, lhs: *const ZigClangExpr, rhs: *const ZigClangExpr, -) !*ast.Node.InfixOp { +) !*ast.Node { // common case // c: lhs = rhs // zig: lhs = rhs if (result_used == .unused) { const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); const eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); - _ = try appendToken(rp.c, .Semicolon, ";"); - - const node = try rp.c.a().create(ast.Node.InfixOp); - node.* = ast.Node.InfixOp{ - .base = ast.Node{ .id = .InfixOp }, - .op_token = eq_token, - .lhs = lhs_node.node, - .op = .Assign, - .rhs = rhs_node.node, - }; - return node; + var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value); + if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); + return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false); } // worst case // c: lhs = rhs - // zig: (x: { + // zig: (blk: { // zig: const _tmp = rhs; // zig: lhs = _tmp; - // zig: break :x _tmp + // zig: break :blk _tmp // zig: }) - return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(lhs), "TODO: worst case assign op expr"); + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const tmp = try block_scope.makeMangledName(rp.c, "tmp"); + + const node = try transCreateNodeVarDecl(rp.c, false, true, tmp); + node.eq_token = try appendToken(rp.c, .Equal, "="); + var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value); + if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } + node.init_node = rhs_node; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value); + const eq_token = try appendToken(rp.c, .Equal, "="); + const ident = try transCreateNodeIdentifier(rp.c, tmp); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, eq_token, ident, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); + _ = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + return &block_scope.block_node.base; } fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.BuiltinCall { const builtin_token = try appendToken(c, .Builtin, name); _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.BuiltinCall); - node.* = ast.Node.BuiltinCall{ - .base = ast.Node{ .id = .BuiltinCall }, + node.* = .{ .builtin_token = builtin_token, .params = ast.Node.BuiltinCall.ParamList.init(c.a()), .rparen_token = undefined, // set after appending args @@ -1031,11 +3659,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.SuffixOp); - node.* = ast.Node.SuffixOp{ - .base = ast.Node{ .id = .SuffixOp }, - .lhs = fn_expr, - .op = ast.Node.SuffixOp.Op{ - .Call = ast.Node.SuffixOp.Op.Call{ + node.* = .{ + .lhs = .{ .node = fn_expr }, + .op = .{ + .Call = .{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), .async_token = null, }, @@ -1045,6 +3672,17 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { return node; } +fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { + const field_access_node = try c.a().create(ast.Node.InfixOp); + field_access_node.* = .{ + .op_token = try appendToken(c, .Period, "."), + .lhs = container, + .op = .Period, + .rhs = try transCreateNodeIdentifier(c, field_name), + }; + return &field_access_node.base; +} + fn transCreateNodePrefixOp( c: *Context, op: ast.Node.PrefixOp.Op, @@ -1053,7 +3691,6 @@ fn transCreateNodePrefixOp( ) !*ast.Node.PrefixOp { const node = try c.a().create(ast.Node.PrefixOp); node.* = ast.Node.PrefixOp{ - .base = ast.Node{ .id = .PrefixOp }, .op_token = try appendToken(c, op_tok_id, bytes), .op = op, .rhs = undefined, // translate and set afterward @@ -1064,34 +3701,62 @@ fn transCreateNodePrefixOp( fn transCreateNodeInfixOp( rp: RestorePoint, scope: *Scope, - stmt: *const ZigClangBinaryOperator, + lhs_node: *ast.Node, op: ast.Node.InfixOp.Op, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, + op_token: ast.TokenIndex, + rhs_node: *ast.Node, + used: ResultUsed, grouped: bool, ) !*ast.Node { - const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; - const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); - const op_token = try appendToken(rp.c, op_tok_id, bytes); - const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + var lparen = if (grouped) + try appendToken(rp.c, .LParen, "(") + else + null; const node = try rp.c.a().create(ast.Node.InfixOp); - node.* = ast.Node.InfixOp{ - .base = ast.Node{ .id = .InfixOp }, + node.* = .{ .op_token = op_token, - .lhs = lhs.node, + .lhs = lhs_node, .op = op, - .rhs = rhs.node, + .rhs = rhs_node, }; - if (!grouped) return &node.base; + if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base); const rparen = try appendToken(rp.c, .RParen, ")"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); - grouped_expr.* = ast.Node.GroupedExpression{ - .base = ast.Node{ .id = .GroupedExpression }, - .lparen = lparen, + grouped_expr.* = .{ + .lparen = lparen.?, .expr = &node.base, .rparen = rparen, }; - return &grouped_expr.base; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); +} + +fn transCreateNodeBoolInfixOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + op: ast.Node.InfixOp.Op, + used: ResultUsed, + grouped: bool, +) !*ast.Node { + std.debug.assert(op == .BoolAnd or op == .BoolOr); + + const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true); + const op_token = if (op == .BoolAnd) + try appendToken(rp.c, .Keyword_and, "and") + else + try appendToken(rp.c, .Keyword_or, "or"); + const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true); + + return transCreateNodeInfixOp( + rp, + scope, + lhs_hode, + op, + op_token, + rhs, + used, + grouped, + ); } fn transCreateNodePtrType( @@ -1099,16 +3764,29 @@ fn transCreateNodePtrType( is_const: bool, is_volatile: bool, op_tok_id: std.zig.Token.Id, - bytes: []const u8, ) !*ast.Node.PrefixOp { const node = try c.a().create(ast.Node.PrefixOp); - node.* = ast.Node.PrefixOp{ - .base = ast.Node{ .id = .PrefixOp }, - .op_token = try appendToken(c, op_tok_id, bytes), - .op = ast.Node.PrefixOp.Op{ - .PtrType = ast.Node.PrefixOp.PtrInfo{ - .allowzero_token = null, - .align_info = null, + const op_token = switch (op_tok_id) { + .LBracket => blk: { + const lbracket = try appendToken(c, .LBracket, "["); + _ = try appendToken(c, .Asterisk, "*"); + _ = try appendToken(c, .RBracket, "]"); + break :blk lbracket; + }, + .Identifier => blk: { + const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token + _ = try appendToken(c, .Asterisk, "*"); + _ = try appendIdentifier(c, "c"); + _ = try appendToken(c, .RBracket, "]"); + break :blk lbracket; + }, + .Asterisk => try appendToken(c, .Asterisk, "*"), + else => unreachable, + }; + node.* = .{ + .op_token = op_token, + .op = .{ + .PtrType = .{ .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, }, @@ -1118,12 +3796,18 @@ fn transCreateNodePtrType( return node; } -fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { - const num_limbs = ZigClangAPSInt_getNumWords(int.?); - var big = try std.math.big.Int.initCapacity(c.a(), num_limbs); +fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { + const num_limbs = ZigClangAPSInt_getNumWords(int); + var aps_int = int; + const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int); + if (is_negative) + aps_int = ZigClangAPSInt_negate(aps_int); + var big = try math.big.Int.initCapacity(c.a(), num_limbs); + if (is_negative) + big.negate(); defer big.deinit(); - const data = ZigClangAPSInt_getRawData(int.?); - var i: usize = 0; + const data = ZigClangAPSInt_getRawData(aps_int); + var i: @TypeOf(num_limbs) = 0; while (i < num_limbs) : (i += 1) big.limbs[i] = data[i]; const str = big.toString(c.a(), 10) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -1131,25 +3815,417 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { }; const token = try appendToken(c, .IntegerLiteral, str); const node = try c.a().create(ast.Node.IntegerLiteral); - node.* = ast.Node.IntegerLiteral{ - .base = ast.Node{ .id = .IntegerLiteral }, + node.* = .{ + .token = token, + }; + if (is_negative) + ZigClangAPSInt_free(aps_int); + return &node.base; +} + +fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { + const ltoken = try appendToken(c, .Keyword_return, "return"); + const node = try c.a().create(ast.Node.ControlFlowExpression); + node.* = .{ + .ltoken = ltoken, + .kind = .Return, + .rhs = null, + }; + return node; +} + +fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { + const token = try appendToken(c, .Keyword_undefined, "undefined"); + const node = try c.a().create(ast.Node.UndefinedLiteral); + node.* = .{ .token = token, }; return &node.base; } -fn transCreateNodeReturnExpr(c: *Context) !*ast.Node { - const ltoken = try appendToken(c, .Keyword_return, "return"); - const node = try c.a().create(ast.Node.ControlFlowExpression); - node.* = ast.Node.ControlFlowExpression{ - .base = ast.Node{ .id = .ControlFlowExpression }, - .ltoken = ltoken, - .kind = .Return, - .rhs = null, +fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { + const token = try appendToken(c, .Keyword_null, "null"); + const node = try c.a().create(ast.Node.NullLiteral); + node.* = .{ + .token = token, }; return &node.base; } +fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { + const token = if (value) + try appendToken(c, .Keyword_true, "true") + else + try appendToken(c, .Keyword_false, "false"); + const node = try c.a().create(ast.Node.BoolLiteral); + node.* = .{ + .token = token, + }; + return &node.base; +} + +fn transCreateNodeContainerInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp { + _ = try appendToken(c, .LBrace, "{"); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ + .lhs = .{ .dot = dot_tok }, + .op = .{ + .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(c.a()), + }, + .rtoken = undefined, // set after appending values + }; + return node; +} + +fn transCreateNodeStructInitializer(c: *Context, ty: *ast.Node) !*ast.Node.SuffixOp { + _ = try appendToken(c, .LBrace, "{"); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ + .lhs = .{ .node = ty }, + .op = .{ + .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(c.a()), + }, + .rtoken = undefined, // set after appending values + }; + return node; +} + +fn transCreateNodeInt(c: *Context, int: var) !*ast.Node { + const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int}); + const node = try c.a().create(ast.Node.IntegerLiteral); + node.* = .{ + .token = token, + }; + return &node.base; +} + +fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node { + const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); + const node = try c.a().create(ast.Node.FloatLiteral); + node.* = .{ + .token = token, + }; + return &node.base; +} + +fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { + const call_node = try transCreateNodeBuiltinFnCall(c, "@OpaqueType"); + call_node.rparen_token = try appendToken(c, .RParen, ")"); + return &call_node.base; +} + +fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node { + const scope = &c.global_scope.base; + + const pub_tok = try appendToken(c, .Keyword_pub, "pub"); + const inline_tok = try appendToken(c, .Keyword_inline, "inline"); + const fn_tok = try appendToken(c, .Keyword_fn, "fn"); + const name_tok = try appendIdentifier(c, name); + _ = try appendToken(c, .LParen, "("); + + var fn_params = ast.Node.FnProto.ParamList.init(c.a()); + var it = proto_alias.params.iterator(0); + while (it.next()) |pn| { + if (it.index != 0) { + _ = try appendToken(c, .Comma, ","); + } + const param = pn.*.cast(ast.Node.ParamDecl).?; + + const param_name_tok = param.name_token orelse + try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()}); + + _ = try appendToken(c, .Colon, ":"); + + const param_node = try c.a().create(ast.Node.ParamDecl); + param_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .noalias_token = param.noalias_token, + .name_token = param_name_tok, + .type_node = param.type_node, + .var_args_token = null, + }; + try fn_params.push(¶m_node.base); + } + + _ = try appendToken(c, .RParen, ")"); + + const fn_proto = try c.a().create(ast.Node.FnProto); + fn_proto.* = .{ + .doc_comments = null, + .visib_token = pub_tok, + .fn_token = fn_tok, + .name_token = name_tok, + .params = fn_params, + .return_type = proto_alias.return_type, + .var_args_token = null, + .extern_export_inline_token = inline_tok, + .cc_token = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + .section_expr = null, + .callconv_expr = null, + }; + + const block = try transCreateNodeBlock(c, null); + + const return_expr = try transCreateNodeReturnExpr(c); + const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?); + const call_expr = try transCreateNodeFnCall(c, unwrap_expr); + it = fn_params.iterator(0); + while (it.next()) |pn| { + if (it.index != 0) { + _ = try appendToken(c, .Comma, ","); + } + const param = pn.*.cast(ast.Node.ParamDecl).?; + try call_expr.op.Call.params.push(try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?))); + } + call_expr.rtoken = try appendToken(c, .RParen, ")"); + return_expr.rhs = &call_expr.base; + _ = try appendToken(c, .Semicolon, ";"); + + block.rbrace = try appendToken(c, .RBrace, "}"); + try block.statements.push(&return_expr.base); + fn_proto.body_node = &block.base; + return &fn_proto.base; +} + +fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node { + _ = try appendToken(c, .Period, "."); + const qm = try appendToken(c, .QuestionMark, "?"); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .op = .UnwrapOptional, + .lhs = .{ .node = wrapped }, + .rtoken = qm, + }; + return &node.base; +} + +fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { + const node = try c.a().create(ast.Node.EnumLiteral); + node.* = .{ + .dot = try appendToken(c, .Period, "."), + .name = try appendIdentifier(c, name), + }; + return &node.base; +} + +fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node { + const node = try c.a().create(ast.Node.StringLiteral); + node.* = .{ + .token = try appendToken(c, .StringLiteral, str), + }; + return &node.base; +} + +fn transCreateNodeIf(c: *Context) !*ast.Node.If { + const if_tok = try appendToken(c, .Keyword_if, "if"); + _ = try appendToken(c, .LParen, "("); + const node = try c.a().create(ast.Node.If); + node.* = .{ + .if_token = if_tok, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + }; + return node; +} + +fn transCreateNodeElse(c: *Context) !*ast.Node.Else { + const node = try c.a().create(ast.Node.Else); + node.* = .{ + .else_token = try appendToken(c, .Keyword_else, "else"), + .payload = null, + .body = undefined, + }; + return node; +} + +fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block { + const label_node = if (label) |l| blk: { + const ll = try appendIdentifier(c, l); + _ = try appendToken(c, .Colon, ":"); + break :blk ll; + } else null; + const block_node = try c.a().create(ast.Node.Block); + block_node.* = .{ + .label = label_node, + .lbrace = try appendToken(c, .LBrace, "{"), + .statements = ast.Node.Block.StatementList.init(c.a()), + .rbrace = undefined, + }; + return block_node; +} + +fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression { + const ltoken = try appendToken(c, .Keyword_break, "break"); + const label_node = if (label) |l| blk: { + _ = try appendToken(c, .Colon, ":"); + break :blk try transCreateNodeIdentifier(c, l); + } else null; + const node = try c.a().create(ast.Node.ControlFlowExpression); + node.* = .{ + .ltoken = ltoken, + .kind = .{ .Break = label_node }, + .rhs = null, + }; + return node; +} + +fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl { + const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null; + const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var"); + const name_tok = try appendIdentifier(c, name); + + const node = try c.a().create(ast.Node.VarDecl); + node.* = .{ + .doc_comments = null, + .visib_token = visib_tok, + .thread_local_token = null, + .name_token = name_tok, + .eq_token = undefined, + .mut_token = mut_tok, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .type_node = null, + .align_node = null, + .section_node = null, + .init_node = null, + .semicolon_token = undefined, + }; + return node; +} + +fn transCreateNodeWhile(c: *Context) !*ast.Node.While { + const while_tok = try appendToken(c, .Keyword_while, "while"); + _ = try appendToken(c, .LParen, "("); + + const node = try c.a().create(ast.Node.While); + node.* = .{ + .label = null, + .inline_token = null, + .while_token = while_tok, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, + }; + return node; +} + +fn transCreateNodeContinue(c: *Context) !*ast.Node { + const ltoken = try appendToken(c, .Keyword_continue, "continue"); + const node = try c.a().create(ast.Node.ControlFlowExpression); + node.* = .{ + .ltoken = ltoken, + .kind = .{ .Continue = null }, + .rhs = null, + }; + _ = try appendToken(c, .Semicolon, ";"); + return &node.base; +} + +fn transCreateNodeSwitch(c: *Context) !*ast.Node.Switch { + const switch_tok = try appendToken(c, .Keyword_switch, "switch"); + _ = try appendToken(c, .LParen, "("); + + const node = try c.a().create(ast.Node.Switch); + node.* = .{ + .switch_token = switch_tok, + .expr = undefined, + .cases = ast.Node.Switch.CaseList.init(c.a()), + .rbrace = undefined, + }; + return node; +} + +fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase { + const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>"); + + const node = try c.a().create(ast.Node.SwitchCase); + node.* = .{ + .items = ast.Node.SwitchCase.ItemList.init(c.a()), + .arrow_token = arrow_tok, + .payload = null, + .expr = undefined, + }; + try node.items.push(lhs); + return node; +} + +fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { + const node = try c.a().create(ast.Node.SwitchElse); + node.* = .{ + .token = try appendToken(c, .Keyword_else, "else"), + }; + return &node.base; +} + +fn transCreateNodeShiftOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + op: ast.Node.InfixOp.Op, + op_tok_id: std.zig.Token.Id, + bytes: []const u8, +) !*ast.Node { + std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight); + + const lhs_expr = ZigClangBinaryOperator_getLHS(stmt); + const rhs_expr = ZigClangBinaryOperator_getRHS(stmt); + const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); + // lhs >> @as(u5, rh) + + const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); + const op_token = try appendToken(rp.c, op_tok_id, bytes); + + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location); + try cast_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value); + try cast_node.params.push(rhs); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const node = try rp.c.a().create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ + .op_token = op_token, + .lhs = lhs, + .op = op, + .rhs = &cast_node.base, + }; + + return &node.base; +} + +fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .lhs = .{ .node = lhs }, + .op = .Deref, + .rtoken = try appendToken(c, .PeriodAsterisk, ".*"), + }; + return &node.base; +} + +fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp { + _ = try appendToken(c, .LBrace, "["); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .lhs = .{ .node = lhs }, + .op = .{ + .ArrayAccess = undefined, + }, + .rtoken = undefined, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -1173,32 +4249,37 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour switch (ZigClangType_getTypeClass(ty)) { .Builtin => { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - .Void => return appendIdentifier(rp.c, "c_void"), - .Bool => return appendIdentifier(rp.c, "bool"), - .Char_U, .UChar, .Char_S, .Char8 => return appendIdentifier(rp.c, "u8"), - .SChar => return appendIdentifier(rp.c, "i8"), - .UShort => return appendIdentifier(rp.c, "c_ushort"), - .UInt => return appendIdentifier(rp.c, "c_uint"), - .ULong => return appendIdentifier(rp.c, "c_ulong"), - .ULongLong => return appendIdentifier(rp.c, "c_ulonglong"), - .Short => return appendIdentifier(rp.c, "c_short"), - .Int => return appendIdentifier(rp.c, "c_int"), - .Long => return appendIdentifier(rp.c, "c_long"), - .LongLong => return appendIdentifier(rp.c, "c_longlong"), - .UInt128 => return appendIdentifier(rp.c, "u128"), - .Int128 => return appendIdentifier(rp.c, "i128"), - .Float => return appendIdentifier(rp.c, "f32"), - .Double => return appendIdentifier(rp.c, "f64"), - .Float128 => return appendIdentifier(rp.c, "f128"), - .Float16 => return appendIdentifier(rp.c, "f16"), - .LongDouble => return appendIdentifier(rp.c, "c_longdouble"), - else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"), - } + return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Void => "c_void", + .Bool => "bool", + .Char_U, .UChar, .Char_S, .Char8 => "u8", + .SChar => "i8", + .UShort => "c_ushort", + .UInt => "c_uint", + .ULong => "c_ulong", + .ULongLong => "c_ulonglong", + .Short => "c_short", + .Int => "c_int", + .Long => "c_long", + .LongLong => "c_longlong", + .UInt128 => "u128", + .Int128 => "i128", + .Float => "f32", + .Double => "f64", + .Float128 => "f128", + .Float16 => "f16", + .LongDouble => "c_longdouble", + else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), + }); }, .FunctionProto => { const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty); - const fn_proto = try transFnProto(rp, fn_proto_ty, source_loc, null, false); + const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false); + return &fn_proto.base; + }, + .FunctionNoProto => { + const fn_no_proto_ty = @ptrCast(*const ZigClangFunctionType, ty); + const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false); return &fn_proto.base; }, .Paren => { @@ -1219,7 +4300,6 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour ZigClangQualType_isConstQualified(child_qt), ZigClangQualType_isVolatileQualified(child_qt), .Asterisk, - "*", ); optional_node.rhs = &pointer_node.base; pointer_node.rhs = try transQualType(rp, child_qt, source_loc); @@ -1229,24 +4309,102 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour rp.c, ZigClangQualType_isConstQualified(child_qt), ZigClangQualType_isVolatileQualified(child_qt), - .BracketStarCBracket, - "[*c]", + .Identifier, ); pointer_node.rhs = try transQualType(rp, child_qt, source_loc); return &pointer_node.base; }, + .ConstantArray => { + const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, ty); + + const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty); + const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize)); + var node = try transCreateNodePrefixOp( + rp.c, + .{ + .ArrayType = .{ + .len_expr = undefined, + .sentinel = null, + }, + }, + .LBracket, + "[", + ); + node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size); + _ = try appendToken(rp.c, .RBracket, "]"); + node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc); + return &node.base; + }, + .IncompleteArray => { + const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty); + + const child_qt = ZigClangIncompleteArrayType_getElementType(incomplete_array_ty); + var node = try transCreateNodePtrType( + rp.c, + ZigClangQualType_isConstQualified(child_qt), + ZigClangQualType_isVolatileQualified(child_qt), + .Identifier, + ); + node.rhs = try transQualType(rp, child_qt, source_loc); + return &node.base; + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + return (try transTypeDef(rp.c, typedef_decl, false)) orelse + revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); + }, + .Record => { + const record_ty = @ptrCast(*const ZigClangRecordType, ty); + + const record_decl = ZigClangRecordType_getDecl(record_ty); + return (try transRecordDecl(rp.c, record_decl)) orelse + revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); + }, + .Enum => { + const enum_ty = @ptrCast(*const ZigClangEnumType, ty); + + const enum_decl = ZigClangEnumType_getDecl(enum_ty); + return (try transEnumDecl(rp.c, enum_decl)) orelse + revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); + }, + .Elaborated => { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); + return transQualType(rp, ZigClangElaboratedType_getNamedType(elaborated_ty), source_loc); + }, + .Decayed => { + const decayed_ty = @ptrCast(*const ZigClangDecayedType, ty); + return transQualType(rp, ZigClangDecayedType_getDecayedType(decayed_ty), source_loc); + }, + .Attributed => { + const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty); + return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc); + }, + .MacroQualified => { + const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty); + return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc); + }, else => { const type_name = rp.c.str(ZigClangType_getTypeClassName(ty)); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); }, } } +fn isCVoid(qt: ZigClangQualType) bool { + const ty = ZigClangQualType_getTypePtr(qt); + if (ZigClangType_getTypeClass(ty) == .Builtin) { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + return ZigClangBuiltinType_getKind(builtin_ty) == .Void; + } + return false; +} + const FnDeclContext = struct { fn_name: []const u8, has_body: bool, storage_class: ZigClangStorageClass, - scope: **Scope, is_export: bool, }; @@ -1259,12 +4417,24 @@ fn transCC( switch (clang_cc) { .C => return CallingConvention.C, .X86StdCall => return CallingConvention.Stdcall, - else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)), + .X86FastCall => return CallingConvention.Fastcall, + .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall, + .X86ThisCall => return CallingConvention.Thiscall, + .AAPCS => return CallingConvention.AAPCS, + .AAPCS_VFP => return CallingConvention.AAPCSVFP, + else => return revertAndWarn( + rp, + error.UnsupportedType, + source_loc, + "unsupported calling convention: {}", + .{@tagName(clang_cc)}, + ), } } fn transFnProto( rp: RestorePoint, + fn_decl: ?*const ZigClangFunctionDecl, fn_proto_ty: *const ZigClangFunctionProtoType, source_loc: ZigClangSourceLocation, fn_decl_context: ?FnDeclContext, @@ -1273,13 +4443,7 @@ fn transFnProto( const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty); const cc = try transCC(rp, fn_ty, source_loc); const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty); - const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty); - var i: usize = 0; - while (i < param_count) : (i += 1) { - return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType"); - } - - return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); + return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } fn transFnNoProto( @@ -1291,11 +4455,13 @@ fn transFnNoProto( ) !*ast.Node.FnProto { const cc = try transCC(rp, fn_ty, source_loc); const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true; - return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); + return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } fn finishTransFnProto( rp: RestorePoint, + fn_decl: ?*const ZigClangFunctionDecl, + fn_proto_ty: ?*const ZigClangFunctionProtoType, fn_ty: *const ZigClangFunctionType, source_loc: ZigClangSourceLocation, fn_decl_context: ?FnDeclContext, @@ -1304,14 +4470,13 @@ fn finishTransFnProto( is_pub: bool, ) !*ast.Node.FnProto { const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; - const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else true; + const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; // TODO check for always_inline attribute // TODO check for align attribute // pub extern fn name(...) T const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; - const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; const extern_export_inline_tok = if (is_export) try appendToken(rp.c, .Keyword_export, "export") else if (cc == .C and is_extern) @@ -1319,22 +4484,125 @@ fn finishTransFnProto( else null; const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn"); - const name_tok = if (fn_decl_context) |ctx| try appendToken(rp.c, .Identifier, ctx.fn_name) else null; + const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null; const lparen_tok = try appendToken(rp.c, .LParen, "("); - const var_args_tok = if (is_var_args) try appendToken(rp.c, .Ellipsis3, "...") else null; + + var fn_params = ast.Node.FnProto.ParamList.init(rp.c.a()); + const param_count: usize = if (fn_proto_ty != null) ZigClangFunctionProtoType_getNumParams(fn_proto_ty.?) else 0; + + var i: usize = 0; + while (i < param_count) : (i += 1) { + const param_qt = ZigClangFunctionProtoType_getParamType(fn_proto_ty.?, @intCast(c_uint, i)); + + const noalias_tok = if (ZigClangQualType_isRestrictQualified(param_qt)) try appendToken(rp.c, .Keyword_noalias, "noalias") else null; + + const param_name_tok: ?ast.TokenIndex = blk: { + if (fn_decl) |decl| { + const param = ZigClangFunctionDecl_getParamDecl(decl, @intCast(c_uint, i)); + const param_name: []const u8 = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, param))); + if (param_name.len < 1) + break :blk null; + + const result = try appendIdentifier(rp.c, param_name); + _ = try appendToken(rp.c, .Colon, ":"); + break :blk result; + } + break :blk null; + }; + + const type_node = try transQualType(rp, param_qt, source_loc); + + const param_node = try rp.c.a().create(ast.Node.ParamDecl); + param_node.* = ast.Node.ParamDecl{ + .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, + .doc_comments = null, + .comptime_token = null, + .noalias_token = noalias_tok, + .name_token = param_name_tok, + .type_node = type_node, + .var_args_token = null, + }; + try fn_params.push(¶m_node.base); + + if (i + 1 < param_count) { + _ = try appendToken(rp.c, .Comma, ","); + } + } + + if (is_var_args) { + if (param_count > 0) { + _ = try appendToken(rp.c, .Comma, ","); + } + + const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl); + var_arg_node.* = ast.Node.ParamDecl{ + .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, + .doc_comments = null, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, // Note: Accessing this causes an access violation. Need to check .var_args_token first before trying this field + .var_args_token = try appendToken(rp.c, .Ellipsis3, "..."), + }; + try fn_params.push(&var_arg_node.base); + } + const rparen_tok = try appendToken(rp.c, .RParen, ")"); + const linksection_expr = blk: { + if (fn_decl) |decl| { + var str_len: usize = undefined; + if (ZigClangFunctionDecl_getSectionAttribute(decl, &str_len)) |str_ptr| { + _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); + _ = try appendToken(rp.c, .LParen, "("); + const expr = try transCreateNodeStringLiteral( + rp.c, + try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + ); + _ = try appendToken(rp.c, .RParen, ")"); + + break :blk expr; + } + } + break :blk null; + }; + + const align_expr = blk: { + if (fn_decl) |decl| { + const alignment = ZigClangFunctionDecl_getAlignedAttribute(decl, rp.c.clang_context); + if (alignment != 0) { + _ = try appendToken(rp.c, .Keyword_linksection, "align"); + _ = try appendToken(rp.c, .LParen, "("); + // Clang reports the alignment in bits + const expr = try transCreateNodeInt(rp.c, alignment / 8); + _ = try appendToken(rp.c, .RParen, ")"); + + break :blk expr; + } + } + break :blk null; + }; + + const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: { + _ = try appendToken(rp.c, .Keyword_callconv, "callconv"); + _ = try appendToken(rp.c, .LParen, "("); + const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc)); + _ = try appendToken(rp.c, .RParen, ")"); + break :blk expr; + }; + const return_type_node = blk: { if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) { - break :blk try appendIdentifier(rp.c, "noreturn"); + break :blk try transCreateNodeIdentifier(rp.c, "noreturn"); } else { const return_qt = ZigClangFunctionType_getReturnType(fn_ty); - if (ZigClangType_isVoidType(qualTypeCanon(return_qt))) { - break :blk try appendIdentifier(rp.c, "void"); + if (isCVoid(return_qt)) { + // convert primitive c_void to actual void (only for return type) + break :blk try transCreateNodeIdentifier(rp.c, "void"); } else { break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) { error.UnsupportedType => { - try emitWarning(rp.c, source_loc, "unsupported function proto return type"); + try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{}); return err; }, error.OutOfMemory => |e| return e, @@ -1344,35 +4612,22 @@ fn finishTransFnProto( }; const fn_proto = try rp.c.a().create(ast.Node.FnProto); - fn_proto.* = ast.Node.FnProto{ - .base = ast.Node{ .id = ast.Node.Id.FnProto }, + fn_proto.* = .{ .doc_comments = null, .visib_token = pub_tok, .fn_token = fn_tok, .name_token = name_tok, - .params = ast.Node.FnProto.ParamList.init(rp.c.a()), - .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node }, + .params = fn_params, + .return_type = .{ .Explicit = return_type_node }, .var_args_token = null, // TODO this field is broken in the AST data model .extern_export_inline_token = extern_export_inline_tok, - .cc_token = cc_tok, + .cc_token = null, .body_node = null, .lib_name = null, - .align_expr = null, - .section_expr = null, + .align_expr = align_expr, + .section_expr = linksection_expr, + .callconv_expr = callconv_expr, }; - if (is_var_args) { - const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl); - var_arg_node.* = ast.Node.ParamDecl{ - .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, - .doc_comments = null, - .comptime_token = null, - .noalias_token = null, - .name_token = null, - .type_node = undefined, - .var_args_token = var_args_tok, - }; - try fn_proto.params.push(&var_arg_node.base); - } return fn_proto; } @@ -1381,21 +4636,23 @@ fn revertAndWarn( err: var, source_loc: ZigClangSourceLocation, comptime format: []const u8, - args: ..., -) (@typeOf(err) || error{OutOfMemory}) { + args: var, +) (@TypeOf(err) || error{OutOfMemory}) { rp.activate(); try emitWarning(rp.c, source_loc, format, args); return err; } -fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: ...) !void { - _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, c.locStr(loc), args); +fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: var) !void { + const args_prefix = .{c.locStr(loc)}; + _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args); } -fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: ...) !void { - // const name = @compileError(msg); +pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void { + // pub const name = @compileError(msg); + const pub_tok = try appendToken(c, .Keyword_pub, "pub"); const const_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendToken(c, .Identifier, name); + const name_tok = try appendIdentifier(c, name); const eq_tok = try appendToken(c, .Equal, "="); const builtin_tok = try appendToken(c, .Builtin, "@compileError"); const lparen_tok = try appendToken(c, .LParen, "("); @@ -1422,7 +4679,7 @@ fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime var_decl_node.* = ast.Node.VarDecl{ .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = null, - .visib_token = null, + .visib_token = pub_tok, .thread_local_token = null, .name_token = name_tok, .eq_token = eq_tok, @@ -1436,14 +4693,15 @@ fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime .init_node = &call_node.base, .semicolon_token = semi_tok, }; - try c.tree.root_node.decls.push(&var_decl_node.base); + try addTopLevelDecl(c, name, &var_decl_node.base); } fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex { - return appendTokenFmt(c, token_id, "{}", bytes); + std.debug.assert(token_id != .Identifier); // use appendIdentifier + return appendTokenFmt(c, token_id, "{}", .{bytes}); } -fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: ...) !ast.TokenIndex { +fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: var) !ast.TokenIndex { const S = struct { fn callback(context: *Context, bytes: []const u8) error{OutOfMemory}!void { return context.source_buffer.append(bytes); @@ -1468,8 +4726,62 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, return token_index; } -fn appendIdentifier(c: *Context, name: []const u8) !*ast.Node { - const token_index = try appendToken(c, .Identifier, name); +// TODO hook up with codegen +fn isZigPrimitiveType(name: []const u8) bool { + if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { + for (name[1..]) |c| { + switch (c) { + '0'...'9' => {}, + else => return false, + } + } + return true; + } + // void is invalid in c so it doesn't need to be checked. + return mem.eql(u8, name, "comptime_float") or + mem.eql(u8, name, "comptime_int") or + mem.eql(u8, name, "bool") or + mem.eql(u8, name, "isize") or + mem.eql(u8, name, "usize") or + mem.eql(u8, name, "f16") or + mem.eql(u8, name, "f32") or + mem.eql(u8, name, "f64") or + mem.eql(u8, name, "f128") or + mem.eql(u8, name, "c_longdouble") or + mem.eql(u8, name, "noreturn") or + mem.eql(u8, name, "type") or + mem.eql(u8, name, "anyerror") or + mem.eql(u8, name, "c_short") or + mem.eql(u8, name, "c_ushort") or + mem.eql(u8, name, "c_int") or + mem.eql(u8, name, "c_uint") or + mem.eql(u8, name, "c_long") or + mem.eql(u8, name, "c_ulong") or + mem.eql(u8, name, "c_longlong") or + mem.eql(u8, name, "c_ulonglong"); +} + +fn isValidZigIdentifier(name: []const u8) bool { + for (name) |c, i| { + switch (c) { + '_', 'a'...'z', 'A'...'Z' => {}, + '0'...'9' => if (i == 0) return false, + else => return false, + } + } + return true; +} + +fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { + if (!isValidZigIdentifier(name) or std.zig.Token.getKeyword(name) != null) { + return appendTokenFmt(c, .Identifier, "@\"{}\"", .{name}); + } else { + return appendTokenFmt(c, .Identifier, "{}", .{name}); + } +} + +fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { + const token_index = try appendIdentifier(c, name); const identifier = try c.a().create(ast.Node.Identifier); identifier.* = ast.Node.Identifier{ .base = ast.Node{ .id = ast.Node.Id.Identifier }, @@ -1481,3 +4793,806 @@ fn appendIdentifier(c: *Context, name: []const u8) !*ast.Node { pub fn freeErrors(errors: []ClangErrMsg) void { ZigClangErrorMsg_delete(errors.ptr, errors.len); } + +fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { + // TODO if we see #undef, delete it from the table + var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit); + const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit); + var tok_list = ctok.TokenList.init(c.a()); + const scope = c.global_scope; + + while (it.I != it_end.I) : (it.I += 1) { + const entity = ZigClangPreprocessingRecord_iterator_deref(it); + tok_list.shrink(0); + switch (ZigClangPreprocessedEntity_getKind(entity)) { + .MacroDefinitionKind => { + const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity); + const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro); + const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro); + + const name = try c.str(raw_name); + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "_{}", .{name}) else name; + if (scope.containsNow(mangled_name)) { + continue; + } + + const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); + ctok.tokenizeCMacro(c, begin_loc, mangled_name, &tok_list, begin_c) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => { + continue; + }, + }; + + var tok_it = tok_list.iterator(0); + const first_tok = tok_it.next().?; + assert(first_tok.id == .Identifier and mem.eql(u8, first_tok.bytes, name)); + const next = tok_it.peek().?; + switch (next.id) { + .Identifier => { + // if it equals itself, ignore. for example, from stdio.h: + // #define stdin stdin + if (mem.eql(u8, name, next.bytes)) { + continue; + } + }, + .Eof => { + // this means it is a macro without a value + // we don't care about such things + continue; + }, + else => {}, + } + + const macro_fn = if (tok_it.peek().?.id == .Fn) blk: { + _ = tok_it.next(); + break :blk true; + } else false; + + (if (macro_fn) + transMacroFnDefine(c, &tok_it, mangled_name, begin_loc) + else + transMacroDefine(c, &tok_it, mangled_name, begin_loc)) catch |err| switch (err) { + error.ParseError => continue, + error.OutOfMemory => |e| return e, + }; + }, + else => {}, + } + } +} + +fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { + const scope = &c.global_scope.base; + + const node = try transCreateNodeVarDecl(c, true, true, name); + node.eq_token = try appendToken(c, .Equal, "="); + + node.init_node = try parseCExpr(c, it, source_loc, scope); + const last = it.next().?; + if (last.id != .Eof) + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: unexpected token {}", + .{last.id}, + ); + + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + _ = try c.global_scope.macro_table.put(name, &node.base); +} + +fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { + const block_scope = try Scope.Block.init(c, &c.global_scope.base, null); + const scope = &block_scope.base; + + const pub_tok = try appendToken(c, .Keyword_pub, "pub"); + const inline_tok = try appendToken(c, .Keyword_inline, "inline"); + const fn_tok = try appendToken(c, .Keyword_fn, "fn"); + const name_tok = try appendIdentifier(c, name); + _ = try appendToken(c, .LParen, "("); + + if (it.next().?.id != .LParen) { + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected '('", + .{}, + ); + } + var fn_params = ast.Node.FnProto.ParamList.init(c.a()); + while (true) { + const param_tok = it.next().?; + if (param_tok.id != .Identifier) { + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected identifier", + .{}, + ); + } + + const mangled_name = try block_scope.makeMangledName(c, param_tok.bytes); + const param_name_tok = try appendIdentifier(c, mangled_name); + _ = try appendToken(c, .Colon, ":"); + + const token_index = try appendToken(c, .Keyword_var, "var"); + const identifier = try c.a().create(ast.Node.Identifier); + identifier.* = ast.Node.Identifier{ + .base = ast.Node{ .id = ast.Node.Id.Identifier }, + .token = token_index, + }; + + const param_node = try c.a().create(ast.Node.ParamDecl); + param_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .noalias_token = null, + .name_token = param_name_tok, + .type_node = &identifier.base, + .var_args_token = null, + }; + try fn_params.push(¶m_node.base); + + if (it.peek().?.id != .Comma) + break; + _ = it.next(); + _ = try appendToken(c, .Comma, ","); + } + + if (it.next().?.id != .RParen) { + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: expected ')'", + .{}, + ); + } + + _ = try appendToken(c, .RParen, ")"); + + const type_of = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); + type_of.rparen_token = try appendToken(c, .RParen, ")"); + + const fn_proto = try c.a().create(ast.Node.FnProto); + fn_proto.* = .{ + .visib_token = pub_tok, + .extern_export_inline_token = inline_tok, + .fn_token = fn_tok, + .name_token = name_tok, + .params = fn_params, + .return_type = .{ .Explicit = &type_of.base }, + .doc_comments = null, + .var_args_token = null, + .cc_token = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + .section_expr = null, + .callconv_expr = null, + }; + + const block = try transCreateNodeBlock(c, null); + + const return_expr = try transCreateNodeReturnExpr(c); + const expr = try parseCExpr(c, it, source_loc, scope); + const last = it.next().?; + if (last.id != .Eof) + return failDecl( + c, + source_loc, + name, + "unable to translate C expr: unexpected token {}", + .{last.id}, + ); + _ = try appendToken(c, .Semicolon, ";"); + try type_of.params.push(expr); + return_expr.rhs = expr; + + block.rbrace = try appendToken(c, .RBrace, "}"); + try block.statements.push(&return_expr.base); + fn_proto.body_node = &block.base; + _ = try c.global_scope.macro_table.put(name, &fn_proto.base); +} + +const ParseError = Error || error{ParseError}; + +fn parseCExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + const node = try parseCPrefixOpExpr(c, it, source_loc, scope); + switch (it.next().?.id) { + .QuestionMark => { + // must come immediately after expr + _ = try appendToken(c, .RParen, ")"); + const if_node = try transCreateNodeIf(c); + if_node.condition = node; + if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope); + if (it.next().?.id != .Colon) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ':'", + .{}, + ); + return error.ParseError; + } + if_node.@"else" = try transCreateNodeElse(c); + if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope); + return &if_node.base; + }, + else => { + _ = it.prev(); + return node; + }, + } +} + +fn parseCNumLit(c: *Context, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node { + if (tok.id == .NumLitInt) { + var lit_bytes = tok.bytes; + + if (tok.bytes.len > 2 and tok.bytes[0] == '0') { + switch (tok.bytes[1]) { + '0'...'7' => { + // Octal + lit_bytes = try std.fmt.allocPrint(c.a(), "0o{}", .{tok.bytes}); + }, + 'X' => { + // Hexadecimal with capital X, valid in C but not in Zig + lit_bytes = try std.fmt.allocPrint(c.a(), "0x{}", .{tok.bytes[2..]}); + }, + else => {}, + } + } + + if (tok.num_lit_suffix == .None) { + return transCreateNodeInt(c, lit_bytes); + } + + const cast_node = try transCreateNodeBuiltinFnCall(c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) { + .U => "c_uint", + .L => "c_long", + .LU => "c_ulong", + .LL => "c_longlong", + .LLU => "c_ulonglong", + else => unreachable, + })); + _ = try appendToken(c, .Comma, ","); + try cast_node.params.push(try transCreateNodeInt(c, lit_bytes)); + cast_node.rparen_token = try appendToken(c, .RParen, ")"); + return &cast_node.base; + } else if (tok.id == .NumLitFloat) { + if (tok.num_lit_suffix == .None) { + return transCreateNodeFloat(c, tok.bytes); + } + const cast_node = try transCreateNodeBuiltinFnCall(c, "@as"); + try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) { + .F => "f32", + .L => "f64", + else => unreachable, + })); + _ = try appendToken(c, .Comma, ","); + try cast_node.params.push(try transCreateNodeFloat(c, tok.bytes)); + cast_node.rparen_token = try appendToken(c, .RParen, ")"); + return &cast_node.base; + } else unreachable; +} + +fn parseCPrimaryExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + const tok = it.next().?; + switch (tok.id) { + .CharLit => { + const token = try appendToken(c, .CharLiteral, tok.bytes); + const node = try c.a().create(ast.Node.CharLiteral); + node.* = ast.Node.CharLiteral{ + .token = token, + }; + return &node.base; + }, + .StrLit => { + const token = try appendToken(c, .StringLiteral, tok.bytes); + const node = try c.a().create(ast.Node.StringLiteral); + node.* = ast.Node.StringLiteral{ + .token = token, + }; + return &node.base; + }, + .NumLitInt, .NumLitFloat => { + return parseCNumLit(c, tok, source_loc); + }, + .Identifier => { + const mangled_name = scope.getAlias(tok.bytes); + return transCreateNodeIdentifier(c, mangled_name); + }, + .LParen => { + const inner_node = try parseCExpr(c, it, source_loc, scope); + + if (it.peek().?.id == .RParen) { + _ = it.next(); + if (it.peek().?.id != .LParen) { + return inner_node; + } + _ = it.next(); + } + + // hack to get zig fmt to render a comma in builtin calls + _ = try appendToken(c, .Comma, ","); + + const node_to_cast = try parseCExpr(c, it, source_loc, scope); + + if (it.next().?.id != .RParen) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ')''", + .{}, + ); + return error.ParseError; + } + + //if (@typeId(@TypeOf(x)) == .Pointer) + // @ptrCast(dest, x) + //else if (@typeId(@TypeOf(x)) == .Integer) + // @intToPtr(dest, x) + //else + // @as(dest, x) + + const if_1 = try transCreateNodeIf(c); + const type_id_1 = try transCreateNodeBuiltinFnCall(c, "@typeId"); + const type_of_1 = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); + try type_id_1.params.push(&type_of_1.base); + try type_of_1.params.push(node_to_cast); + type_of_1.rparen_token = try appendToken(c, .RParen, ")"); + type_id_1.rparen_token = try appendToken(c, .RParen, ")"); + + const cmp_1 = try c.a().create(ast.Node.InfixOp); + cmp_1.* = .{ + .op_token = try appendToken(c, .EqualEqual, "=="), + .lhs = &type_id_1.base, + .op = .EqualEqual, + .rhs = try transCreateNodeEnumLiteral(c, "Pointer"), + }; + if_1.condition = &cmp_1.base; + _ = try appendToken(c, .RParen, ")"); + + const ptr_cast = try transCreateNodeBuiltinFnCall(c, "@ptrCast"); + try ptr_cast.params.push(inner_node); + try ptr_cast.params.push(node_to_cast); + ptr_cast.rparen_token = try appendToken(c, .RParen, ")"); + if_1.body = &ptr_cast.base; + + const else_1 = try transCreateNodeElse(c); + if_1.@"else" = else_1; + + const if_2 = try transCreateNodeIf(c); + const type_id_2 = try transCreateNodeBuiltinFnCall(c, "@typeId"); + const type_of_2 = try transCreateNodeBuiltinFnCall(c, "@TypeOf"); + try type_id_2.params.push(&type_of_2.base); + try type_of_2.params.push(node_to_cast); + type_of_2.rparen_token = try appendToken(c, .RParen, ")"); + type_id_2.rparen_token = try appendToken(c, .RParen, ")"); + + const cmp_2 = try c.a().create(ast.Node.InfixOp); + cmp_2.* = .{ + .op_token = try appendToken(c, .EqualEqual, "=="), + .lhs = &type_id_2.base, + .op = .EqualEqual, + .rhs = try transCreateNodeEnumLiteral(c, "Int"), + }; + if_2.condition = &cmp_2.base; + else_1.body = &if_2.base; + _ = try appendToken(c, .RParen, ")"); + + const int_to_ptr = try transCreateNodeBuiltinFnCall(c, "@intToPtr"); + try int_to_ptr.params.push(inner_node); + try int_to_ptr.params.push(node_to_cast); + int_to_ptr.rparen_token = try appendToken(c, .RParen, ")"); + if_2.body = &int_to_ptr.base; + + const else_2 = try transCreateNodeElse(c); + if_2.@"else" = else_2; + + const as = try transCreateNodeBuiltinFnCall(c, "@as"); + try as.params.push(inner_node); + try as.params.push(node_to_cast); + as.rparen_token = try appendToken(c, .RParen, ")"); + else_2.body = &as.base; + + return &if_1.base; + }, + else => { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: unexpected token {}", + .{tok.id}, + ); + return error.ParseError; + }, + } +} + +fn parseCSuffixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + var node = try parseCPrimaryExpr(c, it, source_loc, scope); + while (true) { + const tok = it.next().?; + switch (tok.id) { + .Dot => { + const name_tok = it.next().?; + if (name_tok.id != .Identifier) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected identifier", + .{}, + ); + return error.ParseError; + } + + node = try transCreateNodeFieldAccess(c, node, name_tok.bytes); + }, + .Arrow => { + const name_tok = it.next().?; + if (name_tok.id != .Identifier) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected identifier", + .{}, + ); + return error.ParseError; + } + + const deref = try transCreateNodePtrDeref(c, node); + node = try transCreateNodeFieldAccess(c, deref, name_tok.bytes); + }, + .Asterisk => { + if (it.peek().?.id == .RParen) { + // type *) + + // hack to get zig fmt to render a comma in builtin calls + _ = try appendToken(c, .Comma, ","); + + const ptr = try transCreateNodePtrType(c, false, false, .Identifier); + ptr.rhs = node; + return &ptr.base; + } else { + // expr * expr + const op_token = try appendToken(c, .Asterisk, "*"); + const rhs = try parseCPrimaryExpr(c, it, source_loc, scope); + const mul_node = try c.a().create(ast.Node.InfixOp); + mul_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitShiftLeft, + .rhs = rhs, + }; + node = &mul_node.base; + } + }, + .Shl => { + const op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const bitshift_node = try c.a().create(ast.Node.InfixOp); + bitshift_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitShiftLeft, + .rhs = rhs, + }; + node = &bitshift_node.base; + }, + .Shr => { + const op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const bitshift_node = try c.a().create(ast.Node.InfixOp); + bitshift_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitShiftRight, + .rhs = rhs, + }; + node = &bitshift_node.base; + }, + .Pipe => { + const op_token = try appendToken(c, .Pipe, "|"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const or_node = try c.a().create(ast.Node.InfixOp); + or_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitOr, + .rhs = rhs, + }; + node = &or_node.base; + }, + .Ampersand => { + const op_token = try appendToken(c, .Ampersand, "&"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const bitand_node = try c.a().create(ast.Node.InfixOp); + bitand_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitAnd, + .rhs = rhs, + }; + node = &bitand_node.base; + }, + .Plus => { + const op_token = try appendToken(c, .Plus, "+"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const add_node = try c.a().create(ast.Node.InfixOp); + add_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .Add, + .rhs = rhs, + }; + node = &add_node.base; + }, + .Minus => { + const op_token = try appendToken(c, .Minus, "-"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const sub_node = try c.a().create(ast.Node.InfixOp); + sub_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .Sub, + .rhs = rhs, + }; + node = &sub_node.base; + }, + .And => { + const op_token = try appendToken(c, .Keyword_and, "and"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const and_node = try c.a().create(ast.Node.InfixOp); + and_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BoolAnd, + .rhs = rhs, + }; + node = &and_node.base; + }, + .Or => { + const op_token = try appendToken(c, .Keyword_or, "or"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const or_node = try c.a().create(ast.Node.InfixOp); + or_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BoolOr, + .rhs = rhs, + }; + node = &or_node.base; + }, + .Gt => { + const op_token = try appendToken(c, .AngleBracketRight, ">"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const and_node = try c.a().create(ast.Node.InfixOp); + and_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .GreaterThan, + .rhs = rhs, + }; + node = &and_node.base; + }, + .Gte => { + const op_token = try appendToken(c, .AngleBracketRightEqual, ">="); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const and_node = try c.a().create(ast.Node.InfixOp); + and_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .GreaterOrEqual, + .rhs = rhs, + }; + node = &and_node.base; + }, + .Lt => { + const op_token = try appendToken(c, .AngleBracketLeft, "<"); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const and_node = try c.a().create(ast.Node.InfixOp); + and_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .LessThan, + .rhs = rhs, + }; + node = &and_node.base; + }, + .Lte => { + const op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); + const rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + const and_node = try c.a().create(ast.Node.InfixOp); + and_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .LessOrEqual, + .rhs = rhs, + }; + node = &and_node.base; + }, + .LBrace => { + const arr_node = try transCreateNodeArrayAccess(c, node); + arr_node.op.ArrayAccess = try parseCPrefixOpExpr(c, it, source_loc, scope); + arr_node.rtoken = try appendToken(c, .RBrace, "]"); + node = &arr_node.base; + if (it.next().?.id != .RBrace) { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ']'", + .{}, + ); + return error.ParseError; + } + }, + .LParen => { + const call_node = try transCreateNodeFnCall(c, node); + while (true) { + const arg = try parseCPrefixOpExpr(c, it, source_loc, scope); + try call_node.op.Call.params.push(arg); + const next = it.next().?; + if (next.id == .Comma) + _ = try appendToken(c, .Comma, ",") + else if (next.id == .RParen) + break + else { + try failDecl( + c, + source_loc, + it.list.at(0).*.bytes, + "unable to translate C expr: expected ',' or ')'", + .{}, + ); + return error.ParseError; + } + } + call_node.rtoken = try appendToken(c, .RParen, ")"); + node = &call_node.base; + }, + else => { + _ = it.prev(); + return node; + }, + } + } +} + +fn parseCPrefixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node { + const op_tok = it.next().?; + + switch (op_tok.id) { + .Bang => { + const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + return &node.base; + }, + .Minus => { + const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + return &node.base; + }, + .Tilde => { + const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~"); + node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope); + return &node.base; + }, + .Asterisk => { + const prefix_op_expr = try parseCPrefixOpExpr(c, it, source_loc, scope); + return try transCreateNodePtrDeref(c, prefix_op_expr); + }, + else => { + _ = it.prev(); + return try parseCSuffixOpExpr(c, it, source_loc, scope); + }, + } +} + +fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { + const tok = c.tree.tokens.at(token); + const slice = c.source_buffer.toSlice()[tok.start..tok.end]; + return if (mem.startsWith(u8, slice, "@\"")) + slice[2 .. slice.len - 1] + else + slice; +} + +fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { + if (node.id == .ContainerDecl) { + return node; + } else if (node.id == .PrefixOp) { + return node; + } else if (node.cast(ast.Node.Identifier)) |ident| { + if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { + if (kv.value.cast(ast.Node.VarDecl)) |var_decl| + return getContainer(c, var_decl.init_node.?); + } + } else if (node.cast(ast.Node.InfixOp)) |infix| { + if (infix.op != .Period) + return null; + if (getContainerTypeOf(c, infix.lhs)) |ty_node| { + if (ty_node.cast(ast.Node.ContainerDecl)) |container| { + var it = container.fields_and_decls.iterator(0); + while (it.next()) |field_ref| { + const field = field_ref.*.cast(ast.Node.ContainerField).?; + const ident = infix.rhs.cast(ast.Node.Identifier).?; + if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + return getContainer(c, field.type_expr.?); + } + } + } + } + } + return null; +} + +fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { + if (ref.cast(ast.Node.Identifier)) |ident| { + if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { + if (kv.value.cast(ast.Node.VarDecl)) |var_decl| { + if (var_decl.type_node) |ty| + return getContainer(c, ty); + } + } + } else if (ref.cast(ast.Node.InfixOp)) |infix| { + if (infix.op != .Period) + return null; + if (getContainerTypeOf(c, infix.lhs)) |ty_node| { + if (ty_node.cast(ast.Node.ContainerDecl)) |container| { + var it = container.fields_and_decls.iterator(0); + while (it.next()) |field_ref| { + const field = field_ref.*.cast(ast.Node.ContainerField).?; + const ident = infix.rhs.cast(ast.Node.Identifier).?; + if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + return getContainer(c, field.type_expr.?); + } + } + } else + return ty_node; + } + } + return null; +} + +fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { + const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; + if (getContainerTypeOf(c, init)) |ty_node| { + if (ty_node.cast(ast.Node.PrefixOp)) |prefix| { + if (prefix.op == .OptionalType) { + if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { + return fn_proto; + } + } + } + } + return null; +} + +fn addMacros(c: *Context) !void { + var macro_it = c.global_scope.macro_table.iterator(); + while (macro_it.next()) |kv| { + if (getFnProto(c, kv.value)) |proto_node| { + // If a macro aliases a global variable which is a function pointer, we conclude that + // the macro is intended to represent a function that assumes the function pointer + // variable is non-null and calls it. + try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node)); + } else { + try addTopLevelDecl(c, kv.key, kv.value); + } + } +} diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 5a8dc47ef..67e1aebdc 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const builtin = @import("builtin"); +const builtin = std.builtin; const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const Value = @import("value.zig").Value; @@ -20,31 +20,31 @@ pub const Type = struct { pub fn destroy(base: *Type, comp: *Compilation) void { switch (base.id) { - Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(comp), - Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), - Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(comp), - Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), - Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), - Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), - Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), - Id.Float => @fieldParentPtr(Float, "base", base).destroy(comp), - Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp), - Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp), - Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp), - Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp), - Id.EnumLiteral => @fieldParentPtr(EnumLiteral, "base", base).destroy(comp), - Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp), - Id.Null => @fieldParentPtr(Null, "base", base).destroy(comp), - Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(comp), - Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp), - Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp), - Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(comp), - Id.Union => @fieldParentPtr(Union, "base", base).destroy(comp), - Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp), - Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp), - Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), - Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp), - Id.Vector => @fieldParentPtr(Vector, "base", base).destroy(comp), + .Struct => @fieldParentPtr(Struct, "base", base).destroy(comp), + .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), + .Type => @fieldParentPtr(MetaType, "base", base).destroy(comp), + .Void => @fieldParentPtr(Void, "base", base).destroy(comp), + .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), + .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), + .Int => @fieldParentPtr(Int, "base", base).destroy(comp), + .Float => @fieldParentPtr(Float, "base", base).destroy(comp), + .Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp), + .Array => @fieldParentPtr(Array, "base", base).destroy(comp), + .ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp), + .ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp), + .EnumLiteral => @fieldParentPtr(EnumLiteral, "base", base).destroy(comp), + .Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp), + .Null => @fieldParentPtr(Null, "base", base).destroy(comp), + .Optional => @fieldParentPtr(Optional, "base", base).destroy(comp), + .ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp), + .ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp), + .Enum => @fieldParentPtr(Enum, "base", base).destroy(comp), + .Union => @fieldParentPtr(Union, "base", base).destroy(comp), + .BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp), + .Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), + .Frame => @fieldParentPtr(Frame, "base", base).destroy(comp), + .AnyFrame => @fieldParentPtr(AnyFrame, "base", base).destroy(comp), + .Vector => @fieldParentPtr(Vector, "base", base).destroy(comp), } } @@ -52,107 +52,107 @@ pub const Type = struct { base: *Type, allocator: *Allocator, llvm_context: *llvm.Context, - ) (error{OutOfMemory}!*llvm.Type) { + ) error{OutOfMemory}!*llvm.Type { switch (base.id) { - Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context), - Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context), - Id.Type => unreachable, - Id.Void => unreachable, - Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context), - Id.NoReturn => unreachable, - Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context), - Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context), - Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context), - Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context), - Id.ComptimeFloat => unreachable, - Id.ComptimeInt => unreachable, - Id.EnumLiteral => unreachable, - Id.Undefined => unreachable, - Id.Null => unreachable, - Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context), - Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context), - Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context), - Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), - Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), - Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), - Id.ArgTuple => unreachable, - Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), - Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context), - Id.Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context), + .Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context), + .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context), + .Type => unreachable, + .Void => unreachable, + .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context), + .NoReturn => unreachable, + .Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context), + .Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context), + .Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context), + .Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context), + .ComptimeFloat => unreachable, + .ComptimeInt => unreachable, + .EnumLiteral => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context), + .ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context), + .ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context), + .Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), + .Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), + .BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), + .Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), + .Frame => return @fieldParentPtr(Frame, "base", base).getLlvmType(allocator, llvm_context), + .AnyFrame => return @fieldParentPtr(AnyFrame, "base", base).getLlvmType(allocator, llvm_context), + .Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context), } } pub fn handleIsPtr(base: *Type) bool { switch (base.id) { - Id.Type, - Id.ComptimeFloat, - Id.ComptimeInt, - Id.EnumLiteral, - Id.Undefined, - Id.Null, - Id.BoundFn, - Id.ArgTuple, - Id.Opaque, + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .BoundFn, + .Opaque, => unreachable, - Id.NoReturn, - Id.Void, - Id.Bool, - Id.Int, - Id.Float, - Id.Pointer, - Id.ErrorSet, - Id.Enum, - Id.Fn, - Id.Promise, - Id.Vector, + .NoReturn, + .Void, + .Bool, + .Int, + .Float, + .Pointer, + .ErrorSet, + .Enum, + .Fn, + .Frame, + .AnyFrame, + .Vector, => return false, - Id.Struct => @panic("TODO"), - Id.Array => @panic("TODO"), - Id.Optional => @panic("TODO"), - Id.ErrorUnion => @panic("TODO"), - Id.Union => @panic("TODO"), + .Struct => @panic("TODO"), + .Array => @panic("TODO"), + .Optional => @panic("TODO"), + .ErrorUnion => @panic("TODO"), + .Union => @panic("TODO"), } } pub fn hasBits(base: *Type) bool { switch (base.id) { - Id.Type, - Id.ComptimeFloat, - Id.ComptimeInt, - Id.EnumLiteral, - Id.Undefined, - Id.Null, - Id.BoundFn, - Id.ArgTuple, - Id.Opaque, + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .BoundFn, + .Opaque, => unreachable, - Id.Void, - Id.NoReturn, + .Void, + .NoReturn, => return false, - Id.Bool, - Id.Int, - Id.Float, - Id.Fn, - Id.Promise, - Id.Vector, + .Bool, + .Int, + .Float, + .Fn, + .Frame, + .AnyFrame, + .Vector, => return true, - Id.Pointer => { + .Pointer => { const ptr_type = @fieldParentPtr(Pointer, "base", base); return ptr_type.key.child_type.hasBits(); }, - Id.ErrorSet => @panic("TODO"), - Id.Enum => @panic("TODO"), - Id.Struct => @panic("TODO"), - Id.Array => @panic("TODO"), - Id.Optional => @panic("TODO"), - Id.ErrorUnion => @panic("TODO"), - Id.Union => @panic("TODO"), + .ErrorSet => @panic("TODO"), + .Enum => @panic("TODO"), + .Struct => @panic("TODO"), + .Array => @panic("TODO"), + .Optional => @panic("TODO"), + .ErrorUnion => @panic("TODO"), + .Union => @panic("TODO"), } } @@ -162,26 +162,26 @@ pub const Type = struct { } pub fn dump(base: *const Type) void { - std.debug.warn("{}", @tagName(base.id)); + std.debug.warn("{}", .{@tagName(base.id)}); } fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void { base.* = Type{ .base = Value{ - .id = Value.Id.Type, + .id = .Type, .typ = &MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = id, .name = name, - .abi_alignment = AbiAlignment.init(comp.loop), + .abi_alignment = AbiAlignment.init(), }; } /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead. /// Otherwise, this one will grab one from the pool and then release it. - pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { - if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; + pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { + if (base.abi_alignment.start()) |ptr| return ptr.*; { const held = try comp.zig_compiler.getAnyLlvmContext(); @@ -189,23 +189,23 @@ pub const Type = struct { const llvm_context = held.node.data; - base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); + base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context); } base.abi_alignment.resolve(); return base.abi_alignment.data; } /// If you have an llvm conext handy, you can use it here. - pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { - if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; + pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { + if (base.abi_alignment.start()) |ptr| return ptr.*; - base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); + base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context); base.abi_alignment.resolve(); return base.abi_alignment.data; } /// Lower level function that does the work. See getAbiAlignment. - async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { + fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context); return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type)); } @@ -261,30 +261,18 @@ pub const Type = struct { pub const Generic = struct { param_count: usize, - cc: CC, - - pub const CC = union(CallingConvention) { - Auto, - C, - Cold, - Naked, - Stdcall, - Async: *Type, // allocator type - }; + cc: CallingConvention, }; pub fn hash(self: *const Key) u32 { var result: u32 = 0; result +%= hashAny(self.alignment, 0); switch (self.data) { - Kind.Generic => |generic| { + .Generic => |generic| { result +%= hashAny(generic.param_count, 1); - switch (generic.cc) { - CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2), - else => result +%= hashAny(CallingConvention(generic.cc), 3), - } + result +%= hashAny(generic.cc, 3); }, - Kind.Normal => |normal| { + .Normal => |normal| { result +%= hashAny(normal.return_type, 4); result +%= hashAny(normal.is_var_args, 5); result +%= hashAny(normal.cc, 6); @@ -302,21 +290,14 @@ pub const Type = struct { if (self.alignment) |self_align| { if (self_align != other.alignment.?) return false; } - if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false; + if (@as(@TagType(Data), self.data) != @as(@TagType(Data), other.data)) return false; switch (self.data) { - Kind.Generic => |*self_generic| { + .Generic => |*self_generic| { const other_generic = &other.data.Generic; if (self_generic.param_count != other_generic.param_count) return false; - if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false; - switch (self_generic.cc) { - CallingConvention.Async => |self_allocator_type| { - const other_allocator_type = other_generic.cc.Async; - if (self_allocator_type != other_allocator_type) return false; - }, - else => {}, - } + if (self_generic.cc != other_generic.cc) return false; }, - Kind.Normal => |*self_normal| { + .Normal => |*self_normal| { const other_normal = &other.data.Normal; if (self_normal.cc != other_normal.cc) return false; if (self_normal.is_var_args != other_normal.is_var_args) return false; @@ -333,13 +314,8 @@ pub const Type = struct { pub fn deref(key: Key, comp: *Compilation) void { switch (key.data) { - Kind.Generic => |generic| { - switch (generic.cc) { - CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp), - else => {}, - } - }, - Kind.Normal => |normal| { + .Generic => {}, + .Normal => |normal| { normal.return_type.base.deref(comp); for (normal.params) |param| { param.typ.base.deref(comp); @@ -350,13 +326,8 @@ pub const Type = struct { pub fn ref(key: Key) void { switch (key.data) { - Kind.Generic => |generic| { - switch (generic.cc) { - CallingConvention.Async => |allocator_type| allocator_type.base.ref(), - else => {}, - } - }, - Kind.Normal => |normal| { + .Generic => {}, + .Normal => |normal| { normal.return_type.base.ref(); for (normal.params) |param| { param.typ.base.ref(); @@ -366,14 +337,7 @@ pub const Type = struct { } }; - pub const CallingConvention = enum { - Auto, - C, - Cold, - Naked, - Stdcall, - Async, - }; + const CallingConvention = builtin.CallingConvention; pub const Param = struct { is_noalias: bool, @@ -382,26 +346,27 @@ pub const Type = struct { fn ccFnTypeStr(cc: CallingConvention) []const u8 { return switch (cc) { - CallingConvention.Auto => "", - CallingConvention.C => "extern ", - CallingConvention.Cold => "coldcc ", - CallingConvention.Naked => "nakedcc ", - CallingConvention.Stdcall => "stdcallcc ", - CallingConvention.Async => unreachable, + .Unspecified => "", + .C => "extern ", + .Cold => "coldcc ", + .Naked => "nakedcc ", + .Stdcall => "stdcallcc ", + .Async => "async ", + else => unreachable, }; } pub fn paramCount(self: *Fn) usize { return switch (self.key.data) { - Kind.Generic => |generic| generic.param_count, - Kind.Normal => |normal| normal.params.len, + .Generic => |generic| generic.param_count, + .Normal => |normal| normal.params.len, }; } /// takes ownership of key.Normal.params on success - pub async fn get(comp: *Compilation, key: Key) !*Fn { + pub fn get(comp: *Compilation, key: Key) !*Fn { { - const held = await (async comp.fn_type_table.acquire() catch unreachable); + const held = comp.fn_type_table.acquire(); defer held.release(); if (held.value.get(&key)) |entry| { @@ -428,18 +393,10 @@ pub const Type = struct { const name_stream = &std.io.BufferOutStream.init(&name_buf).stream; switch (key.data) { - Kind.Generic => |generic| { + .Generic => |generic| { self.non_key = NonKey{ .Generic = {} }; - switch (generic.cc) { - CallingConvention.Async => |async_allocator_type| { - try name_stream.print("async<{}> ", async_allocator_type.name); - }, - else => { - const cc_str = ccFnTypeStr(generic.cc); - try name_stream.write(cc_str); - }, - } - try name_stream.write("fn("); + const cc_str = ccFnTypeStr(generic.cc); + try name_stream.print("{}fn(", .{cc_str}); var param_i: usize = 0; while (param_i < generic.param_count) : (param_i += 1) { const arg = if (param_i == 0) "var" else ", var"; @@ -447,16 +404,16 @@ pub const Type = struct { } try name_stream.write(")"); if (key.alignment) |alignment| { - try name_stream.print(" align<{}>", alignment); + try name_stream.print(" align({})", .{alignment}); } try name_stream.write(" var"); }, - Kind.Normal => |normal| { + .Normal => |normal| { self.non_key = NonKey{ .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) }, }; const cc_str = ccFnTypeStr(normal.cc); - try name_stream.print("{}fn(", cc_str); + try name_stream.print("{}fn(", .{cc_str}); for (normal.params) |param, i| { if (i != 0) try name_stream.write(", "); if (param.is_noalias) try name_stream.write("noalias "); @@ -468,16 +425,16 @@ pub const Type = struct { } try name_stream.write(")"); if (key.alignment) |alignment| { - try name_stream.print(" align<{}>", alignment); + try name_stream.print(" align({})", .{alignment}); } - try name_stream.print(" {}", normal.return_type.name); + try name_stream.print(" {}", .{normal.return_type.name}); }, } - self.base.init(comp, Id.Fn, name_buf.toOwnedSlice()); + self.base.init(comp, .Fn, name_buf.toOwnedSlice()); { - const held = await (async comp.fn_type_table.acquire() catch unreachable); + const held = comp.fn_type_table.acquire(); defer held.release(); _ = try held.value.put(&self.key, self); @@ -488,8 +445,8 @@ pub const Type = struct { pub fn destroy(self: *Fn, comp: *Compilation) void { self.key.deref(comp); switch (self.key.data) { - Kind.Generic => {}, - Kind.Normal => { + .Generic => {}, + .Normal => { self.non_key.Normal.variable_list.deinit(); }, } @@ -499,7 +456,7 @@ pub const Type = struct { pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type { const normal = &self.key.data.Normal; const llvm_return_type = switch (normal.return_type.id) { - Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, + .Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, else => try normal.return_type.getLlvmType(allocator, llvm_context), }; const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len); @@ -604,9 +561,9 @@ pub const Type = struct { return comp.u8_type; } - pub async fn get(comp: *Compilation, key: Key) !*Int { + pub fn get(comp: *Compilation, key: Key) !*Int { { - const held = await (async comp.int_type_table.acquire() catch unreachable); + const held = comp.int_type_table.acquire(); defer held.release(); if (held.value.get(&key)) |entry| { @@ -624,13 +581,13 @@ pub const Type = struct { errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; - const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count); + const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count }); errdefer comp.gpa().free(name); - self.base.init(comp, Id.Int, name); + self.base.init(comp, .Int, name); { - const held = await (async comp.int_type_table.acquire() catch unreachable); + const held = comp.int_type_table.acquire(); defer held.release(); _ = try held.value.put(&self.key, self); @@ -646,9 +603,9 @@ pub const Type = struct { comp.registerGarbage(Int, &self.garbage_node); } - pub async fn gcDestroy(self: *Int, comp: *Compilation) void { + pub fn gcDestroy(self: *Int, comp: *Compilation) void { { - const held = await (async comp.int_type_table.acquire() catch unreachable); + const held = comp.int_type_table.acquire(); defer held.release(); _ = held.value.remove(&self.key).?; @@ -689,8 +646,8 @@ pub const Type = struct { pub fn hash(self: *const Key) u32 { var result: u32 = 0; result +%= switch (self.alignment) { - Align.Abi => 0xf201c090, - Align.Override => |x| hashAny(x, 0), + .Abi => 0xf201c090, + .Override => |x| hashAny(x, 0), }; result +%= hashAny(self.child_type, 1); result +%= hashAny(self.mut, 2); @@ -704,13 +661,13 @@ pub const Type = struct { self.mut != other.mut or self.vol != other.vol or self.size != other.size or - @TagType(Align)(self.alignment) != @TagType(Align)(other.alignment)) + @as(@TagType(Align), self.alignment) != @as(@TagType(Align), other.alignment)) { return false; } switch (self.alignment) { - Align.Abi => return true, - Align.Override => |x| return x == other.alignment.Override, + .Abi => return true, + .Override => |x| return x == other.alignment.Override, } } }; @@ -740,9 +697,9 @@ pub const Type = struct { comp.registerGarbage(Pointer, &self.garbage_node); } - pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void { + pub fn gcDestroy(self: *Pointer, comp: *Compilation) void { { - const held = await (async comp.ptr_type_table.acquire() catch unreachable); + const held = comp.ptr_type_table.acquire(); defer held.release(); _ = held.value.remove(&self.key).?; @@ -751,29 +708,31 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { + pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { switch (self.key.alignment) { - Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable), - Align.Override => |alignment| return alignment, + .Abi => return self.key.child_type.getAbiAlignment(comp), + .Override => |alignment| return alignment, } } - pub async fn get( + pub fn get( comp: *Compilation, key: Key, ) !*Pointer { var normal_key = key; switch (key.alignment) { - Align.Abi => {}, - Align.Override => |alignment| { - const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable); - if (abi_align == alignment) { - normal_key.alignment = Align.Abi; + .Abi => {}, + .Override => |alignment| { + // TODO https://github.com/ziglang/zig/issues/3190 + var align_spill = alignment; + const abi_align = try key.child_type.getAbiAlignment(comp); + if (abi_align == align_spill) { + normal_key.alignment = .Abi; } }, } { - const held = await (async comp.ptr_type_table.acquire() catch unreachable); + const held = comp.ptr_type_table.acquire(); defer held.release(); if (held.value.get(&normal_key)) |entry| { @@ -791,44 +750,40 @@ pub const Type = struct { errdefer comp.gpa().destroy(self); const size_str = switch (self.key.size) { - Size.One => "*", - Size.Many => "[*]", - Size.Slice => "[]", - Size.C => "[*c]", + .One => "*", + .Many => "[*]", + .Slice => "[]", + .C => "[*c]", }; const mut_str = switch (self.key.mut) { - Mut.Const => "const ", - Mut.Mut => "", + .Const => "const ", + .Mut => "", }; const vol_str = switch (self.key.vol) { - Vol.Volatile => "volatile ", - Vol.Non => "", + .Volatile => "volatile ", + .Non => "", }; const name = switch (self.key.alignment) { - Align.Abi => try std.fmt.allocPrint( - comp.gpa(), - "{}{}{}{}", + .Abi => try std.fmt.allocPrint(comp.gpa(), "{}{}{}{}", .{ size_str, mut_str, vol_str, self.key.child_type.name, - ), - Align.Override => |alignment| try std.fmt.allocPrint( - comp.gpa(), - "{}align<{}> {}{}{}", + }), + .Override => |alignment| try std.fmt.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{ size_str, alignment, mut_str, vol_str, self.key.child_type.name, - ), + }), }; errdefer comp.gpa().free(name); - self.base.init(comp, Id.Pointer, name); + self.base.init(comp, .Pointer, name); { - const held = await (async comp.ptr_type_table.acquire() catch unreachable); + const held = comp.ptr_type_table.acquire(); defer held.release(); _ = try held.value.put(&self.key, self); @@ -868,12 +823,12 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub async fn get(comp: *Compilation, key: Key) !*Array { + pub fn get(comp: *Compilation, key: Key) !*Array { key.elem_type.base.ref(); errdefer key.elem_type.base.deref(comp); { - const held = await (async comp.array_type_table.acquire() catch unreachable); + const held = comp.array_type_table.acquire(); defer held.release(); if (held.value.get(&key)) |entry| { @@ -890,13 +845,13 @@ pub const Type = struct { }; errdefer comp.gpa().destroy(self); - const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name); + const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name }); errdefer comp.gpa().free(name); - self.base.init(comp, Id.Array, name); + self.base.init(comp, .Array, name); { - const held = await (async comp.array_type_table.acquire() catch unreachable); + const held = comp.array_type_table.acquire(); defer held.release(); _ = try held.value.put(&self.key, self); @@ -1046,14 +1001,6 @@ pub const Type = struct { } }; - pub const ArgTuple = struct { - base: Type, - - pub fn destroy(self: *ArgTuple, comp: *Compilation) void { - comp.gpa().destroy(self); - } - }; - pub const Opaque = struct { base: Type, @@ -1066,51 +1013,63 @@ pub const Type = struct { } }; - pub const Promise = struct { + pub const Frame = struct { base: Type, - pub fn destroy(self: *Promise, comp: *Compilation) void { + pub fn destroy(self: *Frame, comp: *Compilation) void { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { + pub fn getLlvmType(self: *Frame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { + @panic("TODO"); + } + }; + + pub const AnyFrame = struct { + base: Type, + + pub fn destroy(self: *AnyFrame, comp: *Compilation) void { + comp.gpa().destroy(self); + } + + pub fn getLlvmType(self: *AnyFrame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; }; fn hashAny(x: var, comptime seed: u64) u32 { - switch (@typeInfo(@typeOf(x))) { - builtin.TypeId.Int => |info| { + switch (@typeInfo(@TypeOf(x))) { + .Int => |info| { comptime var rng = comptime std.rand.DefaultPrng.init(seed); const unsigned_x = @bitCast(@IntType(false, info.bits), x); if (info.bits <= 32) { - return u32(unsigned_x) *% comptime rng.random.scalar(u32); + return @as(u32, unsigned_x) *% comptime rng.random.scalar(u32); } else { - return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x))); + return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@TypeOf(unsigned_x))); } }, - builtin.TypeId.Pointer => |info| { + .Pointer => |info| { switch (info.size) { - builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed), - builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"), - builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"), - builtin.TypeInfo.Pointer.Size.C => unreachable, + .One => return hashAny(@ptrToInt(x), seed), + .Many => @compileError("implement hash function"), + .Slice => @compileError("implement hash function"), + .C => unreachable, } }, - builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), - builtin.TypeId.Bool => { + .Enum => return hashAny(@enumToInt(x), seed), + .Bool => { comptime var rng = comptime std.rand.DefaultPrng.init(seed); const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) }; return vals[@boolToInt(x)]; }, - builtin.TypeId.Optional => { + .Optional => { if (x) |non_opt| { return hashAny(non_opt, seed); } else { - return hashAny(u32(1), seed); + return hashAny(@as(u32, 1), seed); } }, - else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))), + else => @compileError("implement hash function for " ++ @typeName(@TypeOf(x))), } } diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig new file mode 100644 index 000000000..85e2fc0e7 --- /dev/null +++ b/src-self-hosted/util.zig @@ -0,0 +1,212 @@ +const std = @import("std"); +const Target = std.Target; +const llvm = @import("llvm.zig"); + +pub const FloatAbi = enum { + Hard, + Soft, + SoftFp, +}; + +/// TODO expose the arch and subarch separately +pub fn isArmOrThumb(self: Target) bool { + return switch (self.getArch()) { + .arm, + .armeb, + .aarch64, + .aarch64_be, + .thumb, + .thumbeb, + => true, + else => false, + }; +} + +pub fn getFloatAbi(self: Target) FloatAbi { + return switch (self.getAbi()) { + .gnueabihf, + .eabihf, + .musleabihf, + => .Hard, + else => .Soft, + }; +} + +pub fn getObjectFormat(target: Target) Target.ObjectFormat { + switch (target) { + .Native => return @import("builtin").object_format, + .Cross => blk: { + if (target.isWindows() or target.isUefi()) { + return .coff; + } else if (target.isDarwin()) { + return .macho; + } + if (target.isWasm()) { + return .wasm; + } + return .elf; + }, + } +} + +pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { + const env = self.getAbi(); + const arch = self.getArch(); + const os = self.getOs(); + switch (os) { + .freebsd => { + return "/libexec/ld-elf.so.1"; + }, + .linux => { + switch (env) { + .android => { + if (self.getArchPtrBitWidth() == 64) { + return "/system/bin/linker64"; + } else { + return "/system/bin/linker"; + } + }, + .gnux32 => { + if (arch == .x86_64) { + return "/libx32/ld-linux-x32.so.2"; + } + }, + .musl, + .musleabi, + .musleabihf, + => { + if (arch == .x86_64) { + return "/lib/ld-musl-x86_64.so.1"; + } + }, + else => {}, + } + switch (arch) { + .i386, + .sparc, + .sparcel, + => return "/lib/ld-linux.so.2", + + .aarch64 => return "/lib/ld-linux-aarch64.so.1", + + .aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", + + .arm, + .thumb, + => return switch (getFloatAbi(self)) { + .Hard => return "/lib/ld-linux-armhf.so.3", + else => return "/lib/ld-linux.so.3", + }, + + .armeb, + .thumbeb, + => return switch (getFloatAbi(self)) { + .Hard => return "/lib/ld-linux-armhf.so.3", + else => return "/lib/ld-linux.so.3", + }, + + .mips, + .mipsel, + .mips64, + .mips64el, + => return null, + + .powerpc => return "/lib/ld.so.1", + .powerpc64 => return "/lib64/ld64.so.2", + .powerpc64le => return "/lib64/ld64.so.2", + .s390x => return "/lib64/ld64.so.1", + .sparcv9 => return "/lib64/ld-linux.so.2", + .x86_64 => return "/lib64/ld-linux-x86-64.so.2", + + .arc, + .avr, + .bpfel, + .bpfeb, + .hexagon, + .msp430, + .r600, + .amdgcn, + .riscv32, + .riscv64, + .tce, + .tcele, + .xcore, + .nvptx, + .nvptx64, + .le32, + .le64, + .amdil, + .amdil64, + .hsail, + .hsail64, + .spir, + .spir64, + .kalimba, + .shave, + .lanai, + .wasm32, + .wasm64, + .renderscript32, + .renderscript64, + .aarch64_32, + => return null, + } + }, + else => return null, + } +} + +pub fn getDarwinArchString(self: Target) [:0]const u8 { + const arch = self.getArch(); + switch (arch) { + .aarch64 => return "arm64", + .thumb, + .arm, + => return "arm", + .powerpc => return "ppc", + .powerpc64 => return "ppc64", + .powerpc64le => return "ppc64le", + // @tagName should be able to return sentinel terminated slice + else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch), + } +} + +pub fn llvmTargetFromTriple(triple: std.Buffer) !*llvm.Target { + var result: *llvm.Target = undefined; + var err_msg: [*:0]u8 = undefined; + if (llvm.GetTargetFromTriple(triple.toSlice(), &result, &err_msg) != 0) { + std.debug.warn("triple: {s} error: {s}\n", .{ triple.toSlice(), err_msg }); + return error.UnsupportedTarget; + } + return result; +} + +pub fn initializeAllTargets() void { + llvm.InitializeAllTargets(); + llvm.InitializeAllTargetInfos(); + llvm.InitializeAllTargetMCs(); + llvm.InitializeAllAsmPrinters(); + llvm.InitializeAllAsmParsers(); +} + +pub fn getTriple(allocator: *std.mem.Allocator, self: std.Target) !std.Buffer { + var result = try std.Buffer.initSize(allocator, 0); + errdefer result.deinit(); + + // LLVM WebAssembly output support requires the target to be activated at + // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. + // + // LLVM determines the output format based on the abi suffix, + // defaulting to an object based on the architecture. The default format in + // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to + // explicitly set this ourself in order for it to work. + // + // This is fixed in LLVM 7 and you will be able to get wasm output by + // using the target triple `wasm32-unknown-unknown-unknown`. + const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi()); + + var out = &std.io.BufferOutStream.init(&result).stream; + try out.print("{}-unknown-{}-{}", .{ @tagName(self.getArch()), @tagName(self.getOs()), env_name }); + + return result; +} diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 6908307c5..03c0db0c3 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const ObjectFile = @import("codegen.zig").ObjectFile; @@ -24,15 +23,15 @@ pub const Value = struct { if (base.ref_count.decr() == 1) { base.typ.base.deref(comp); switch (base.id) { - Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), - Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), - Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp), - Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), - Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), - Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), - Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), - Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), - Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp), + .Type => @fieldParentPtr(Type, "base", base).destroy(comp), + .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), + .FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp), + .Void => @fieldParentPtr(Void, "base", base).destroy(comp), + .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), + .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), + .Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), + .Int => @fieldParentPtr(Int, "base", base).destroy(comp), + .Array => @fieldParentPtr(Array, "base", base).destroy(comp), } } } @@ -54,20 +53,20 @@ pub const Value = struct { } pub fn dump(base: *const Value) void { - std.debug.warn("{}", @tagName(base.id)); + std.debug.warn("{}", .{@tagName(base.id)}); } pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) { switch (base.id) { - Id.Type => unreachable, - Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile), - Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), - Id.Void => return null, - Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), - Id.NoReturn => unreachable, - Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile), - Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile), - Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile), + .Type => unreachable, + .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile), + .FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), + .Void => return null, + .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), + .NoReturn => unreachable, + .Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile), + .Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile), + .Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile), } } @@ -83,15 +82,15 @@ pub const Value = struct { pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) { switch (base.id) { - Id.Type => unreachable, - Id.Fn => unreachable, - Id.FnProto => unreachable, - Id.Void => unreachable, - Id.Bool => unreachable, - Id.NoReturn => unreachable, - Id.Ptr => unreachable, - Id.Array => unreachable, - Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base, + .Type => unreachable, + .Fn => unreachable, + .FnProto => unreachable, + .Void => unreachable, + .Bool => unreachable, + .NoReturn => unreachable, + .Ptr => unreachable, + .Array => unreachable, + .Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base, } } @@ -138,7 +137,7 @@ pub const Value = struct { const self = try comp.gpa().create(FnProto); self.* = FnProto{ .base = Value{ - .id = Value.Id.FnProto, + .id = .FnProto, .typ = &fn_type.base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -157,7 +156,7 @@ pub const Value = struct { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - self.symbol_name.ptr(), + self.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -202,7 +201,7 @@ pub const Value = struct { const self = try comp.gpa().create(Fn); self.* = Fn{ .base = Value{ - .id = Value.Id.Fn, + .id = .Fn, .typ = &fn_type.base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -242,7 +241,7 @@ pub const Value = struct { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - self.symbol_name.ptr(), + self.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -335,7 +334,7 @@ pub const Value = struct { field_index: usize, }; - pub async fn createArrayElemPtr( + pub fn createArrayElemPtr( comp: *Compilation, array_val: *Array, mut: Type.Pointer.Mut, @@ -346,20 +345,20 @@ pub const Value = struct { errdefer array_val.base.deref(comp); const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type; - const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{ + const ptr_type = try Type.Pointer.get(comp, Type.Pointer.Key{ .child_type = elem_type, .mut = mut, .vol = Type.Pointer.Vol.Non, .size = size, - .alignment = Type.Pointer.Align.Abi, - }) catch unreachable); + .alignment = .Abi, + }); var ptr_type_consumed = false; errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); const self = try comp.gpa().create(Value.Ptr); self.* = Value.Ptr{ .base = Value{ - .id = Value.Id.Ptr, + .id = .Ptr, .typ = &ptr_type.base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -385,25 +384,25 @@ pub const Value = struct { const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context); // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr switch (self.special) { - Special.Scalar => |scalar| @panic("TODO"), - Special.BaseArray => |base_array| { + .Scalar => |scalar| @panic("TODO"), + .BaseArray => |base_array| { // TODO put this in one .o file only, and after that, generate extern references to it const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?; const ptr_bit_count = ofile.comp.target_ptr_bits; const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory; - const indices = [_]*llvm.Value{ + var indices = [_]*llvm.Value{ llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory, llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory, }; return llvm.ConstInBoundsGEP( array_llvm_value, - &indices, + @ptrCast([*]*llvm.Value, &indices), @intCast(c_uint, indices.len), ) orelse return error.OutOfMemory; }, - Special.BaseStruct => |base_struct| @panic("TODO"), - Special.HardCodedAddr => |addr| @panic("TODO"), - Special.Discard => unreachable, + .BaseStruct => |base_struct| @panic("TODO"), + .HardCodedAddr => |addr| @panic("TODO"), + .Discard => unreachable, } } }; @@ -424,20 +423,20 @@ pub const Value = struct { }; /// Takes ownership of buffer - pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { + pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { const u8_type = Type.Int.get_u8(comp); defer u8_type.base.base.deref(comp); - const array_type = try await (async Type.Array.get(comp, Type.Array.Key{ + const array_type = try Type.Array.get(comp, Type.Array.Key{ .elem_type = &u8_type.base, .len = buffer.len, - }) catch unreachable); + }); errdefer array_type.base.base.deref(comp); const self = try comp.gpa().create(Value.Array); self.* = Value.Array{ .base = Value{ - .id = Value.Id.Array, + .id = .Array, .typ = &array_type.base, .ref_count = std.atomic.Int(usize).init(1), }, @@ -450,22 +449,22 @@ pub const Value = struct { pub fn destroy(self: *Array, comp: *Compilation) void { switch (self.special) { - Special.Undefined => {}, - Special.OwnedBuffer => |buf| { + .Undefined => {}, + .OwnedBuffer => |buf| { comp.gpa().free(buf); }, - Special.Explicit => {}, + .Explicit => {}, } comp.gpa().destroy(self); } pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value { switch (self.special) { - Special.Undefined => { + .Undefined => { const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); return llvm.GetUndef(llvm_type); }, - Special.OwnedBuffer => |buf| { + .OwnedBuffer => |buf| { const dont_null_terminate = 1; const llvm_str_init = llvm.ConstStringInContext( ofile.context, @@ -474,7 +473,7 @@ pub const Value = struct { dont_null_terminate, ) orelse return error.OutOfMemory; const str_init_type = llvm.TypeOf(llvm_str_init); - const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory; + const global = llvm.AddGlobal(ofile.module, str_init_type, "") orelse return error.OutOfMemory; llvm.SetInitializer(global, llvm_str_init); llvm.SetLinkage(global, llvm.PrivateLinkage); llvm.SetGlobalConstant(global, 1); @@ -482,7 +481,7 @@ pub const Value = struct { llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type)); return global; }, - Special.Explicit => @panic("TODO"), + .Explicit => @panic("TODO"), } //{ @@ -517,7 +516,7 @@ pub const Value = struct { const self = try comp.gpa().create(Value.Int); self.* = Value.Int{ .base = Value{ - .id = Value.Id.Int, + .id = .Int, .typ = typ, .ref_count = std.atomic.Int(usize).init(1), }, @@ -536,7 +535,7 @@ pub const Value = struct { pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value { switch (self.base.typ.id) { - Type.Id.Int => { + .Int => { const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context); if (self.big_int.len() == 0) { return llvm.ConstNull(type_ref); @@ -554,7 +553,7 @@ pub const Value = struct { }; return if (self.big_int.isPositive()) unsigned_val else llvm.ConstNeg(unsigned_val); }, - Type.Id.ComptimeInt => unreachable, + .ComptimeInt => unreachable, else => unreachable, } } @@ -566,7 +565,7 @@ pub const Value = struct { const new = try comp.gpa().create(Value.Int); new.* = Value.Int{ .base = Value{ - .id = Value.Id.Int, + .id = .Int, .typ = old.base.typ, .ref_count = std.atomic.Int(usize).init(1), }, diff --git a/src/all_types.hpp b/src/all_types.hpp index 695f22ac9..0fed73f7b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -32,7 +32,7 @@ struct ErrorTableEntry; struct BuiltinFnEntry; struct TypeStructField; struct CodeGen; -struct ConstExprValue; +struct ZigValue; struct IrInstruction; struct IrInstructionCast; struct IrInstructionAllocaGen; @@ -48,6 +48,7 @@ struct ResultLoc; struct ResultLocPeer; struct ResultLocPeerParent; struct ResultLocBitCast; +struct ResultLocCast; struct ResultLocReturn; enum PtrLen { @@ -56,6 +57,23 @@ enum PtrLen { PtrLenC, }; +enum CallingConvention { + CallingConventionUnspecified, + CallingConventionC, + CallingConventionCold, + CallingConventionNaked, + CallingConventionAsync, + CallingConventionInterrupt, + CallingConventionSignal, + CallingConventionStdcall, + CallingConventionFastcall, + CallingConventionVectorcall, + CallingConventionThiscall, + CallingConventionAPCS, + CallingConventionAAPCS, + CallingConventionAAPCSVFP, +}; + // This one corresponds to the builtin.zig enum. enum BuiltinPtrSize { BuiltinPtrSizeOne, @@ -74,6 +92,7 @@ enum UndefAllowed { enum X64CABIClass { X64CABIClass_Unknown, X64CABIClass_MEMORY, + X64CABIClass_MEMORY_nobyval, X64CABIClass_INTEGER, X64CABIClass_SSE, }; @@ -99,6 +118,10 @@ struct IrExecutable { bool is_inline; bool is_generic_instantiation; bool need_err_code_spill; + + // This is a function for use in the debugger to print + // the source location. + void src(); }; enum OutType { @@ -124,38 +147,38 @@ struct ConstParent { union { struct { - ConstExprValue *array_val; + ZigValue *array_val; size_t elem_index; } p_array; struct { - ConstExprValue *struct_val; + ZigValue *struct_val; size_t field_index; } p_struct; struct { - ConstExprValue *err_union_val; + ZigValue *err_union_val; } p_err_union_code; struct { - ConstExprValue *err_union_val; + ZigValue *err_union_val; } p_err_union_payload; struct { - ConstExprValue *optional_val; + ZigValue *optional_val; } p_optional_payload; struct { - ConstExprValue *union_val; + ZigValue *union_val; } p_union; struct { - ConstExprValue *scalar_val; + ZigValue *scalar_val; } p_scalar; } data; }; struct ConstStructValue { - ConstExprValue *fields; + ZigValue **fields; }; struct ConstUnionValue { BigInt tag; - ConstExprValue *payload; + ZigValue *payload; }; enum ConstArraySpecial { @@ -168,7 +191,7 @@ struct ConstArrayValue { ConstArraySpecial special; union { struct { - ConstExprValue *elements; + ZigValue *elements; } s_none; Buf *s_buf; } data; @@ -229,27 +252,24 @@ struct ConstPtrValue { union { struct { - ConstExprValue *pointee; + ZigValue *pointee; } ref; struct { - ConstExprValue *array_val; + ZigValue *array_val; size_t elem_index; - // This helps us preserve the null byte when performing compile-time - // concatenation on C strings. - bool is_cstr; } base_array; struct { - ConstExprValue *struct_val; + ZigValue *struct_val; size_t field_index; } base_struct; struct { - ConstExprValue *err_union_val; + ZigValue *err_union_val; } base_err_union_code; struct { - ConstExprValue *err_union_val; + ZigValue *err_union_val; } base_err_union_payload; struct { - ConstExprValue *optional_val; + ZigValue *optional_val; } base_optional_payload; struct { uint64_t addr; @@ -261,8 +281,8 @@ struct ConstPtrValue { }; struct ConstErrValue { - ConstExprValue *error_set; - ConstExprValue *payload; + ZigValue *error_set; + ZigValue *payload; }; struct ConstBoundFnValue { @@ -310,12 +330,6 @@ struct RuntimeHintSlice { uint64_t len; }; -struct ConstGlobalRefs { - LLVMValueRef llvm_value; - LLVMValueRef llvm_global; - uint32_t align; -}; - enum LazyValueId { LazyValueIdInvalid, LazyValueIdAlignOf, @@ -325,6 +339,7 @@ enum LazyValueId { LazyValueIdSliceType, LazyValueIdFnType, LazyValueIdErrUnionType, + LazyValueIdArrayType, }; struct LazyValue { @@ -349,6 +364,7 @@ struct LazyValueSliceType { LazyValue base; IrAnalyze *ira; + IrInstruction *sentinel; // can be null IrInstruction *elem_type; IrInstruction *align_inst; // can be null @@ -357,10 +373,20 @@ struct LazyValueSliceType { bool is_allowzero; }; +struct LazyValueArrayType { + LazyValue base; + + IrAnalyze *ira; + IrInstruction *sentinel; // can be null + IrInstruction *elem_type; + uint64_t length; +}; + struct LazyValuePtrType { LazyValue base; IrAnalyze *ira; + IrInstruction *sentinel; // can be null IrInstruction *elem_type; IrInstruction *align_inst; // can be null @@ -389,6 +415,7 @@ struct LazyValueFnType { IrInstruction *align_inst; // can be null IrInstruction *return_type; + CallingConvention cc; bool is_generic; }; @@ -398,15 +425,21 @@ struct LazyValueErrUnionType { IrAnalyze *ira; IrInstruction *err_set_type; IrInstruction *payload_type; + Buf *type_name; }; -struct ConstExprValue { +struct ZigValue { ZigType *type; ConstValSpecial special; + uint32_t llvm_align; ConstParent parent; - ConstGlobalRefs *global_refs; + LLVMValueRef llvm_value; + LLVMValueRef llvm_global; union { + // populated if special == ConstValSpecialLazy + LazyValue *x_lazy; + // populated if special == ConstValSpecialStatic BigInt x_bigint; BigFloat x_bigfloat; @@ -417,7 +450,7 @@ struct ConstExprValue { bool x_bool; ConstBoundFnValue x_bound_fn; ZigType *x_type; - ConstExprValue *x_optional; + ZigValue *x_optional; ConstErrValue x_err_union; ErrorTableEntry *x_err_set; BigInt x_enum_tag; @@ -427,7 +460,6 @@ struct ConstExprValue { ConstPtrValue x_ptr; ConstArgTuple x_arg_tuple; Buf *x_enum_literal; - LazyValue *x_lazy; // populated if special == ConstValSpecialRuntime RuntimeHintErrorUnion rh_error_union; @@ -437,8 +469,8 @@ struct ConstExprValue { } data; // uncomment these to find bugs. can't leave them uncommented because of a gcc-9 warning - //ConstExprValue(const ConstExprValue &other) = delete; // plz zero initialize with {} - //ConstExprValue& operator= (const ConstExprValue &other) = delete; // use copy_const_val + //ZigValue(const ZigValue &other) = delete; // plz zero initialize with {} + //ZigValue& operator= (const ZigValue &other) = delete; // use copy_const_val }; enum ReturnKnowledge { @@ -494,7 +526,6 @@ struct TldVar { ZigVar *var; Buf *extern_lib_name; - Buf *section_name; bool analyzing_type; // flag to detect dependency loops }; @@ -519,7 +550,7 @@ struct TldCompTime { struct TldUsingNamespace { Tld base; - ConstExprValue *using_namespace_value; + ZigValue *using_namespace_value; }; struct TypeEnumField { @@ -532,7 +563,7 @@ struct TypeEnumField { struct TypeUnionField { Buf *name; ZigType *type_entry; // available after ResolveStatusSizeKnown - ConstExprValue *type_val; // available after ResolveStatusZeroBitsKnown + ZigValue *type_val; // available after ResolveStatusZeroBitsKnown TypeEnumField *enum_field; AstNode *decl_node; uint32_t gen_index; @@ -589,20 +620,13 @@ enum NodeType { NodeTypeIfErrorExpr, NodeTypeIfOptional, NodeTypeErrorSetDecl, + NodeTypeErrorSetField, NodeTypeResume, NodeTypeAwaitExpr, NodeTypeSuspend, NodeTypeAnyFrameType, NodeTypeEnumLiteral, -}; - -enum CallingConvention { - CallingConventionUnspecified, - CallingConventionC, - CallingConventionCold, - CallingConventionNaked, - CallingConventionStdcall, - CallingConventionAsync, + NodeTypeVarFieldType, }; enum FnInline { @@ -612,16 +636,10 @@ enum FnInline { }; struct AstNodeFnProto { - VisibMod visib_mod; Buf *name; ZigList params; AstNode *return_type; Token *return_var_token; - bool is_var_args; - bool is_extern; - bool is_export; - FnInline fn_inline; - CallingConvention cc; AstNode *fn_def_node; // populated if this is an extern declaration Buf *lib_name; @@ -629,8 +647,18 @@ struct AstNodeFnProto { AstNode *align_expr; // populated if the "section(S)" is present AstNode *section_expr; + // populated if the "callconv(S)" is present + AstNode *callconv_expr; + Buf doc_comments; + FnInline fn_inline; + bool is_async; + + VisibMod visib_mod; bool auto_err_set; + bool is_var_args; + bool is_extern; + bool is_export; }; struct AstNodeFnDef { @@ -642,6 +670,7 @@ struct AstNodeParamDecl { Buf *name; AstNode *type; Token *var_token; + Buf doc_comments; bool is_noalias; bool is_comptime; bool is_var_args; @@ -684,6 +713,7 @@ struct AstNodeVariableDeclaration { // populated if the "section(S)" is present AstNode *section_expr; Token *threadlocal_tok; + Buf doc_comments; VisibMod visib_mod; bool is_const; @@ -759,10 +789,18 @@ struct AstNodeUnwrapOptional { AstNode *expr; }; +// Must be synchronized with std.builtin.CallOptions.Modifier enum CallModifier { CallModifierNone, CallModifierAsync, + CallModifierNeverTail, + CallModifierNeverInline, CallModifierNoAsync, + CallModifierAlwaysTail, + CallModifierAlwaysInline, + CallModifierCompileTime, + + // These are additional tags in the compiler, but not exposed in the std lib. CallModifierBuiltin, }; @@ -782,6 +820,7 @@ struct AstNodeSliceExpr { AstNode *array_ref_expr; AstNode *start; AstNode *end; + AstNode *sentinel; // can be null }; struct AstNodeFieldAccessExpr { @@ -810,6 +849,7 @@ struct AstNodePrefixOpExpr { struct AstNodePointerType { Token *star_token; + AstNode *sentinel; AstNode *align_expr; BigInt *bit_offset_start; BigInt *host_int_bytes; @@ -820,11 +860,13 @@ struct AstNodePointerType { }; struct AstNodeInferredArrayType { + AstNode *sentinel; // can be null AstNode *child_type; }; struct AstNodeArrayType { AstNode *size; + AstNode *sentinel; AstNode *child_type; AstNode *align_expr; Token *allow_zero_token; @@ -938,7 +980,7 @@ struct AsmToken { struct AstNodeAsmExpr { Token *volatile_token; - Token *asm_template; + AstNode *asm_template; ZigList output_list; ZigList input_list; ZigList clobber_list; @@ -957,30 +999,39 @@ enum ContainerLayout { }; struct AstNodeContainerDecl { - ContainerKind kind; + AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T)) ZigList fields; ZigList decls; + Buf doc_comments; + + ContainerKind kind; ContainerLayout layout; - AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T)) + bool auto_enum, is_root; // union(enum) }; +struct AstNodeErrorSetField { + Buf doc_comments; + AstNode *field_name; +}; + struct AstNodeErrorSetDecl { + // Each AstNode could be AstNodeErrorSetField or just AstNodeSymbolExpr to save memory ZigList decls; }; struct AstNodeStructField { - VisibMod visib_mod; Buf *name; AstNode *type; AstNode *value; // populated if the "align(A)" is present AstNode *align_expr; + Buf doc_comments; + Token *comptime_token; }; struct AstNodeStringLiteral { Buf *buf; - bool c; }; struct AstNodeCharLiteral { @@ -1126,6 +1177,7 @@ struct AstNode { AstNodeInferredArrayType inferred_array_type; AstNodeErrorType error_type; AstNodeErrorSetDecl err_set_decl; + AstNodeErrorSetField err_set_field; AstNodeResumeExpr resume_expr; AstNodeAwaitExpr await_expr; AstNodeSuspend suspend; @@ -1147,7 +1199,7 @@ struct FnTypeParamInfo { struct GenericFnTypeId { CodeGen *codegen; ZigFn *fn_entry; - ConstExprValue *params; + ZigValue *params; size_t param_count; }; @@ -1167,13 +1219,39 @@ struct FnTypeId { uint32_t fn_type_id_hash(FnTypeId*); bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); +static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX; +static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1; + +struct InferredStructField { + ZigType *inferred_struct_type; + Buf *field_name; +}; + struct ZigTypePointer { ZigType *child_type; ZigType *slice_parent; + + // Anonymous struct literal syntax uses this when the result location has + // no type in it. This field is null if this pointer does not refer to + // a field of a currently-being-inferred struct type. + // When this is non-null, the pointer is pointing to the base of the inferred + // struct. + InferredStructField *inferred_struct_field; + + // This can be null. If it is non-null, it means the pointer is terminated by this + // sentinel value. This is most commonly used for C-style strings, with a 0 byte + // to specify the length of the memory pointed to. + ZigValue *sentinel; + PtrLen ptr_len; uint32_t explicit_alignment; // 0 means use ABI alignment + uint32_t bit_offset_in_host; - uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned + // size of host integer. 0 means no host integer; this field is aligned + // when vector_index != VECTOR_INDEX_NONE this is the len of the containing vector + uint32_t host_int_bytes; + + uint32_t vector_index; // see the VECTOR_INDEX_* constants bool is_const; bool is_volatile; bool allow_zero; @@ -1192,25 +1270,28 @@ struct ZigTypeFloat { struct ZigTypeArray { ZigType *child_type; uint64_t len; + ZigValue *sentinel; }; struct TypeStructField { Buf *name; ZigType *type_entry; // available after ResolveStatusSizeKnown - ConstExprValue *type_val; // available after ResolveStatusZeroBitsKnown + ZigValue *type_val; // available after ResolveStatusZeroBitsKnown size_t src_index; size_t gen_index; size_t offset; // byte offset from beginning of struct AstNode *decl_node; - ConstExprValue *init_val; // null and then memoized + ZigValue *init_val; // null and then memoized uint32_t bit_offset_in_host; // offset from the memory at gen_index uint32_t host_int_bytes; // size of host integer uint32_t align; + bool is_comptime; }; enum ResolveStatus { ResolveStatusUnstarted, ResolveStatusInvalid, + ResolveStatusBeingInferred, ResolveStatusZeroBitsKnown, ResolveStatusAlignmentKnown, ResolveStatusSizeKnown, @@ -1238,9 +1319,16 @@ struct RootStruct { ZigLLVMDIFile *di_file; }; +enum StructSpecial { + StructSpecialNone, + StructSpecialSlice, + StructSpecialInferredTuple, + StructSpecialInferredStruct, +}; + struct ZigTypeStruct { AstNode *decl_node; - TypeStructField *fields; + TypeStructField **fields; ScopeDecls *decls_scope; HashMap fields_by_name; RootStruct *root_struct; @@ -1253,7 +1341,7 @@ struct ZigTypeStruct { ContainerLayout layout; ResolveStatus resolve_status; - bool is_slice; + StructSpecial special; // whether any of the fields require comptime // known after ResolveStatusZeroBitsKnown bool requires_comptime; @@ -1274,9 +1362,10 @@ struct ZigTypeErrorUnion { }; struct ZigTypeErrorSet { - uint32_t err_count; ErrorTableEntry **errors; ZigFn *infer_fn; + uint32_t err_count; + bool incomplete; }; struct ZigTypeEnum { @@ -1300,6 +1389,21 @@ struct ZigTypeEnum { uint32_t type_ptr_hash(const ZigType *ptr); bool type_ptr_eql(const ZigType *a, const ZigType *b); +uint32_t pkg_ptr_hash(const ZigPackage *ptr); +bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b); + +uint32_t tld_ptr_hash(const Tld *ptr); +bool tld_ptr_eql(const Tld *a, const Tld *b); + +uint32_t node_ptr_hash(const AstNode *ptr); +bool node_ptr_eql(const AstNode *a, const AstNode *b); + +uint32_t fn_ptr_hash(const ZigFn *ptr); +bool fn_ptr_eql(const ZigFn *a, const ZigFn *b); + +uint32_t err_ptr_hash(const ErrorTableEntry *ptr); +bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b); + struct ZigTypeUnion { AstNode *decl_node; TypeUnionField *fields; @@ -1378,7 +1482,6 @@ enum ZigTypeId { ZigTypeIdUnion, ZigTypeIdFn, ZigTypeIdBoundFn, - ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdFnFrame, ZigTypeIdAnyFrame, @@ -1446,7 +1549,7 @@ struct ZigType { ZigType *any_frame_parent; // If we generate a constant name value for this type, we memoize it here. // The type of this is array - ConstExprValue *cached_const_name_val; + ZigValue *cached_const_name_val; OnePossibleValue one_possible_value; // Known after ResolveStatusAlignmentKnown. @@ -1496,7 +1599,7 @@ struct ZigFn { // in the case of async functions this is the implicit return type according to the // zig source code, not according to zig ir ZigType *src_implicit_return_type; - IrExecutable ir_executable; + IrExecutable *ir_executable; IrExecutable analyzed_executable; size_t prealloc_bbc; size_t prealloc_backward_branch_quota; @@ -1587,7 +1690,7 @@ enum BuiltinFnId { BuiltinFnIdCos, BuiltinFnIdExp, BuiltinFnIdExp2, - BuiltinFnIdLn, + BuiltinFnIdLog, BuiltinFnIdLog2, BuiltinFnIdLog10, BuiltinFnIdFabs, @@ -1627,8 +1730,6 @@ enum BuiltinFnId { BuiltinFnIdFieldParentPtr, BuiltinFnIdByteOffsetOf, BuiltinFnIdBitOffsetOf, - BuiltinFnIdInlineCall, - BuiltinFnIdNoInlineCall, BuiltinFnIdNewStackCall, BuiltinFnIdAsyncCall, BuiltinFnIdTypeId, @@ -1644,12 +1745,15 @@ enum BuiltinFnId { BuiltinFnIdErrorReturnTrace, BuiltinFnIdAtomicRmw, BuiltinFnIdAtomicLoad, + BuiltinFnIdAtomicStore, BuiltinFnIdHasDecl, BuiltinFnIdUnionInit, BuiltinFnIdFrameAddress, BuiltinFnIdFrameType, BuiltinFnIdFrameHandle, BuiltinFnIdFrameSize, + BuiltinFnIdAs, + BuiltinFnIdCall, }; struct BuiltinFnEntry { @@ -1684,6 +1788,8 @@ enum PanicMsgId { PanicMsgIdFrameTooSmall, PanicMsgIdResumedFnPendingAwait, PanicMsgIdBadNoAsyncCall, + PanicMsgIdResumeNotSuspendedFn, + PanicMsgIdBadSentinel, PanicMsgIdCount, }; @@ -1696,18 +1802,26 @@ struct TypeId { union { struct { + CodeGen *codegen; ZigType *child_type; + InferredStructField *inferred_struct_field; + ZigValue *sentinel; PtrLen ptr_len; uint32_t alignment; + uint32_t bit_offset_in_host; uint32_t host_int_bytes; + + uint32_t vector_index; bool is_const; bool is_volatile; bool allow_zero; } pointer; struct { + CodeGen *codegen; ZigType *child_type; uint64_t size; + ZigValue *sentinel; } array; struct { bool is_signed; @@ -1826,6 +1940,12 @@ enum WantStackCheck { WantStackCheckEnabled, }; +enum WantCSanitize { + WantCSanitizeAuto, + WantCSanitizeDisabled, + WantCSanitizeEnabled, +}; + struct CFile { ZigList args; const char *source_path; @@ -1855,6 +1975,7 @@ struct CodeGen { size_t cur_resume_block_count; LLVMValueRef cur_err_ret_trace_val_arg; LLVMValueRef cur_err_ret_trace_val_stack; + LLVMValueRef cur_bad_not_suspended_index; LLVMValueRef memcpy_fn_val; LLVMValueRef memset_fn_val; LLVMValueRef trap_fn_val; @@ -1879,12 +2000,13 @@ struct CodeGen { HashMap fn_type_table; HashMap error_table; HashMap generic_table; - HashMap memoized_fn_eval_table; + HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap external_prototypes; - HashMap string_literals_table; - HashMap type_info_cache; + HashMap string_literals_table; + HashMap type_info_cache; + HashMap one_possible_values; ZigList resolve_queue; size_t resolve_queue_index; @@ -1897,13 +2019,13 @@ struct CodeGen { ZigList type_resolve_stack; ZigPackage *std_package; - ZigPackage *panic_package; ZigPackage *test_runner_package; ZigPackage *compile_var_package; + ZigPackage *root_pkg; // @import("root") + ZigPackage *main_pkg; // usually same as root_pkg, except for `zig test` ZigType *compile_var_import; ZigType *root_import; ZigType *start_import; - ZigType *test_runner_import; struct { ZigType *entry_bool; @@ -1935,11 +2057,24 @@ struct CodeGen { ZigType *entry_null; ZigType *entry_var; ZigType *entry_global_error_set; - ZigType *entry_arg_tuple; ZigType *entry_enum_literal; ZigType *entry_any_frame; } builtin_types; + struct Intern { + ZigValue x_undefined; + ZigValue x_void; + ZigValue x_null; + ZigValue x_unreachable; + ZigValue zero_byte; + + ZigValue *for_undefined(); + ZigValue *for_void(); + ZigValue *for_null(); + ZigValue *for_unreachable(); + ZigValue *for_zero_byte(); + } intern; + ZigType *align_amt_type; ZigType *stack_trace_type; ZigType *err_tag_type; @@ -1962,8 +2097,7 @@ struct CodeGen { IrInstruction *invalid_instruction; IrInstruction *unreach_instruction; - ConstExprValue const_void_val; - ConstExprValue panic_msg_vals[PanicMsgIdCount]; + ZigValue panic_msg_vals[PanicMsgIdCount]; // The function definitions this module includes. ZigList fn_defs; @@ -1971,14 +2105,16 @@ struct CodeGen { ZigList global_vars; ZigFn *cur_fn; - ZigFn *main_fn; ZigFn *panic_fn; - TldFn *panic_tld_fn; ZigFn *largest_frame_fn; + Stage2ProgressNode *main_progress_node; + Stage2ProgressNode *sub_progress_node; + WantPIC want_pic; WantStackCheck want_stack_check; + WantCSanitize want_sanitize_c; CacheHash cache_hash; ErrColor err_color; uint32_t next_unresolved_index; @@ -1989,13 +2125,12 @@ struct CodeGen { uint32_t target_abi_index; uint32_t target_oformat_index; bool is_big_endian; - bool have_pub_main; bool have_c_main; bool have_winmain; bool have_winmain_crt_startup; bool have_dllmain_crt_startup; - bool have_pub_panic; bool have_err_ret_tracing; + bool link_eh_frame_hdr; bool c_want_stdint; bool c_want_stdbool; bool verbose_tokenize; @@ -2055,14 +2190,17 @@ struct CodeGen { bool have_pic; bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic bool have_stack_probing; + bool have_sanitize_c; bool function_sections; + bool enable_dump_analysis; + bool enable_doc_generation; + bool disable_bin_generation; Buf *mmacosx_version_min; Buf *mios_version_min; Buf *root_out_name; Buf *test_filter; Buf *test_name_prefix; - ZigPackage *root_package; Buf *zig_lib_dir; Buf *zig_std_dir; Buf *dynamic_linker_path; @@ -2077,7 +2215,7 @@ struct CodeGen { struct ZigVar { const char *name; - ConstExprValue *const_value; + ZigValue *const_value; ZigType *var_type; LLVMValueRef value_ref; IrInstruction *is_comptime; @@ -2092,6 +2230,8 @@ struct ZigVar { size_t mem_slot_index; IrExecutable *owner_exec; + Buf *section_name; + // In an inline loop, multiple variables may be created, // In this case, a reference to a variable should follow // this pointer to the redefined variable. @@ -2106,16 +2246,19 @@ struct ZigVar { bool src_is_const; bool gen_is_const; bool is_thread_local; + bool is_comptime_memoized; + bool is_comptime_memoized_value; }; struct ErrorTableEntry { Buf name; uint32_t value; AstNode *decl_node; + ErrorTableEntry *other; // null, or another error decl that was merged into this ZigType *set_with_only_this_in_it; // If we generate a constant error name value for this error, we memoize it here. // The type of this is array - ConstExprValue *cached_error_name_val; + ZigValue *cached_error_name_val; }; enum ScopeId { @@ -2273,7 +2416,7 @@ struct ScopeFnDef { ZigFn *fn_entry; }; -// This scope is created for a @typeOf. +// This scope is created for a @TypeOf. // All runtime side-effects are elided within it. // NodeTypeFnCallExpr struct ScopeTypeOf { @@ -2372,9 +2515,11 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, + IrInstructionIdMergeErrSets, IrInstructionIdLoadPtr, IrInstructionIdLoadPtrGen, IrInstructionIdStorePtr, + IrInstructionIdVectorStoreElem, IrInstructionIdFieldPtr, IrInstructionIdStructFieldPtr, IrInstructionIdUnionFieldPtr, @@ -2382,6 +2527,8 @@ enum IrInstructionId { IrInstructionIdVarPtr, IrInstructionIdReturnPtr, IrInstructionIdCallSrc, + IrInstructionIdCallSrcArgs, + IrInstructionIdCallExtra, IrInstructionIdCallGen, IrInstructionIdConst, IrInstructionIdReturn, @@ -2397,8 +2544,8 @@ enum IrInstructionId { IrInstructionIdArrayType, IrInstructionIdAnyFrameType, IrInstructionIdSliceType, - IrInstructionIdGlobalAsm, - IrInstructionIdAsm, + IrInstructionIdAsmSrc, + IrInstructionIdAsmGen, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, IrInstructionIdOptionalUnwrapPtr, @@ -2500,6 +2647,7 @@ enum IrInstructionId { IrInstructionIdErrorUnion, IrInstructionIdAtomicRmw, IrInstructionIdAtomicLoad, + IrInstructionIdAtomicStore, IrInstructionIdSaveErrRetAddr, IrInstructionIdAddImplicitReturnType, IrInstructionIdErrSetCast, @@ -2524,17 +2672,18 @@ enum IrInstructionId { IrInstructionIdResume, IrInstructionIdSpillBegin, IrInstructionIdSpillEnd, + IrInstructionIdVectorExtractElem, }; struct IrInstruction { Scope *scope; AstNode *source_node; - ConstExprValue value; - size_t debug_id; LLVMValueRef llvm_value; + ZigValue *value; + uint32_t debug_id; // if ref_count is zero and the instruction has no side effects, // the instruction can be omitted in codegen - size_t ref_count; + uint32_t ref_count; // When analyzing IR, instructions that point to this instruction in the "old ir" // can find the instruction that corresponds to this value in the "new ir" // with this child field. @@ -2547,6 +2696,10 @@ struct IrInstruction { IrInstructionId id; // true if this instruction was generated by zig and not from user code bool is_gen; + + // for debugging purposes, these are useful to call to inspect the instruction + void dump(); + void src(); }; struct IrInstructionDeclVarSrc { @@ -2678,7 +2831,6 @@ enum IrBinOp { IrBinOpRemMod, IrBinOpArrayCat, IrBinOpArrayMult, - IrBinOpMergeErrorSets, }; struct IrInstructionBinOp { @@ -2690,6 +2842,14 @@ struct IrInstructionBinOp { bool safety_check_on; }; +struct IrInstructionMergeErrSets { + IrInstruction base; + + IrInstruction *op1; + IrInstruction *op2; + Buf *type_name; +}; + struct IrInstructionLoadPtr { IrInstruction base; @@ -2711,6 +2871,14 @@ struct IrInstructionStorePtr { IrInstruction *value; }; +struct IrInstructionVectorStoreElem { + IrInstruction base; + + IrInstruction *vector_ptr; + IrInstruction *index; + IrInstruction *value; +}; + struct IrInstructionFieldPtr { IrInstruction base; @@ -2742,7 +2910,7 @@ struct IrInstructionElemPtr { IrInstruction *array_ptr; IrInstruction *elem_index; - IrInstruction *init_array_type; + AstNode *init_array_type_source_node; PtrLen ptr_len; bool safety_check_on; }; @@ -2768,15 +2936,37 @@ struct IrInstructionCallSrc { ZigFn *fn_entry; size_t arg_count; IrInstruction **args; + IrInstruction *ret_ptr; ResultLoc *result_loc; IrInstruction *new_stack; - FnInline fn_inline; CallModifier modifier; - bool is_async_call_builtin; - bool is_comptime; +}; + +// This is a pass1 instruction, used by @call when the args node is +// a tuple or struct literal. +struct IrInstructionCallSrcArgs { + IrInstruction base; + + IrInstruction *options; + IrInstruction *fn_ref; + IrInstruction **args_ptr; + size_t args_len; + ResultLoc *result_loc; +}; + +// This is a pass1 instruction, used by @call, when the args node +// is not a literal. +// `args` is expected to be either a struct or a tuple. +struct IrInstructionCallExtra { + IrInstruction base; + + IrInstruction *options; + IrInstruction *fn_ref; + IrInstruction *args; + ResultLoc *result_loc; }; struct IrInstructionCallGen { @@ -2790,7 +2980,6 @@ struct IrInstructionCallGen { IrInstruction *frame_result_loc; IrInstruction *new_stack; - FnInline fn_inline; CallModifier modifier; bool is_async_call_builtin; @@ -2839,11 +3028,11 @@ struct IrInstructionResizeSlice { struct IrInstructionContainerInitList { IrInstruction base; - IrInstruction *container_type; IrInstruction *elem_type; size_t item_count; IrInstruction **elem_result_loc_list; IrInstruction *result_loc; + AstNode *init_array_type_source_node; }; struct IrInstructionContainerInitFieldsField { @@ -2856,7 +3045,6 @@ struct IrInstructionContainerInitFieldsField { struct IrInstructionContainerInitFields { IrInstruction base; - IrInstruction *container_type; size_t field_count; IrInstructionContainerInitFieldsField *fields; IrInstruction *result_loc; @@ -2895,12 +3083,14 @@ struct IrInstructionArrayType { IrInstruction base; IrInstruction *size; + IrInstruction *sentinel; IrInstruction *child_type; }; struct IrInstructionPtrType { IrInstruction base; + IrInstruction *sentinel; IrInstruction *align_value; IrInstruction *child_type; uint32_t bit_offset_start; @@ -2920,6 +3110,7 @@ struct IrInstructionAnyFrameType { struct IrInstructionSliceType { IrInstruction base; + IrInstruction *sentinel; IrInstruction *align_value; IrInstruction *child_type; bool is_const; @@ -2927,13 +3118,19 @@ struct IrInstructionSliceType { bool is_allow_zero; }; -struct IrInstructionGlobalAsm { +struct IrInstructionAsmSrc { IrInstruction base; - Buf *asm_code; + IrInstruction *asm_template; + IrInstruction **input_list; + IrInstruction **output_types; + ZigVar **output_vars; + size_t return_count; + bool has_side_effects; + bool is_global; }; -struct IrInstructionAsm { +struct IrInstructionAsmGen { IrInstruction base; Buf *asm_template; @@ -3206,6 +3403,7 @@ struct IrInstructionSliceSrc { IrInstruction *ptr; IrInstruction *start; IrInstruction *end; + IrInstruction *sentinel; ResultLoc *result_loc; }; @@ -3364,6 +3562,7 @@ struct IrInstructionFnProto { IrInstruction **param_types; IrInstruction *align_value; + IrInstruction *callconv_value; IrInstruction *return_type; bool is_var_args; }; @@ -3390,6 +3589,13 @@ struct IrInstructionPtrCastGen { bool safety_check_on; }; +struct IrInstructionImplicitCast { + IrInstruction base; + + IrInstruction *operand; + ResultLocCast *result_loc_cast; +}; + struct IrInstructionBitCastSrc { IrInstruction base; @@ -3574,14 +3780,14 @@ struct IrInstructionArgType { IrInstruction *fn_type; IrInstruction *arg_index; + bool allow_var; }; struct IrInstructionExport { IrInstruction base; - IrInstruction *name; - IrInstruction *linkage; IrInstruction *target; + IrInstruction *options; }; struct IrInstructionErrorReturnTrace { @@ -3598,6 +3804,7 @@ struct IrInstructionErrorUnion { IrInstruction *err_set; IrInstruction *payload; + Buf *type_name; }; struct IrInstructionAtomicRmw { @@ -3621,6 +3828,16 @@ struct IrInstructionAtomicLoad { AtomicOrder resolved_ordering; }; +struct IrInstructionAtomicStore { + IrInstruction base; + + IrInstruction *operand_type; + IrInstruction *ptr; + IrInstruction *value; + IrInstruction *ordering; + AtomicOrder resolved_ordering; +}; + struct IrInstructionSaveErrRetAddr { IrInstruction base; }; @@ -3636,9 +3853,8 @@ struct IrInstructionAddImplicitReturnType { struct IrInstructionFloatOp { IrInstruction base; - BuiltinFnId op; - IrInstruction *type; - IrInstruction *op1; + BuiltinFnId fn_id; + IrInstruction *operand; }; struct IrInstructionCheckRuntimeScope { @@ -3754,14 +3970,6 @@ struct IrInstructionEndExpr { ResultLoc *result_loc; }; -struct IrInstructionImplicitCast { - IrInstruction base; - - IrInstruction *dest_type; - IrInstruction *target; - ResultLoc *result_loc; -}; - // This one is for writing through the result pointer. struct IrInstructionResolveResult { IrInstruction base; @@ -3843,6 +4051,13 @@ struct IrInstructionSpillEnd { IrInstructionSpillBegin *begin; }; +struct IrInstructionVectorExtractElem { + IrInstruction base; + + IrInstruction *vector; + IrInstruction *index; +}; + enum ResultLocId { ResultLocIdInvalid, ResultLocIdNone, @@ -3852,6 +4067,7 @@ enum ResultLocId { ResultLocIdPeerParent, ResultLocIdInstruction, ResultLocIdBitCast, + ResultLocIdCast, }; // Additions to this struct may need to be handled in @@ -3919,6 +4135,13 @@ struct ResultLocBitCast { ResultLoc *parent; }; +// The source_instruction is the destination type +struct ResultLocCast { + ResultLoc base; + + ResultLoc *parent; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1282126fb..b7838003c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1,4 +1,4 @@ -๏ปฟ/* +/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. @@ -120,6 +120,12 @@ static ScopeExpr *find_expr_scope(Scope *scope) { } } +static void update_progress_display(CodeGen *g) { + stage2_progress_update_node(g->sub_progress_node, + g->resolve_queue_index + g->fn_defs_index, + g->resolve_queue.length + g->fn_defs.length); +} + ScopeDecls *get_container_scope(ZigType *type_entry) { return *get_container_scope_ptr(type_entry); } @@ -134,7 +140,6 @@ void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ZigType *import, Buf *bare_name) { - assert(node == nullptr || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr); ScopeDecls *scope = allocate(1); init_scope(g, &scope->base, ScopeIdDecls, node, parent); scope->decl_table.init(4); @@ -318,7 +323,6 @@ AstNode *type_decl_node(ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdVector: case ZigTypeIdAnyFrame: return nullptr; @@ -340,6 +344,8 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { switch (status) { case ResolveStatusInvalid: zig_unreachable(); + case ResolveStatusBeingInferred: + zig_unreachable(); case ResolveStatusUnstarted: case ResolveStatusZeroBitsKnown: return true; @@ -356,6 +362,8 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { switch (status) { case ResolveStatusInvalid: zig_unreachable(); + case ResolveStatusBeingInferred: + zig_unreachable(); case ResolveStatusUnstarted: return true; case ResolveStatusZeroBitsKnown: @@ -383,7 +391,6 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdVector: case ZigTypeIdAnyFrame: return true; @@ -411,7 +418,7 @@ uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) { } static bool is_slice(ZigType *type) { - return type->id == ZigTypeIdStruct && type->data.structure.is_slice; + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; } ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { @@ -443,18 +450,6 @@ ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) { return entry; } -static const char *ptr_len_to_star_str(PtrLen ptr_len) { - switch (ptr_len) { - case PtrLenSingle: - return "*"; - case PtrLenUnknown: - return "[*]"; - case PtrLenC: - return "[*c]"; - } - zig_unreachable(); -} - ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) { if (fn->frame_type != nullptr) { return fn->frame_type; @@ -474,9 +469,47 @@ ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) { return entry; } -ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, +static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) { + const char *const_str = ptr_type->data.pointer.is_const ? "const " : ""; + const char *volatile_str = ptr_type->data.pointer.is_volatile ? "volatile " : ""; + const char *allow_zero_str; + if (ptr_type->data.pointer.ptr_len == PtrLenC) { + assert(ptr_type->data.pointer.allow_zero); + allow_zero_str = ""; + } else { + allow_zero_str = ptr_type->data.pointer.allow_zero ? "allowzero " : ""; + } + if (ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.host_int_bytes != 0 || + ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) + { + buf_appendf(type_name, "align("); + if (ptr_type->data.pointer.explicit_alignment != 0) { + buf_appendf(type_name, "%" PRIu32, ptr_type->data.pointer.explicit_alignment); + } + if (ptr_type->data.pointer.host_int_bytes != 0) { + buf_appendf(type_name, ":%" PRIu32 ":%" PRIu32, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + } + if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + buf_appendf(type_name, ":?"); + } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index); + } + buf_appendf(type_name, ") "); + } + buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str); + if (ptr_type->data.pointer.inferred_struct_field != nullptr) { + buf_appendf(type_name, " field '%s' of %s)", + buf_ptr(ptr_type->data.pointer.inferred_struct_field->field_name), + buf_ptr(&ptr_type->data.pointer.inferred_struct_field->inferred_struct_type->name)); + } else { + buf_appendf(type_name, "%s", buf_ptr(&ptr_type->data.pointer.child_type->name)); + } +} + +ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, - uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero) + uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero, + uint32_t vector_index, InferredStructField *inferred_struct_field, ZigValue *sentinel) { assert(ptr_len != PtrLenC || allow_zero); assert(!type_is_invalid(child_type)); @@ -488,7 +521,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons byte_alignment = 0; } - if (host_int_bytes != 0) { + if (host_int_bytes != 0 && vector_index == VECTOR_INDEX_NONE) { uint32_t child_type_bits = type_size_bits(g, child_type); if (host_int_bytes * 8 == child_type_bits) { assert(bit_offset_in_host == 0); @@ -498,8 +531,12 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons TypeId type_id = {}; ZigType **parent_pointer = nullptr; - if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) { + if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || + allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr || + sentinel != nullptr) + { type_id.id = ZigTypeIdPointer; + type_id.data.pointer.codegen = g; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; type_id.data.pointer.is_volatile = is_volatile; @@ -508,6 +545,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons type_id.data.pointer.host_int_bytes = host_int_bytes; type_id.data.pointer.ptr_len = ptr_len; type_id.data.pointer.allow_zero = allow_zero; + type_id.data.pointer.vector_index = vector_index; + type_id.data.pointer.inferred_struct_field = inferred_struct_field; + type_id.data.pointer.sentinel = sentinel; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) @@ -523,34 +563,40 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons ZigType *entry = new_type_table_entry(ZigTypeIdPointer); - const char *star_str = ptr_len_to_star_str(ptr_len); - const char *const_str = is_const ? "const " : ""; - const char *volatile_str = is_volatile ? "volatile " : ""; - const char *allow_zero_str; - if (ptr_len == PtrLenC) { - assert(allow_zero); - allow_zero_str = ""; - } else { - allow_zero_str = allow_zero ? "allowzero " : ""; - } buf_resize(&entry->name, 0); - if (host_int_bytes == 0 && byte_alignment == 0) { - buf_appendf(&entry->name, "%s%s%s%s%s", - star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name)); - } else if (host_int_bytes == 0) { - buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment, - const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name)); - } else if (byte_alignment == 0) { - buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, - bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); - } else { - buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment, - bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str, - buf_ptr(&child_type->name)); + if (inferred_struct_field != nullptr) { + buf_appendf(&entry->name, "("); + } + switch (ptr_len) { + case PtrLenSingle: + buf_appendf(&entry->name, "*"); + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "[*"); + break; + case PtrLenC: + assert(sentinel == nullptr); + buf_appendf(&entry->name, "[*c]"); + break; + } + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + switch (ptr_len) { + case PtrLenSingle: + case PtrLenC: + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "]"); + break; } - if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) { + if (inferred_struct_field != nullptr) { + entry->abi_size = g->builtin_types.entry_usize->abi_size; + entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + entry->abi_align = g->builtin_types.entry_usize->abi_align; + } else if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) { if (type_has_bits(child_type)) { entry->abi_size = g->builtin_types.entry_usize->abi_size; entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits; @@ -575,6 +621,11 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons entry->data.pointer.bit_offset_in_host = bit_offset_in_host; entry->data.pointer.host_int_bytes = host_int_bytes; entry->data.pointer.allow_zero = allow_zero; + entry->data.pointer.vector_index = vector_index; + entry->data.pointer.inferred_struct_field = inferred_struct_field; + entry->data.pointer.sentinel = sentinel; + + append_ptr_type_attrs(&entry->name, entry); if (parent_pointer) { *parent_pointer = entry; @@ -584,8 +635,17 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons return entry; } +ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, + uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero) +{ + return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len, + byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, nullptr); +} + ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) { - return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false); + return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, nullptr); } ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { @@ -701,11 +761,13 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa return entry; } -ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size) { +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) { TypeId type_id = {}; type_id.id = ZigTypeIdArray; + type_id.data.array.codegen = g; type_id.data.array.child_type = child_type; type_id.data.array.size = array_size; + type_id.data.array.sentinel = sentinel; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) { return existing_entry->value; @@ -716,14 +778,27 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size) { ZigType *entry = new_type_table_entry(ZigTypeIdArray); buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s", array_size, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "[%" ZIG_PRI_u64, array_size); + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name)); - entry->size_in_bits = child_type->size_in_bits * array_size; + size_t full_array_size; + if (array_size == 0) { + full_array_size = 0; + } else { + full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); + } + + entry->size_in_bits = child_type->size_in_bits * full_array_size; entry->abi_align = child_type->abi_align; - entry->abi_size = child_type->abi_size * array_size; + entry->abi_size = child_type->abi_size * full_array_size; entry->data.array.child_type = child_type; entry->data.array.len = array_size; + entry->data.array.sentinel = sentinel; g->type_table.put(type_id, entry); return entry; @@ -740,10 +815,14 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { ZigType *entry = new_type_table_entry(ZigTypeIdStruct); - // replace the & with [] to go from a ptr type name to a slice type name buf_resize(&entry->name, 0); - size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3; - buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); + buf_appendf(&entry->name, "["); + if (ptr_type->data.pointer.sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, ptr_type->data.pointer.sentinel); + } + buf_appendf(&entry->name, "]"); + append_ptr_type_attrs(&entry->name, ptr_type); unsigned element_count = 2; Buf *ptr_field_name = buf_create_from_str("ptr"); @@ -751,22 +830,22 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { entry->data.structure.resolve_status = ResolveStatusSizeKnown; entry->data.structure.layout = ContainerLayoutAuto; - entry->data.structure.is_slice = true; + entry->data.structure.special = StructSpecialSlice; entry->data.structure.src_field_count = element_count; entry->data.structure.gen_field_count = element_count; - entry->data.structure.fields = allocate(element_count); + entry->data.structure.fields = alloc_type_struct_fields(element_count); entry->data.structure.fields_by_name.init(element_count); - entry->data.structure.fields[slice_ptr_index].name = ptr_field_name; - entry->data.structure.fields[slice_ptr_index].type_entry = ptr_type; - entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index; - entry->data.structure.fields[slice_ptr_index].gen_index = 0; - entry->data.structure.fields[slice_len_index].name = len_field_name; - entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize; - entry->data.structure.fields[slice_len_index].src_index = slice_len_index; - entry->data.structure.fields[slice_len_index].gen_index = 1; + entry->data.structure.fields[slice_ptr_index]->name = ptr_field_name; + entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type; + entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index; + entry->data.structure.fields[slice_ptr_index]->gen_index = 0; + entry->data.structure.fields[slice_len_index]->name = len_field_name; + entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize; + entry->data.structure.fields[slice_len_index]->src_index = slice_len_index; + entry->data.structure.fields[slice_len_index]->gen_index = 1; - entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]); - entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]); + entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]); + entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]); switch (type_requires_comptime(g, ptr_type)) { case ReqCompTimeInvalid: @@ -779,24 +858,8 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { if (!type_has_bits(ptr_type)) { entry->data.structure.gen_field_count = 1; - entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX; - entry->data.structure.fields[slice_len_index].gen_index = 0; - } - - ZigType *child_type = ptr_type->data.pointer.child_type; - if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero) - { - ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, - PtrLenUnknown, 0, 0, 0, false); - ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); - - entry->size_in_bits = peer_slice_type->size_in_bits; - entry->abi_size = peer_slice_type->abi_size; - entry->abi_align = peer_slice_type->abi_align; - - *parent_pointer = entry; - return entry; + entry->data.structure.fields[slice_ptr_index]->gen_index = SIZE_MAX; + entry->data.structure.fields[slice_len_index]->gen_index = 0; } if (type_has_bits(ptr_type)) { @@ -856,24 +919,20 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) { const char *calling_convention_name(CallingConvention cc) { switch (cc) { - case CallingConventionUnspecified: return "undefined"; - case CallingConventionC: return "ccc"; - case CallingConventionCold: return "coldcc"; - case CallingConventionNaked: return "nakedcc"; - case CallingConventionStdcall: return "stdcallcc"; - case CallingConventionAsync: return "async"; - } - zig_unreachable(); -} - -static const char *calling_convention_fn_type_str(CallingConvention cc) { - switch (cc) { - case CallingConventionUnspecified: return ""; - case CallingConventionC: return "extern "; - case CallingConventionCold: return "coldcc "; - case CallingConventionNaked: return "nakedcc "; - case CallingConventionStdcall: return "stdcallcc "; - case CallingConventionAsync: return "async "; + case CallingConventionUnspecified: return "Unspecified"; + case CallingConventionC: return "C"; + case CallingConventionCold: return "Cold"; + case CallingConventionNaked: return "Naked"; + case CallingConventionAsync: return "Async"; + case CallingConventionInterrupt: return "Interrupt"; + case CallingConventionSignal: return "Signal"; + case CallingConventionStdcall: return "Stdcall"; + case CallingConventionFastcall: return "Fastcall"; + case CallingConventionVectorcall: return "Vectorcall"; + case CallingConventionThiscall: return "Thiscall"; + case CallingConventionAPCS: return "Apcs"; + case CallingConventionAAPCS: return "Aapcs"; + case CallingConventionAAPCSVFP: return "Aapcsvfp"; } zig_unreachable(); } @@ -886,7 +945,15 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { case CallingConventionC: case CallingConventionCold: case CallingConventionNaked: + case CallingConventionInterrupt: + case CallingConventionSignal: case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: return false; } zig_unreachable(); @@ -894,10 +961,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { ZigType *get_stack_trace_type(CodeGen *g) { if (g->stack_trace_type == nullptr) { - ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); - assert(stack_trace_type_val->type->id == ZigTypeIdMetaType); - - g->stack_trace_type = stack_trace_type_val->data.x_type; + g->stack_trace_type = get_builtin_type(g, "StackTrace"); assertNoError(type_resolve(g, g->stack_trace_type, ResolveStatusZeroBitsKnown)); } return g->stack_trace_type; @@ -913,11 +977,14 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { if (type_is_c_abi_int(g, fn_type_id->return_type)) { return false; } - if (g->zig_target->arch == ZigLLVM_x86_64) { + if (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64 || + target_is_arm(g->zig_target) || + target_is_riscv(g->zig_target) || + target_is_wasm(g->zig_target)) + { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); - return abi_class == X64CABIClass_MEMORY; - } else if (target_is_arm(g->zig_target) || target_is_riscv(g->zig_target)) { - return type_size(g, fn_type_id->return_type) > 16; + return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval; } else if (g->zig_target->arch == ZigLLVM_mipsel) { return false; } @@ -943,8 +1010,6 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { // populate the name of the type buf_resize(&fn_type->name, 0); - const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); - buf_appendf(&fn_type->name, "%s", cc_str); buf_appendf(&fn_type->name, "fn("); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; @@ -963,6 +1028,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if (fn_type_id->alignment != 0) { buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment); } + if (fn_type_id->cc != CallingConventionUnspecified) { + buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc)); + } buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name)); // The fn_type is a pointer; not to be confused with the raw function type. @@ -996,7 +1064,12 @@ static ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf * entry->data.structure.root_struct = root_struct; entry->data.structure.layout = ContainerLayoutAuto; - buf_init_from_str(&entry->name, full_name); + if (full_name[0] == '\0') { + buf_init_from_str(&entry->name, "(root)"); + } else { + buf_init_from_str(&entry->name, full_name); + } + return entry; } @@ -1026,7 +1099,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind return entry; } -ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, +ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name, UndefAllowed undef) { size_t backward_branch_count = 0; @@ -1036,8 +1109,8 @@ ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, Zig nullptr, nullptr, node, type_name, nullptr, nullptr, undef); } -Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type, - ConstExprValue *parent_type_val, bool *is_zero_bits) +Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type, + ZigValue *parent_type_val, bool *is_zero_bits) { Error err; if (type_val->special != ConstValSpecialLazy) { @@ -1065,7 +1138,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType * case LazyValueIdPtrType: { LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); - if (parent_type_val == &lazy_ptr_type->elem_type->value) { + if (parent_type_val == lazy_ptr_type->elem_type->value) { // Does a struct which contains a pointer field to itself have bits? Yes. *is_zero_bits = false; return ErrorNone; @@ -1073,10 +1146,25 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType * if (parent_type_val == nullptr) { parent_type_val = type_val; } - return type_val_resolve_zero_bits(g, &lazy_ptr_type->elem_type->value, parent_type, + return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type, parent_type_val, is_zero_bits); } } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + + if (lazy_array_type->length < 1) { + *is_zero_bits = true; + return ErrorNone; + } + + if ((err = type_val_resolve_zero_bits(g, lazy_array_type->elem_type->value, + parent_type, nullptr, is_zero_bits))) + return err; + + return ErrorNone; + } case LazyValueIdOptType: case LazyValueIdSliceType: case LazyValueIdErrUnionType: @@ -1091,9 +1179,13 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType * zig_unreachable(); } -Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) { +Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) { if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); + if (type_val->data.x_type == g->builtin_types.entry_var) { + *is_opaque_type = false; + return ErrorNone; + } *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque); return ErrorNone; } @@ -1107,13 +1199,14 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool case LazyValueIdFnType: case LazyValueIdOptType: case LazyValueIdErrUnionType: + case LazyValueIdArrayType: *is_opaque_type = false; return ErrorNone; } zig_unreachable(); } -static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue *type_val) { +static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type_val) { if (type_val->special != ConstValSpecialLazy) { return type_requires_comptime(g, type_val->data.x_type); } @@ -1124,21 +1217,25 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue zig_unreachable(); case LazyValueIdSliceType: { LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy); - return type_val_resolve_requires_comptime(g, &lazy_slice_type->elem_type->value); + return type_val_resolve_requires_comptime(g, lazy_slice_type->elem_type->value); } case LazyValueIdPtrType: { LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); - return type_val_resolve_requires_comptime(g, &lazy_ptr_type->elem_type->value); + return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value); } case LazyValueIdOptType: { LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy); - return type_val_resolve_requires_comptime(g, &lazy_opt_type->payload_type->value); + return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value); + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_array_type->elem_type->value); } case LazyValueIdFnType: { LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy); if (lazy_fn_type->is_generic) return ReqCompTimeYes; - switch (type_val_resolve_requires_comptime(g, &lazy_fn_type->return_type->value)) { + switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type->value)) { case ReqCompTimeInvalid: return ReqCompTimeInvalid; case ReqCompTimeYes: @@ -1151,7 +1248,7 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i); bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) break; - switch (type_val_resolve_requires_comptime(g, &lazy_fn_type->param_types[i]->value)) { + switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i]->value)) { case ReqCompTimeInvalid: return ReqCompTimeInvalid; case ReqCompTimeYes: @@ -1165,13 +1262,13 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue case LazyValueIdErrUnionType: { LazyValueErrUnionType *lazy_err_union_type = reinterpret_cast(type_val->data.x_lazy); - return type_val_resolve_requires_comptime(g, &lazy_err_union_type->payload_type->value); + return type_val_resolve_requires_comptime(g, lazy_err_union_type->payload_type->value); } } zig_unreachable(); } -Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val, +Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val, size_t *abi_size, size_t *size_in_bits) { Error err; @@ -1194,7 +1291,7 @@ start_over: case LazyValueIdSliceType: { LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy); bool is_zero_bits; - if ((err = type_val_resolve_zero_bits(g, &lazy_slice_type->elem_type->value, nullptr, + if ((err = type_val_resolve_zero_bits(g, lazy_slice_type->elem_type->value, nullptr, nullptr, &is_zero_bits))) { return err; @@ -1211,7 +1308,7 @@ start_over: case LazyValueIdPtrType: { LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); bool is_zero_bits; - if ((err = type_val_resolve_zero_bits(g, &lazy_ptr_type->elem_type->value, nullptr, + if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr, nullptr, &is_zero_bits))) { return err; @@ -1231,6 +1328,7 @@ start_over: return ErrorNone; case LazyValueIdOptType: case LazyValueIdErrUnionType: + case LazyValueIdArrayType: if ((err = ir_resolve_lazy(g, source_node, type_val))) return err; goto start_over; @@ -1238,7 +1336,7 @@ start_over: zig_unreachable(); } -Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align) { +Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align) { Error err; if (type_val->special != ConstValSpecialLazy) { assert(type_val->special == ConstValSpecialStatic); @@ -1263,14 +1361,21 @@ Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align = g->builtin_types.entry_usize->abi_align; return ErrorNone; case LazyValueIdOptType: { - LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy); - return type_val_resolve_abi_align(g, &lazy_opt_type->payload_type->value, abi_align); + if ((err = ir_resolve_lazy(g, nullptr, type_val))) + return err; + + return type_val_resolve_abi_align(g, source_node, type_val, abi_align); + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_abi_align(g, source_node, lazy_array_type->elem_type->value, abi_align); } case LazyValueIdErrUnionType: { LazyValueErrUnionType *lazy_err_union_type = reinterpret_cast(type_val->data.x_lazy); uint32_t payload_abi_align; - if ((err = type_val_resolve_abi_align(g, &lazy_err_union_type->payload_type->value, + if ((err = type_val_resolve_abi_align(g, source_node, lazy_err_union_type->payload_type->value, &payload_abi_align))) { return err; @@ -1283,7 +1388,7 @@ Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t zig_unreachable(); } -static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ConstExprValue *type_val) { +static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigValue *type_val) { if (type_val->special != ConstValSpecialLazy) { return type_has_one_possible_value(g, type_val->data.x_type); } @@ -1296,6 +1401,13 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons case LazyValueIdOptType: // it has the optional bit case LazyValueIdFnType: return OnePossibleValueNo; + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + if (lazy_array_type->length < 1) + return OnePossibleValueYes; + return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value); + } case LazyValueIdPtrType: { Error err; bool zero_bits; @@ -1311,13 +1423,13 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons case LazyValueIdErrUnionType: { LazyValueErrUnionType *lazy_err_union_type = reinterpret_cast(type_val->data.x_lazy); - switch (type_val_resolve_has_one_possible_value(g, &lazy_err_union_type->err_set_type->value)) { + switch (type_val_resolve_has_one_possible_value(g, lazy_err_union_type->err_set_type->value)) { case OnePossibleValueInvalid: return OnePossibleValueInvalid; case OnePossibleValueNo: return OnePossibleValueNo; case OnePossibleValueYes: - return type_val_resolve_has_one_possible_value(g, &lazy_err_union_type->payload_type->value); + return type_val_resolve_has_one_possible_value(g, lazy_err_union_type->payload_type->value); } } } @@ -1325,7 +1437,7 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { - ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, + ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr, UndefBad); if (type_is_invalid(result->type)) return g->builtin_types.entry_invalid; @@ -1337,8 +1449,6 @@ ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { ZigType *fn_type = new_type_table_entry(ZigTypeIdFn); buf_resize(&fn_type->name, 0); - const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); - buf_appendf(&fn_type->name, "%s", cc_str); buf_appendf(&fn_type->name, "fn("); size_t i = 0; for (; i < fn_type_id->next_param_index; i += 1) { @@ -1350,7 +1460,11 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { const char *comma_str = (i == 0) ? "" : ","; buf_appendf(&fn_type->name, "%svar", comma_str); } - buf_appendf(&fn_type->name, ")var"); + buf_append_str(&fn_type->name, ")"); + if (fn_type_id->cc != CallingConventionUnspecified) { + buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc)); + } + buf_append_str(&fn_type->name, " var"); fn_type->data.fn.fn_type_id = *fn_type_id; fn_type->data.fn.is_generic = true; @@ -1360,17 +1474,21 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { return fn_type; } -void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) { +CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) { + if (fn_proto->is_async) + return CallingConventionAsync; + // Compatible with the C ABI + if (fn_proto->is_extern || fn_proto->is_export) + return CallingConventionC; + + return CallingConventionUnspecified; +} + +void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - if (fn_proto->cc == CallingConventionUnspecified) { - bool extern_abi = fn_proto->is_extern || fn_proto->is_export; - fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified; - } else { - fn_type_id->cc = fn_proto->cc; - } - + fn_type_id->cc = cc; fn_type_id->param_count = fn_proto->params.length; fn_type_id->param_info = allocate(param_count_alloc); fn_type_id->next_param_index = 0; @@ -1378,7 +1496,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { - ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), + ZigValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr, UndefBad); if (type_is_invalid(align_result->type)) return false; @@ -1401,15 +1519,15 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(g, ptr_type); - ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad); + ZigValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad); if (type_is_invalid(result_val->type)) return false; - ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index]; + ZigValue *ptr_field = result_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *len_field = result_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); - ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; + ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; if (array_val->data.x_array.special == ConstArraySpecialBuf) { *out_buffer = array_val->data.x_array.data.s_buf; return true; @@ -1420,7 +1538,7 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; - ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; + ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; if (char_val->special == ConstValSpecialUndef) { add_node_error(g, node, buf_sprintf("use of undefined value")); return false; @@ -1451,7 +1569,6 @@ static Error emit_error_unless_type_allowed_in_packed_container(CodeGen *g, ZigT case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: @@ -1554,7 +1671,6 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdVoid: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: @@ -1586,8 +1702,7 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { case ZigTypeIdArray: return type_allowed_in_extern(g, type_entry->data.array.child_type, result); case ZigTypeIdFn: - *result = type_entry->data.fn.fn_type_id.cc == CallingConventionC || - type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall; + *result = !calling_convention_allows_zig_types(type_entry->data.fn.fn_type_id.cc); return ErrorNone; case ZigTypeIdPointer: if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) @@ -1629,10 +1744,11 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) { ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name)); + buf_appendf(&err_set_type->name, "@TypeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name)); err_set_type->data.error_set.err_count = 0; err_set_type->data.error_set.errors = nullptr; err_set_type->data.error_set.infer_fn = fn_entry; + err_set_type->data.error_set.incomplete = true; err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size; @@ -1640,13 +1756,15 @@ ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) { return err_set_type; } -static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry) { +static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry, + CallingConvention cc) +{ assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; Error err; FnTypeId fn_type_id = {0}; - init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); + init_fn_type_id(&fn_type_id, proto_node, cc, proto_node->data.fn_proto.params.length); for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); @@ -1678,12 +1796,9 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; continue; - } else if (calling_convention_allows_zig_types(fn_type_id.cc)) { - return get_generic_fn_type(g, &fn_type_id); } else { add_node_error(g, param_node, - buf_sprintf("var args not allowed in function with calling convention '%s'", - calling_convention_name(fn_type_id.cc))); + buf_sprintf("var args only allowed in functions with C calling convention")); return g->builtin_types.entry_invalid; } } else if (param_node->data.param_decl.var_token != nullptr) { @@ -1730,7 +1845,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); @@ -1778,6 +1892,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) { return g->builtin_types.entry_invalid; } + fn_entry->align_bytes = fn_type_id.alignment; } if (fn_proto->return_var_token != nullptr) { @@ -1804,7 +1919,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed", buf_ptr(&specified_return_type->name))); return g->builtin_types.entry_invalid; @@ -1854,7 +1968,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdInvalid: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: zig_unreachable(); @@ -1904,6 +2017,8 @@ bool type_is_invalid(ZigType *type_entry) { return type_entry->data.unionation.resolve_status == ResolveStatusInvalid; case ZigTypeIdEnum: return type_entry->data.enumeration.resolve_status == ResolveStatusInvalid; + case ZigTypeIdFnFrame: + return type_entry->data.frame.reported_loop_err; default: return false; } @@ -1926,12 +2041,12 @@ static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fiel struct_type->data.structure.src_field_count = field_count; struct_type->data.structure.gen_field_count = 0; struct_type->data.structure.resolve_status = ResolveStatusSizeKnown; - struct_type->data.structure.fields = allocate(field_count); + struct_type->data.structure.fields = alloc_type_struct_fields(field_count); struct_type->data.structure.fields_by_name.init(field_count); size_t abi_align = min_abi_align; for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; field->name = buf_create_from_str(fields[i].name); field->type_entry = fields[i].ty; field->src_index = i; @@ -1951,7 +2066,7 @@ static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fiel size_t next_offset = 0; for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; if (!type_has_bits(field->type_entry)) continue; @@ -1960,7 +2075,7 @@ static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fiel // find the next non-zero-byte field for offset calculations size_t next_src_field_index = i + 1; for (; next_src_field_index < field_count; next_src_field_index += 1) { - if (type_has_bits(struct_type->data.structure.fields[next_src_field_index].type_entry)) + if (type_has_bits(struct_type->data.structure.fields[next_src_field_index]->type_entry)) break; } size_t next_abi_align; @@ -1968,7 +2083,7 @@ static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fiel next_abi_align = abi_align; } else { next_abi_align = max(fields[next_src_field_index].align, - struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align); + struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align); } next_offset = next_field_offset(next_offset, abi_align, field->type_entry->abi_size, next_abi_align); } @@ -2033,7 +2148,6 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { } assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); - assert(decl_node->type == NodeTypeContainerDecl); size_t field_count = struct_type->data.structure.src_field_count; @@ -2051,7 +2165,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { // Calculate offsets for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; if (field->gen_index == SIZE_MAX) continue; @@ -2062,7 +2176,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { ZigType *field_type = resolve_struct_field_type(g, field); if (field_type == nullptr) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; - return err; + return ErrorSemanticAnalyzeFail; } if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; @@ -2120,12 +2234,12 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { gen_field_index += 1; size_t next_src_field_index = i + 1; for (; next_src_field_index < field_count; next_src_field_index += 1) { - if (struct_type->data.structure.fields[next_src_field_index].gen_index != SIZE_MAX) { + if (struct_type->data.structure.fields[next_src_field_index]->gen_index != SIZE_MAX) { break; } } size_t next_align = (next_src_field_index == field_count) ? - abi_align : struct_type->data.structure.fields[next_src_field_index].align; + abi_align : struct_type->data.structure.fields[next_src_field_index]->align; next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align); size_in_bits = next_offset * 8; } @@ -2148,11 +2262,11 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { // Resolve types for fields for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; ZigType *field_type = resolve_struct_field_type(g, field); if (field_type == nullptr) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; - return err; + return ErrorSemanticAnalyzeFail; } if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) { @@ -2222,7 +2336,7 @@ static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) { &field->align)) { union_type->data.unionation.resolve_status = ResolveStatusInvalid; - return err; + return ErrorSemanticAnalyzeFail; } add_node_error(g, field->decl_node, buf_create_from_str("TODO implement field alignment syntax for unions. https://github.com/ziglang/zig/issues/3125")); @@ -2235,7 +2349,7 @@ static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) { } field->align = field->type_entry->abi_align; } else { - if ((err = type_val_resolve_abi_align(g, field->type_val, &field->align))) { + if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) { if (g->trace_err != nullptr) { g->trace_err = add_error_note(g, g->trace_err, field->decl_node, buf_create_from_str("while checking this field")); @@ -2349,6 +2463,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { union_type->data.unionation.resolve_status = ResolveStatusInvalid; return ErrorSemanticAnalyzeFail; } + if (is_packed) { if ((err = emit_error_unless_type_allowed_in_packed_union(g, field_type, union_field->decl_node))) { union_type->data.unionation.resolve_status = ResolveStatusInvalid; @@ -2546,7 +2661,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { if (tag_value != nullptr) { // A user-specified value is available - ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, + ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr, UndefBad); if (type_is_invalid(result->type)) { enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; @@ -2584,7 +2699,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // Make sure the value is unique auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); - if (entry != nullptr) { + if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; Buf *val_buf = buf_alloc(); @@ -2621,7 +2736,6 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { return ErrorNone; AstNode *decl_node = struct_type->data.structure.decl_node; - assert(decl_node->type == NodeTypeContainerDecl); if (struct_type->data.structure.resolve_loop_flag_zero_bits) { if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { @@ -2632,29 +2746,55 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { } return ErrorSemanticAnalyzeFail; } - struct_type->data.structure.resolve_loop_flag_zero_bits = true; - assert(!struct_type->data.structure.fields); - size_t field_count = decl_node->data.container_decl.fields.length; - struct_type->data.structure.src_field_count = (uint32_t)field_count; - struct_type->data.structure.fields = allocate(field_count); + size_t field_count; + if (decl_node->type == NodeTypeContainerDecl) { + field_count = decl_node->data.container_decl.fields.length; + struct_type->data.structure.src_field_count = (uint32_t)field_count; + + src_assert(struct_type->data.structure.fields == nullptr, decl_node); + struct_type->data.structure.fields = alloc_type_struct_fields(field_count); + } else if (is_anon_container(struct_type)) { + field_count = struct_type->data.structure.src_field_count; + + src_assert(field_count == 0 || struct_type->data.structure.fields != nullptr, decl_node); + } else zig_unreachable(); + struct_type->data.structure.fields_by_name.init(field_count); Scope *scope = &struct_type->data.structure.decls_scope->base; size_t gen_field_index = 0; for (size_t i = 0; i < field_count; i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; - type_struct_field->name = field_node->data.struct_field.name; - type_struct_field->decl_node = field_node; + TypeStructField *type_struct_field = struct_type->data.structure.fields[i]; - if (field_node->data.struct_field.type == nullptr) { - add_node_error(g, field_node, buf_sprintf("struct field missing type")); - struct_type->data.structure.resolve_status = ResolveStatusInvalid; - return ErrorSemanticAnalyzeFail; - } + AstNode *field_node; + if (decl_node->type == NodeTypeContainerDecl) { + field_node = decl_node->data.container_decl.fields.at(i); + type_struct_field->name = field_node->data.struct_field.name; + type_struct_field->decl_node = field_node; + if (field_node->data.struct_field.comptime_token != nullptr) { + if (field_node->data.struct_field.value == nullptr) { + add_token_error(g, field_node->owner, + field_node->data.struct_field.comptime_token, + buf_sprintf("comptime struct field missing initialization value")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + type_struct_field->is_comptime = true; + } + + if (field_node->data.struct_field.type == nullptr) { + add_node_error(g, field_node, buf_sprintf("struct field missing type")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else if (is_anon_container(struct_type)) { + field_node = type_struct_field->decl_node; + + src_assert(type_struct_field->type_entry != nullptr, field_node); + } else zig_unreachable(); auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field); if (field_entry != nullptr) { @@ -2665,16 +2805,21 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { return ErrorSemanticAnalyzeFail; } - ConstExprValue *field_type_val = analyze_const_value(g, scope, - field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef); - if (type_is_invalid(field_type_val->type)) { - struct_type->data.structure.resolve_status = ResolveStatusInvalid; - return ErrorSemanticAnalyzeFail; - } - assert(field_type_val->special != ConstValSpecialRuntime); - type_struct_field->type_val = field_type_val; - if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) - return ErrorSemanticAnalyzeFail; + ZigValue *field_type_val; + if (decl_node->type == NodeTypeContainerDecl) { + field_type_val = analyze_const_value(g, scope, + field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef); + if (type_is_invalid(field_type_val->type)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + assert(field_type_val->special != ConstValSpecialRuntime); + type_struct_field->type_val = field_type_val; + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + } else if (is_anon_container(struct_type)) { + field_type_val = type_struct_field->type_val; + } else zig_unreachable(); bool field_is_opaque_type; if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) { @@ -2691,6 +2836,9 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { type_struct_field->src_index = i; type_struct_field->gen_index = SIZE_MAX; + if (type_struct_field->is_comptime) + continue; + switch (type_val_resolve_requires_comptime(g, field_type_val)) { case ReqCompTimeYes: struct_type->data.structure.requires_comptime = true; @@ -2758,28 +2906,28 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { } struct_type->data.structure.resolve_loop_flag_other = true; - assert(decl_node->type == NodeTypeContainerDecl); size_t field_count = struct_type->data.structure.src_field_count; bool packed = struct_type->data.structure.layout == ContainerLayoutPacked; for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; if (field->gen_index == SIZE_MAX) continue; - AstNode *align_expr = field->decl_node->data.struct_field.align_expr; + AstNode *align_expr = (field->decl_node->type == NodeTypeStructField) ? + field->decl_node->data.struct_field.align_expr : nullptr; if (align_expr != nullptr) { if (!analyze_const_align(g, &struct_type->data.structure.decls_scope->base, align_expr, &field->align)) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; - return err; + return ErrorSemanticAnalyzeFail; } } else if (packed) { field->align = 1; } else { - if ((err = type_val_resolve_abi_align(g, field->type_val, &field->align))) { + if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) { if (g->trace_err != nullptr) { g->trace_err = add_error_note(g, g->trace_err, field->decl_node, buf_create_from_str("while checking this field")); @@ -2954,7 +3102,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { return ErrorSemanticAnalyzeFail; } } else { - ConstExprValue *field_type_val = analyze_const_value(g, scope, + ZigValue *field_type_val = analyze_const_value(g, scope, field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef); if (type_is_invalid(field_type_val->type)) { union_type->data.unionation.resolve_status = ResolveStatusInvalid; @@ -3020,7 +3168,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type; - ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, + ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr, UndefBad); if (type_is_invalid(result->type)) { union_type->data.unionation.resolve_status = ResolveStatusInvalid; @@ -3152,7 +3300,13 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { return ErrorNone; } -static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, bool is_test) { +void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type) { + if (g->root_import == container_type || buf_len(&container_type->name) == 0) return; + buf_append_buf(buf, &container_type->name); + buf_append_char(buf, NAMESPACE_SEP_CHAR); +} + +static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool is_test) { buf_resize(buf, 0); Scope *scope = tld->parent_scope; @@ -3160,8 +3314,7 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, bool is_test) { scope = scope->parent; } ScopeDecls *decls_scope = reinterpret_cast(scope); - buf_append_buf(buf, &decls_scope->container_type->name); - if (buf_len(buf) != 0) buf_append_char(buf, NAMESPACE_SEP_CHAR); + append_namespace_qualification(g, buf, decls_scope->container_type); if (is_test) { buf_append_str(buf, "test \""); buf_append_buf(buf, tld->name); @@ -3172,14 +3325,15 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, bool is_test) { } ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) { - ZigFn *fn_entry = allocate(1); + ZigFn *fn_entry = allocate(1, "ZigFn"); + fn_entry->ir_executable = allocate(1, "IrExecutablePass1"); fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota; fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc; fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota; fn_entry->analyzed_executable.fn_entry = fn_entry; - fn_entry->ir_executable.fn_entry = fn_entry; + fn_entry->ir_executable->fn_entry = fn_entry; fn_entry->fn_inline = inline_value; return fn_entry; @@ -3200,32 +3354,6 @@ ZigFn *create_fn(CodeGen *g, AstNode *proto_node) { return fn_entry; } -static bool scope_is_root_decls(Scope *scope) { - while (scope) { - if (scope->id == ScopeIdDecls) { - ScopeDecls *scope_decls = (ScopeDecls *)scope; - return is_top_level_struct(scope_decls->container_type); - } - scope = scope->parent; - } - zig_unreachable(); -} - -void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) { - ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn"); - assert(panic_fn_type_val != nullptr); - assert(panic_fn_type_val->type->id == ZigTypeIdMetaType); - ZigType *panic_fn_type = panic_fn_type_val->data.x_type; - - AstNode *fake_decl = allocate(1); - *fake_decl = *panic_fn->proto_node; - fake_decl->type = NodeTypeSymbol; - fake_decl->data.symbol_expr.symbol = tld_fn->base.name; - - // call this for the side effects of casting to panic_fn_type - analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr, UndefBad); -} - ZigType *get_test_fn_type(CodeGen *g) { if (g->test_fn_type) return g->test_fn_type; @@ -3244,21 +3372,15 @@ void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLink global_export->linkage = linkage; } -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, bool ccc) { - if (ccc) { - if (strcmp(symbol_name, "main") == 0 && g->libc_link_lib != nullptr) { - g->have_c_main = true; - } else if (strcmp(symbol_name, "WinMain") == 0 && - g->zig_target->os == OsWindows) - { +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { + if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->libc_link_lib != nullptr) { + g->have_c_main = true; + } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) { + if (strcmp(symbol_name, "WinMain") == 0) { g->have_winmain = true; - } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0 && - g->zig_target->os == OsWindows) - { + } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) { g->have_winmain_crt_startup = true; - } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0 && - g->zig_target->os == OsWindows) - { + } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) { g->have_dllmain_crt_startup = true; } } @@ -3270,7 +3392,6 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, G } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { - ZigType *import = tld_fn->base.import; AstNode *source_node = tld_fn->base.source_node; if (source_node->type == NodeTypeFnProto) { AstNodeFnProto *fn_proto = &source_node->data.fn_proto; @@ -3284,12 +3405,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (fn_proto->is_export || is_extern) { buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name); } else { - get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, false); - } - - if (fn_proto->is_export) { - bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC); - add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), GlobalLinkageIdStrong, ccc); + get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, false); } if (!is_extern) { @@ -3310,17 +3426,72 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; - fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry); + CallingConvention cc; + if (fn_proto->callconv_expr != nullptr) { + ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention"); - if (fn_proto->section_expr != nullptr) { - analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name); + ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr, + cc_enum_value, nullptr, UndefBad); + if (type_is_invalid(result_val->type)) { + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + + cc = (CallingConvention)bigint_as_u32(&result_val->data.x_enum_tag); + } else { + cc = cc_from_fn_proto(fn_proto); } - if (fn_table_entry->type_entry->id == ZigTypeIdInvalid) { + if (fn_proto->section_expr != nullptr) { + if (!analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name)) { + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + } + + fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry, cc); + + if (type_is_invalid(fn_table_entry->type_entry)) { tld_fn->base.resolution = TldResolutionInvalid; return; } + const CallingConvention fn_cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; + + if (fn_proto->is_export) { + switch (fn_cc) { + case CallingConventionAsync: + add_node_error(g, fn_def_node, + buf_sprintf("exported function cannot be async")); + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + case CallingConventionC: + case CallingConventionCold: + case CallingConventionNaked: + case CallingConventionInterrupt: + case CallingConventionSignal: + case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: + add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), + GlobalLinkageIdStrong, fn_cc); + break; + case CallingConventionUnspecified: + // An exported function without a specific calling + // convention defaults to C + add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), + GlobalLinkageIdStrong, CallingConventionC); + break; + } + } + if (!fn_table_entry->type_entry->data.fn.is_generic) { if (fn_def_node) g->fn_defs.append(fn_table_entry); @@ -3329,26 +3500,13 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { // if the calling convention implies that it cannot be async, we save that for later // and leave the value to be nullptr to indicate that we have not emitted possible // compile errors for improperly calling async functions. - if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) { + if (fn_cc == CallingConventionAsync) { fn_table_entry->inferred_async_node = fn_table_entry->proto_node; } - - if (scope_is_root_decls(tld_fn->base.parent_scope) && - (import == g->root_import || import->data.structure.root_struct->package == g->panic_package)) - { - if (g->have_pub_main && buf_eql_str(tld_fn->base.name, "main")) { - g->main_fn = fn_table_entry; - } else if ((import->data.structure.root_struct->package == g->panic_package || g->have_pub_panic) && - buf_eql_str(tld_fn->base.name, "panic")) - { - g->panic_fn = fn_table_entry; - g->panic_tld_fn = tld_fn; - } - } } else if (source_node->type == NodeTypeTestDecl) { ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto); - get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, true); + get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, true); tld_fn->fn_entry = fn_table_entry; @@ -3427,7 +3585,7 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope return; ZigType *import = get_scope_import(&decls_scope->base); - if (import->data.structure.root_struct->package != g->root_package) + if (import->data.structure.root_struct->package != g->main_pkg) return; Buf *decl_name_buf = node->data.test_decl.name; @@ -3463,12 +3621,14 @@ void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source tld->parent_scope = parent_scope; } -void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { - Tld *tld = get_container_scope(g->compile_var_import)->decl_table.get(name); +void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) { + ScopeDecls *builtin_scope = get_container_scope(g->compile_var_import); + Tld *tld = find_container_decl(g, builtin_scope, name); + assert(tld != nullptr); resolve_top_level_decl(g, tld, tld->source_node, false); - assert(tld->id == TldIdVar); + assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk); TldVar *tld_var = (TldVar *)tld; - tld_var->var->const_value = value; + copy_const_val(tld_var->var->const_value, value); tld_var->var->var_type = value->type; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } @@ -3572,6 +3732,8 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeSuspend: case NodeTypeEnumLiteral: case NodeTypeAnyFrameType: + case NodeTypeErrorSetField: + case NodeTypeVarFieldType: zig_unreachable(); } } @@ -3599,7 +3761,6 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&type_entry->name))); @@ -3633,7 +3794,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry // Set name to nullptr to make the variable anonymous (not visible to programmer). // TODO merge with definition of add_local_var in ir.cpp ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type) + bool is_const, ZigValue *const_value, Tld *src_tld, ZigType *var_type) { Error err; assert(const_value != nullptr); @@ -3707,6 +3868,16 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf return variable_entry; } +static void validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) { + switch (type->id) { + case ZigTypeIdMetaType: + add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'")); + break; + default: + break; + } +} + static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { AstNode *source_node = tld_var->base.source_node; AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration; @@ -3731,7 +3902,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { assert(!is_export || !is_extern); - ConstExprValue *init_value = nullptr; + ZigValue *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; @@ -3769,7 +3940,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { ZigType *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type); + ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, is_const, init_val, &tld_var->base, type); @@ -3786,8 +3957,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { } if (var_decl->section_expr != nullptr) { - if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->section_name)) { - tld_var->section_name = nullptr; + if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->var->section_name)) { + tld_var->var->section_name = nullptr; } } @@ -3796,6 +3967,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { } if (is_export) { + validate_export_var_type(g, type, source_node); add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); } @@ -3816,7 +3988,7 @@ static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_ } } - ConstExprValue *use_expr = src_using_namespace->using_namespace_value; + ZigValue *use_expr = src_using_namespace->using_namespace_value; if (type_is_invalid(use_expr->type)) { dest_decls_scope->any_imports_failed = true; return; @@ -3893,7 +4065,7 @@ static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, Sco using_namespace->base.resolution = TldResolutionResolving; assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace); - ConstExprValue *result = analyze_const_value(g, &dest_decls_scope->base, + ZigValue *result = analyze_const_value(g, &dest_decls_scope->base, using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type, nullptr, UndefBad); using_namespace->using_namespace_value = result; @@ -3901,7 +4073,7 @@ static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, Sco if (type_is_invalid(result->type)) { dest_decls_scope->any_imports_failed = true; using_namespace->base.resolution = TldResolutionInvalid; - using_namespace->using_namespace_value = &g->invalid_instruction->value; + using_namespace->using_namespace_value = g->invalid_instruction->value; return; } @@ -3910,7 +4082,7 @@ static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, Sco buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name))); dest_decls_scope->any_imports_failed = true; using_namespace->base.resolution = TldResolutionInvalid; - using_namespace->using_namespace_value = &g->invalid_instruction->value; + using_namespace->using_namespace_value = g->invalid_instruction->value; return; } } @@ -3921,6 +4093,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all return; tld->resolution = TldResolutionResolving; + update_progress_display(g); switch (tld->id) { case TldIdVar: { @@ -4062,13 +4235,22 @@ TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) { TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) { assert(type_entry->id == ZigTypeIdStruct); - assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); - if (type_entry->data.structure.src_field_count == 0) + if (type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) { + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *field = type_entry->data.structure.fields[i]; + if (buf_eql_buf(field->name, name)) + return field; + } return nullptr; - auto entry = type_entry->data.structure.fields_by_name.maybe_get(name); - if (entry == nullptr) - return nullptr; - return entry->value; + } else { + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); + if (type_entry->data.structure.src_field_count == 0) + return nullptr; + auto entry = type_entry->data.structure.fields_by_name.maybe_get(name); + if (entry == nullptr) + return nullptr; + return entry->value; + } } TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name) { @@ -4111,7 +4293,7 @@ bool is_container(ZigType *type_entry) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdStruct: - return !type_entry->data.structure.is_slice; + return type_entry->data.structure.special != StructSpecialSlice; case ZigTypeIdEnum: case ZigTypeIdUnion: return true; @@ -4133,7 +4315,6 @@ bool is_container(ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdVector: case ZigTypeIdFnFrame: @@ -4204,7 +4385,7 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) { } else if (ptr_type->id == ZigTypeIdFn) { // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM: // "Cannot getTypeInfo() on a type that is unsized!" - // when getting the alignment of `?extern fn() void`. + // when getting the alignment of `?fn() callconv(.C) void`. // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; } else if (ptr_type->id == ZigTypeIdAnyFrame) { @@ -4236,7 +4417,8 @@ AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index) { return nullptr; } -static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { +static Error define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { + Error err; ZigType *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; @@ -4255,8 +4437,11 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } ZigType *param_type = param_info->type; - bool is_noalias = param_info->is_noalias; + if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) { + return err; + } + bool is_noalias = param_info->is_noalias; if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); } @@ -4271,17 +4456,19 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { fn_table_entry->variable_list.append(var); } } + + return ErrorNone; } bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) { assert(err_set_type->id == ZigTypeIdErrorSet); ZigFn *infer_fn = err_set_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { + if (infer_fn != nullptr && err_set_type->data.error_set.incomplete) { if (infer_fn->anal_state == FnAnalStateInvalid) { return false; } else if (infer_fn->anal_state == FnAnalStateReady) { analyze_fn_body(g, infer_fn); - if (err_set_type->data.error_set.infer_fn != nullptr) { + if (err_set_type->data.error_set.incomplete) { assert(g->errors.length != 0); return false; } @@ -4379,6 +4566,10 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode if (callee->anal_state == FnAnalStateComplete) { analyze_fn_async(g, callee, true); if (callee->anal_state == FnAnalStateInvalid) { + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, call_node, + buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name))); + } return ErrorSemanticAnalyzeFail; } callee_is_async = fn_is_async(callee); @@ -4396,6 +4587,7 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode } } if (callee_is_async) { + bool bad_recursion = (fn->inferred_async_node == inferred_async_none); fn->inferred_async_node = call_node; fn->inferred_async_fn = callee; if (must_not_be_async) { @@ -4405,6 +4597,12 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode add_async_error_notes(g, msg, fn); return ErrorSemanticAnalyzeFail; } + if (bad_recursion) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("recursive function cannot be async")); + add_async_error_notes(g, msg, fn); + return ErrorSemanticAnalyzeFail; + } if (fn->assumed_non_async != nullptr) { ErrorMsg *msg = add_node_error(g, fn->proto_node, buf_sprintf("unable to infer whether '%s' should be async", @@ -4496,7 +4694,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - ZigType *block_return_type = ir_analyze(g, &fn->ir_executable, + ZigType *block_return_type = ir_analyze(g, fn->ir_executable, &fn->analyzed_executable, fn_type_id->return_type, return_type_node); fn->src_implicit_return_type = block_return_type; @@ -4508,7 +4706,9 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { ZigType *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type; - if (return_err_set_type->data.error_set.infer_fn != nullptr) { + if (return_err_set_type->data.error_set.infer_fn != nullptr && + return_err_set_type->data.error_set.incomplete) + { ZigType *inferred_err_set_type; if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) { inferred_err_set_type = fn->src_implicit_return_type; @@ -4521,14 +4721,16 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { return; } - if (inferred_err_set_type->data.error_set.infer_fn != nullptr) { + if (inferred_err_set_type->data.error_set.infer_fn != nullptr && + inferred_err_set_type->data.error_set.incomplete) + { if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { fn->anal_state = FnAnalStateInvalid; return; } } - return_err_set_type->data.error_set.infer_fn = nullptr; + return_err_set_type->data.error_set.incomplete = false; if (type_is_global_error_set(inferred_err_set_type)) { return_err_set_type->data.error_set.err_count = UINT32_MAX; } else { @@ -4570,6 +4772,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { return; fn_table_entry->anal_state = FnAnalStateProbing; + update_progress_display(g); AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ? fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node; @@ -4578,21 +4781,29 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { if (!fn_table_entry->child_scope) fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; - define_local_param_variables(g, fn_table_entry); + if (define_local_param_variables(g, fn_table_entry) != ErrorNone) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } ZigType *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); - ir_gen_fn(g, fn_table_entry); - if (fn_table_entry->ir_executable.first_err_trace_msg != nullptr) { + if (!ir_gen_fn(g, fn_table_entry)) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } + + if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + if (g->verbose_ir) { fprintf(stderr, "\n"); ast_render(stderr, fn_table_entry->body_node, 4); fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name)); - ir_print(g, stderr, &fn_table_entry->ir_executable, 4, IrPassSrc); + ir_print(g, stderr, fn_table_entry->ir_executable, 4, IrPassSrc); fprintf(stderr, "}\n"); } @@ -4680,28 +4891,6 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu ast_print(stderr, root_node, 0); } - if (source_kind == SourceKindRoot || package == g->panic_package) { - // Look for panic and main - for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { - AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); - - if (top_level_decl->type == NodeTypeFnDef) { - AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; - assert(proto_node->type == NodeTypeFnProto); - Buf *proto_name = proto_node->data.fn_proto.name; - - bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); - if (is_pub) { - if (buf_eql_str(proto_name, "main")) { - g->have_pub_main = true; - } else if (buf_eql_str(proto_name, "panic")) { - g->have_pub_panic = true; - } - } - } - } - } - for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl); @@ -4837,7 +5026,6 @@ bool handle_is_ptr(ZigType *type_entry) { case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: zig_unreachable(); case ZigTypeIdUnreachable: @@ -4921,12 +5109,12 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } -static uint32_t hash_const_val_error_set(ConstExprValue *const_val) { +static uint32_t hash_const_val_error_set(ZigValue *const_val) { assert(const_val->data.x_err_set != nullptr); return const_val->data.x_err_set->value ^ 2630160122; } -static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { +static uint32_t hash_const_val_ptr(ZigValue *const_val) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { case ConstPtrMutRuntimeVar: @@ -4951,7 +5139,6 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)1764906839; hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); - hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; return hash_val; case ConstPtrSpecialBaseStruct: hash_val += (uint32_t)3518317043; @@ -4988,7 +5175,7 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { zig_unreachable(); } -static uint32_t hash_const_val(ConstExprValue *const_val) { +static uint32_t hash_const_val(ZigValue *const_val) { assert(const_val->special == ConstValSpecialStatic); switch (const_val->type->id) { case ZigTypeIdOpaque: @@ -5057,9 +5244,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { memcpy(&ints[0], &f128, 16); return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb; } - case ZigTypeIdArgTuple: - return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 + - (uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768; case ZigTypeIdFn: assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); @@ -5105,7 +5289,10 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case ZigTypeIdAnyFrame: // TODO better hashing algorithm return 3747294894; - case ZigTypeIdBoundFn: + case ZigTypeIdBoundFn: { + assert(const_val->data.x_bound_fn.fn != nullptr); + return 3677364617 ^ hash_ptr(const_val->data.x_bound_fn.fn); + } case ZigTypeIdInvalid: case ZigTypeIdUnreachable: zig_unreachable(); @@ -5117,7 +5304,7 @@ uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) { uint32_t result = 0; result += hash_ptr(id->fn_entry); for (size_t i = 0; i < id->param_count; i += 1) { - ConstExprValue *generic_param = &id->params[i]; + ZigValue *generic_param = &id->params[i]; if (generic_param->special != ConstValSpecialRuntime) { result += hash_const_val(generic_param); result += hash_ptr(generic_param->type); @@ -5131,8 +5318,8 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { if (a->fn_entry != b->fn_entry) return false; if (a->param_count != b->param_count) return false; for (size_t i = 0; i < a->param_count; i += 1) { - ConstExprValue *a_val = &a->params[i]; - ConstExprValue *b_val = &b->params[i]; + ZigValue *a_val = &a->params[i]; + ZigValue *b_val = &b->params[i]; if (a_val->type != b_val->type) return false; if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) { assert(a_val->special == ConstValSpecialStatic); @@ -5147,7 +5334,7 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { return true; } -static bool can_mutate_comptime_var_state(ConstExprValue *value) { +static bool can_mutate_comptime_var_state(ZigValue *value) { assert(value != nullptr); switch (value->type->id) { case ZigTypeIdInvalid: @@ -5193,7 +5380,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { zig_unreachable(); case ZigTypeIdStruct: for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) { - if (can_mutate_comptime_var_state(&value->data.x_struct.fields[i])) + if (can_mutate_comptime_var_state(value->data.x_struct.fields[i])) return true; } return false; @@ -5213,9 +5400,6 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case ZigTypeIdUnion: return can_mutate_comptime_var_state(value->data.x_union.payload); - - case ZigTypeIdArgTuple: - zig_panic("TODO var args at comptime is currently not supported"); } zig_unreachable(); } @@ -5256,9 +5440,6 @@ static bool return_type_is_cacheable(ZigType *return_type) { case ZigTypeIdErrorUnion: return return_type_is_cacheable(return_type->data.error_union.payload_type); - - case ZigTypeIdArgTuple: - zig_panic("TODO var args at comptime is currently not supported"); } zig_unreachable(); } @@ -5342,7 +5523,7 @@ bool fn_eval_eql(Scope *a, Scope *b) { return false; } -// Whether the type has bits at runtime. +// Deprecated. Use type_has_bits2. bool type_has_bits(ZigType *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -5350,6 +5531,27 @@ bool type_has_bits(ZigType *type_entry) { return type_entry->abi_size != 0; } +// Whether the type has bits at runtime. +Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) { + Error err; + + if (type_is_invalid(type_entry)) + return ErrorSemanticAnalyzeFail; + + if (type_entry->id == ZigTypeIdStruct && + type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) + { + *result = true; + return ErrorNone; + } + + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return err; + + *result = type_entry->abi_size != 0; + return ErrorNone; +} + // Whether you can infer the value based solely on the type. OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { assert(type_entry != nullptr); @@ -5357,6 +5559,12 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { if (type_entry->one_possible_value != OnePossibleValueInvalid) return type_entry->one_possible_value; + if (type_entry->id == ZigTypeIdStruct && + type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) + { + return OnePossibleValueNo; + } + Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) return OnePossibleValueInvalid; @@ -5369,7 +5577,6 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdEnumLiteral: case ZigTypeIdMetaType: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdBool: @@ -5389,7 +5596,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { return type_has_one_possible_value(g, type_entry->data.array.child_type); case ZigTypeIdStruct: for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *field = &type_entry->data.structure.fields[i]; + TypeStructField *field = type_entry->data.structure.fields[i]; OnePossibleValue opv = (field->type_entry != nullptr) ? type_has_one_possible_value(g, field->type_entry) : type_val_resolve_has_one_possible_value(g, field->type_val); @@ -5428,8 +5635,35 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { zig_unreachable(); } +ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) { + auto entry = g->one_possible_values.maybe_get(type_entry); + if (entry != nullptr) { + return entry->value; + } + ZigValue *result = create_const_vals(1); + result->type = type_entry; + result->special = ConstValSpecialStatic; + if (result->type->id == ZigTypeIdStruct) { + // The fields array cannot be left unpopulated + const ZigType *struct_type = result->type; + const size_t field_count = struct_type->data.structure.src_field_count; + result->data.x_struct.fields = alloc_const_vals_ptrs(field_count); + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = resolve_struct_field_type(g, field); + assert(field_type != nullptr); + result->data.x_struct.fields[i] = get_the_one_possible_value(g, field_type); + } + } + g->one_possible_values.put(type_entry, result); + return result; +} + ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) { Error err; + if (ty == g->builtin_types.entry_var) { + return ReqCompTimeYes; + } switch (ty->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -5440,7 +5674,6 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) { case ZigTypeIdNull: case ZigTypeIdMetaType: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: return ReqCompTimeYes; case ZigTypeIdArray: return type_requires_comptime(g, ty->data.array.child_type); @@ -5488,109 +5721,95 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) { zig_unreachable(); } -void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { +void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) { auto entry = g->string_literals_table.maybe_get(str); if (entry != nullptr) { - memcpy(const_val, entry->value, sizeof(ConstExprValue)); + memcpy(const_val, entry->value, sizeof(ZigValue)); return; } + // first we build the underlying array + ZigValue *array_val = create_const_vals(1); + array_val->special = ConstValSpecialStatic; + array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), g->intern.for_zero_byte()); + array_val->data.x_array.special = ConstArraySpecialBuf; + array_val->data.x_array.data.s_buf = str; + + // then make the pointer point to it const_val->special = ConstValSpecialStatic; - const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); - const_val->data.x_array.special = ConstArraySpecialBuf; - const_val->data.x_array.data.s_buf = str; + const_val->type = get_pointer_to_type_extra2(g, array_val->type, true, false, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.data.ref.pointee = array_val; g->string_literals_table.put(str, const_val); } -ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_str_lit(CodeGen *g, Buf *str) { + ZigValue *const_val = create_const_vals(1); init_const_str_lit(g, const_val, str); return const_val; } -void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { - // first we build the underlying array - size_t len_with_null = buf_len(str) + 1; - ConstExprValue *array_val = create_const_vals(1); - array_val->special = ConstValSpecialStatic; - array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null); - // TODO buf optimization - array_val->data.x_array.data.s_none.elements = create_const_vals(len_with_null); - for (size_t i = 0; i < buf_len(str); i += 1) { - ConstExprValue *this_char = &array_val->data.x_array.data.s_none.elements[i]; - this_char->special = ConstValSpecialStatic; - this_char->type = g->builtin_types.entry_u8; - bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); - } - ConstExprValue *null_char = &array_val->data.x_array.data.s_none.elements[len_with_null - 1]; - null_char->special = ConstValSpecialStatic; - null_char->type = g->builtin_types.entry_u8; - bigint_init_unsigned(&null_char->data.x_bigint, 0); - - // then make the pointer point to it - const_val->special = ConstValSpecialStatic; - // TODO make this `[*]null u8` instead of `[*]u8` - const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, 0, 0, 0, false); - const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - const_val->data.x_ptr.data.base_array.array_val = array_val; - const_val->data.x_ptr.data.base_array.elem_index = 0; - const_val->data.x_ptr.data.base_array.is_cstr = true; -} -ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) { - ConstExprValue *const_val = create_const_vals(1); - init_const_c_str_lit(g, const_val, str); - return const_val; -} - -void init_const_bigint(ConstExprValue *const_val, ZigType *type, const BigInt *bigint) { +void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_bigint(&const_val->data.x_bigint, bigint); } -ConstExprValue *create_const_bigint(ZigType *type, const BigInt *bigint) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_bigint(ZigType *type, const BigInt *bigint) { + ZigValue *const_val = create_const_vals(1); init_const_bigint(const_val, type, bigint); return const_val; } -void init_const_unsigned_negative(ConstExprValue *const_val, ZigType *type, uint64_t x, bool negative) { +void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_unsigned(&const_val->data.x_bigint, x); const_val->data.x_bigint.is_negative = negative; } -ConstExprValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative) { + ZigValue *const_val = create_const_vals(1); init_const_unsigned_negative(const_val, type, x, negative); return const_val; } -void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x) { +void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x) { return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false); } -ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) { +ZigValue *create_const_usize(CodeGen *g, uint64_t x) { return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false); } -void init_const_signed(ConstExprValue *const_val, ZigType *type, int64_t x) { +void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_signed(&const_val->data.x_bigint, x); } -ConstExprValue *create_const_signed(ZigType *type, int64_t x) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_signed(ZigType *type, int64_t x) { + ZigValue *const_val = create_const_vals(1); init_const_signed(const_val, type, x); return const_val; } -void init_const_float(ConstExprValue *const_val, ZigType *type, double value) { +void init_const_null(ZigValue *const_val, ZigType *type) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + const_val->data.x_optional = nullptr; +} + +ZigValue *create_const_null(ZigType *type) { + ZigValue *const_val = create_const_vals(1); + init_const_null(const_val, type); + return const_val; +} + +void init_const_float(ZigValue *const_val, ZigType *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; if (type->id == ZigTypeIdComptimeFloat) { @@ -5617,61 +5836,61 @@ void init_const_float(ConstExprValue *const_val, ZigType *type, double value) { } } -ConstExprValue *create_const_float(ZigType *type, double value) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_float(ZigType *type, double value) { + ZigValue *const_val = create_const_vals(1); init_const_float(const_val, type, value); return const_val; } -void init_const_enum(ConstExprValue *const_val, ZigType *type, const BigInt *tag) { +void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_bigint(&const_val->data.x_enum_tag, tag); } -ConstExprValue *create_const_enum(ZigType *type, const BigInt *tag) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_enum(ZigType *type, const BigInt *tag) { + ZigValue *const_val = create_const_vals(1); init_const_enum(const_val, type, tag); return const_val; } -void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) { +void init_const_bool(CodeGen *g, ZigValue *const_val, bool value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_bool; const_val->data.x_bool = value; } -ConstExprValue *create_const_bool(CodeGen *g, bool value) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_bool(CodeGen *g, bool value) { + ZigValue *const_val = create_const_vals(1); init_const_bool(g, const_val, value); return const_val; } -void init_const_runtime(ConstExprValue *const_val, ZigType *type) { +void init_const_runtime(ZigValue *const_val, ZigType *type) { const_val->special = ConstValSpecialRuntime; const_val->type = type; } -ConstExprValue *create_const_runtime(ZigType *type) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_runtime(ZigType *type) { + ZigValue *const_val = create_const_vals(1); init_const_runtime(const_val, type); return const_val; } -void init_const_type(CodeGen *g, ConstExprValue *const_val, ZigType *type_value) { +void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_type; const_val->data.x_type = type_value; } -ConstExprValue *create_const_type(CodeGen *g, ZigType *type_value) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_type(CodeGen *g, ZigType *type_value) { + ZigValue *const_val = create_const_vals(1); init_const_type(g, const_val, type_value); return const_val; } -void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, +void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val, size_t start, size_t len, bool is_const) { assert(array_val->type->id == ZigTypeIdArray); @@ -5681,20 +5900,20 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, ptr_type); - const_val->data.x_struct.fields = create_const_vals(2); + const_val->data.x_struct.fields = alloc_const_vals_ptrs(2); - init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, + init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, PtrLenUnknown); - init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len); + init_const_usize(g, const_val->data.x_struct.fields[slice_len_index], len); } -ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const) { + ZigValue *const_val = create_const_vals(1); init_const_slice(g, const_val, array_val, start, len, is_const); return const_val; } -void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, +void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len) { assert(array_val->type->id == ZigTypeIdArray); @@ -5708,28 +5927,28 @@ void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue const_val->data.x_ptr.data.base_array.elem_index = elem_index; } -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const, +ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len) { - ConstExprValue *const_val = create_const_vals(1); + ZigValue *const_val = create_const_vals(1); init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len); return const_val; } -void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const) { +void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const) { const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type(g, pointee_val->type, is_const); const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.data.ref.pointee = pointee_val; } -ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const) { - ConstExprValue *const_val = create_const_vals(1); +ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const) { + ZigValue *const_val = create_const_vals(1); init_const_ptr_ref(g, const_val, pointee_val, is_const); return const_val; } -void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, ZigType *pointee_type, +void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type, size_t addr, bool is_const) { const_val->special = ConstValSpecialStatic; @@ -5738,35 +5957,48 @@ void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, ZigTy const_val->data.x_ptr.data.hard_coded_addr.addr = addr; } -ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type, +ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type, size_t addr, bool is_const) { - ConstExprValue *const_val = create_const_vals(1); + ZigValue *const_val = create_const_vals(1); init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const); return const_val; } -void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end) { - const_val->special = ConstValSpecialStatic; - const_val->type = g->builtin_types.entry_arg_tuple; - const_val->data.x_arg_tuple.start_index = arg_index_start; - const_val->data.x_arg_tuple.end_index = arg_index_end; +ZigValue *create_const_vals(size_t count) { + return allocate(count, "ZigValue"); } -ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end) { - ConstExprValue *const_val = create_const_vals(1); - init_const_arg_tuple(g, const_val, arg_index_start, arg_index_end); - return const_val; +ZigValue **alloc_const_vals_ptrs(size_t count) { + return realloc_const_vals_ptrs(nullptr, 0, count); } +ZigValue **realloc_const_vals_ptrs(ZigValue **ptr, size_t old_count, size_t new_count) { + assert(new_count >= old_count); -ConstExprValue *create_const_vals(size_t count) { - ConstGlobalRefs *global_refs = allocate(count); - ConstExprValue *vals = allocate(count); - for (size_t i = 0; i < count; i += 1) { - vals[i].global_refs = &global_refs[i]; + size_t new_item_count = new_count - old_count; + ZigValue **result = reallocate(ptr, old_count, new_count, "ZigValue*"); + ZigValue *vals = create_const_vals(new_item_count); + for (size_t i = old_count; i < new_count; i += 1) { + result[i] = &vals[i - old_count]; } - return vals; + return result; +} + +TypeStructField **alloc_type_struct_fields(size_t count) { + return realloc_type_struct_fields(nullptr, 0, count); +} + +TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count) { + assert(new_count >= old_count); + + size_t new_item_count = new_count - old_count; + TypeStructField **result = reallocate(ptr, old_count, new_count, "TypeStructField*"); + TypeStructField *vals = allocate(new_item_count, "TypeStructField"); + for (size_t i = old_count; i < new_count; i += 1) { + result[i] = &vals[i - old_count]; + } + return result; } static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) { @@ -5920,7 +6152,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { fields.append({"@stack_trace", get_stack_trace_type(g), 0}); fields.append({"@instruction_addresses", - get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); } frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), @@ -5939,7 +6171,8 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { alloca_gen->base.id = IrInstructionIdAllocaGen; alloca_gen->base.source_node = fn->proto_node; alloca_gen->base.scope = fn->child_scope; - alloca_gen->base.value.type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); + alloca_gen->base.value = allocate(1, "ZigValue"); + alloca_gen->base.value->type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); alloca_gen->base.ref_count = 1; alloca_gen->name_hint = ""; fn->alloca_gen_list.append(alloca_gen); @@ -6006,7 +6239,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { call->frame_result_loc = all_calls_alloca; } if (largest_call_frame_type != nullptr) { - all_calls_alloca->value.type = get_pointer_to_type(g, largest_call_frame_type, false); + all_calls_alloca->value->type = get_pointer_to_type(g, largest_call_frame_type, false); } // Since this frame is async, an await might represent a suspend point, and @@ -6017,7 +6250,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { IrInstructionAwaitGen *await = fn->await_list.at(i); // TODO If this is a noasync await, it doesn't suspend // https://github.com/ziglang/zig/issues/3157 - if (await->base.value.special != ConstValSpecialRuntime) { + if (await->base.value->special != ConstValSpecialRuntime) { // Known at comptime. No spill, no suspend. continue; } @@ -6044,10 +6277,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { } if (await->base.ref_count == 0) continue; - if (!type_has_bits(await->base.value.type)) + if (!type_has_bits(await->base.value->type)) continue; await->result_loc = ir_create_alloca(g, await->base.scope, await->base.source_node, fn, - await->base.value.type, ""); + await->base.value->type, ""); } for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) { IrBasicBlock *block = fn->analyzed_executable.basic_block_list.at(block_i); @@ -6072,15 +6305,17 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { // This instruction does its own spilling specially, or otherwise doesn't need it. continue; } - if (instruction->value.special != ConstValSpecialRuntime) + if (instruction->value->special != ConstValSpecialRuntime) continue; if (instruction->ref_count == 0) continue; - if (!type_has_bits(instruction->value.type)) + if ((err = type_resolve(g, instruction->value->type, ResolveStatusZeroBitsKnown))) + return ErrorSemanticAnalyzeFail; + if (!type_has_bits(instruction->value->type)) continue; if (scope_needs_spill(instruction->scope)) { instruction->spill = ir_create_alloca(g, instruction->scope, instruction->source_node, - fn, instruction->value.type, ""); + fn, instruction->value->type, ""); } } } @@ -6116,6 +6351,9 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i); } ZigType *param_type = param_info->type; + if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) { + return err; + } fields.append({buf_ptr(param_name), param_type, 0}); } @@ -6123,21 +6361,21 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { if (codegen_fn_has_err_ret_tracing_stack(g, fn, true)) { fields.append({"@stack_trace", get_stack_trace_type(g), 0}); fields.append({"@instruction_addresses", - get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count), 0}); + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); } for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) { IrInstructionAllocaGen *instruction = fn->alloca_gen_list.at(alloca_i); instruction->field_index = SIZE_MAX; - ZigType *ptr_type = instruction->base.value.type; + ZigType *ptr_type = instruction->base.value->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; if (!type_has_bits(child_type)) continue; if (instruction->base.ref_count == 0) continue; - if (instruction->base.value.special != ConstValSpecialRuntime) { - if (const_ptr_pointee(nullptr, g, &instruction->base.value, nullptr)->special != + if (instruction->base.value->special != ConstValSpecialRuntime) { + if (const_ptr_pointee(nullptr, g, instruction->base.value, nullptr)->special != ConstValSpecialRuntime) { continue; @@ -6158,6 +6396,8 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { } instruction->field_index = fields.length; + src_assert(child_type->id != ZigTypeIdPointer || child_type->data.pointer.inferred_struct_field == nullptr, + instruction->base.source_node); fields.append({name, child_type, instruction->align}); } @@ -6191,10 +6431,11 @@ static Error resolve_pointer_zero_bits(CodeGen *g, ZigType *ty) { ZigType *elem_type = ty->data.pointer.child_type; - if ((err = type_resolve(g, elem_type, ResolveStatusZeroBitsKnown))) + bool has_bits; + if ((err = type_has_bits2(g, elem_type, &has_bits))) return err; - if (type_has_bits(elem_type)) { + if (has_bits) { ty->abi_size = g->builtin_types.entry_usize->abi_size; ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits; ty->abi_align = g->builtin_types.entry_usize->abi_align; @@ -6212,6 +6453,8 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { switch (status) { case ResolveStatusUnstarted: return ErrorNone; + case ResolveStatusBeingInferred: + zig_unreachable(); case ResolveStatusInvalid: zig_unreachable(); case ResolveStatusZeroBitsKnown: @@ -6266,27 +6509,36 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { } bool ir_get_var_is_comptime(ZigVar *var) { + if (var->is_comptime_memoized) + return var->is_comptime_memoized_value; + + var->is_comptime_memoized = true; + // The is_comptime field can be left null, which means not comptime. - if (var->is_comptime == nullptr) - return false; + if (var->is_comptime == nullptr) { + var->is_comptime_memoized_value = false; + return var->is_comptime_memoized_value; + } // When the is_comptime field references an instruction that has to get analyzed, this // is the value. if (var->is_comptime->child != nullptr) { - assert(var->is_comptime->child->value.type->id == ZigTypeIdBool); - return var->is_comptime->child->value.data.x_bool; + assert(var->is_comptime->child->value->type->id == ZigTypeIdBool); + var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; } // As an optimization, is_comptime values which are constant are allowed // to be omitted from analysis. In this case, there is no child instruction // and we simply look at the unanalyzed const parent instruction. - assert(var->is_comptime->value.type->id == ZigTypeIdBool); - return var->is_comptime->value.data.x_bool; + assert(var->is_comptime->value->type->id == ZigTypeIdBool); + var->is_comptime_memoized_value = var->is_comptime->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; } -bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { +bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { if (a->data.x_ptr.special != b->data.x_ptr.special) return false; - if (a->data.x_ptr.mut != b->data.x_ptr.mut) - return false; switch (a->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); @@ -6295,22 +6547,14 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return false; return true; case ConstPtrSpecialBaseArray: - if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && - a->data.x_ptr.data.base_array.array_val->global_refs != - b->data.x_ptr.data.base_array.array_val->global_refs) - { + if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) { return false; } if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) return false; - if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) - return false; return true; case ConstPtrSpecialBaseStruct: - if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && - a->data.x_ptr.data.base_struct.struct_val->global_refs != - b->data.x_ptr.data.base_struct.struct_val->global_refs) - { + if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val) { return false; } if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) @@ -6318,27 +6562,21 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return true; case ConstPtrSpecialBaseErrorUnionCode: if (a->data.x_ptr.data.base_err_union_code.err_union_val != - b->data.x_ptr.data.base_err_union_code.err_union_val && - a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs != - b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs) + b->data.x_ptr.data.base_err_union_code.err_union_val) { return false; } return true; case ConstPtrSpecialBaseErrorUnionPayload: if (a->data.x_ptr.data.base_err_union_payload.err_union_val != - b->data.x_ptr.data.base_err_union_payload.err_union_val && - a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs != - b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs) + b->data.x_ptr.data.base_err_union_payload.err_union_val) { return false; } return true; case ConstPtrSpecialBaseOptionalPayload: if (a->data.x_ptr.data.base_optional_payload.optional_val != - b->data.x_ptr.data.base_optional_payload.optional_val && - a->data.x_ptr.data.base_optional_payload.optional_val->global_refs != - b->data.x_ptr.data.base_optional_payload.optional_val->global_refs) + b->data.x_ptr.data.base_optional_payload.optional_val) { return false; } @@ -6357,7 +6595,7 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { zig_unreachable(); } -static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) { +static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) { assert(a->data.x_array.special != ConstArraySpecialUndef); assert(b->data.x_array.special != ConstArraySpecialUndef); if (a->data.x_array.special == ConstArraySpecialBuf && @@ -6368,8 +6606,8 @@ static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprVal expand_undef_array(g, a); expand_undef_array(g, b); - ConstExprValue *a_elems = a->data.x_array.data.s_none.elements; - ConstExprValue *b_elems = b->data.x_array.data.s_none.elements; + ZigValue *a_elems = a->data.x_array.data.s_none.elements; + ZigValue *b_elems = b->data.x_array.data.s_none.elements; for (size_t i = 0; i < len; i += 1) { if (!const_values_equal(g, &a_elems[i], &b_elems[i])) @@ -6379,10 +6617,20 @@ static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprVal return true; } -bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { - assert(a->type->id == b->type->id); +bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { + if (a->type->id != b->type->id) return false; assert(a->special == ConstValSpecialStatic); assert(b->special == ConstValSpecialStatic); + if (a->type == b->type) { + switch (type_has_one_possible_value(g, a->type)) { + case OnePossibleValueInvalid: + zig_unreachable(); + case OnePossibleValueNo: + break; + case OnePossibleValueYes: + return true; + } + } switch (a->type->id) { case ZigTypeIdOpaque: zig_unreachable(); @@ -6443,8 +6691,8 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { } case ZigTypeIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { - ConstExprValue *field_a = &a->data.x_struct.fields[i]; - ConstExprValue *field_b = &b->data.x_struct.fields[i]; + ZigValue *field_a = a->data.x_struct.fields[i]; + ZigValue *field_b = b->data.x_struct.fields[i]; if (!const_values_equal(g, field_a, field_b)) return false; } @@ -6467,9 +6715,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { } case ZigTypeIdErrorUnion: zig_panic("TODO"); - case ZigTypeIdArgTuple: - return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && - a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -6515,7 +6760,7 @@ void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool } } -void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_val, bool is_max) { +void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max) { if (type_entry->id == ZigTypeIdInt) { const_val->special = ConstValSpecialStatic; eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); @@ -6529,7 +6774,7 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v } } -static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { +static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) { if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) { buf_append_buf(buf, &type_entry->name); return; @@ -6548,15 +6793,10 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); return; case ConstPtrSpecialBaseArray: - if (const_val->data.x_ptr.data.base_array.is_cstr) { - buf_appendf(buf, "*(c str lit)"); - return; - } else { - buf_appendf(buf, "*"); - // TODO we need a source node for const_ptr_pointee because it can generate compile errors - render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); - return; - } + buf_appendf(buf, "*"); + // TODO we need a source node for const_ptr_pointee because it can generate compile errors + render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); + return; case ConstPtrSpecialHardCodedAddr: buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name), const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -6577,7 +6817,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val zig_unreachable(); } -static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { +static void render_const_val_err_set(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) { if (const_val->data.x_err_set == nullptr) { buf_append_str(buf, "null"); } else { @@ -6585,7 +6825,7 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const } } -static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstExprValue *const_val, uint64_t start, uint64_t len) { +static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ZigValue *const_val, uint64_t start, uint64_t len) { ConstArrayValue *array = &const_val->data.x_array; switch (array->special) { case ConstArraySpecialUndef: @@ -6609,7 +6849,7 @@ static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstEx return; } case ConstArraySpecialNone: { - ConstExprValue *base = &array->data.s_none.elements[start]; + ZigValue *base = &array->data.s_none.elements[start]; assert(start + len <= const_val->type->data.array.len); buf_appendf(buf, "%s{", buf_ptr(type_name)); @@ -6624,7 +6864,7 @@ static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstEx zig_unreachable(); } -void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { +void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: buf_appendf(buf, "(runtime value)"); @@ -6752,17 +6992,17 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdStruct: { if (is_slice(type_entry)) { - ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = const_val->data.x_struct.fields[slice_len_index]; size_t len = bigint_as_usize(&len_val->data.x_bigint); - ConstExprValue *ptr_val = &const_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val = const_val->data.x_struct.fields[slice_ptr_index]; if (ptr_val->special == ConstValSpecialUndef) { assert(len == 0); buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name)); return; } assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); - ConstExprValue *array = ptr_val->data.x_ptr.data.base_array.array_val; + ZigValue *array = ptr_val->data.x_ptr.data.base_array.array_val; size_t start = ptr_val->data.x_ptr.data.base_array.elem_index; render_const_val_array(g, buf, &type_entry->name, array, start, len); @@ -6801,11 +7041,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdErrorSet: return render_const_val_err_set(g, buf, const_val, type_entry); - case ZigTypeIdArgTuple: - { - buf_appendf(buf, "(args value)"); - return; - } case ZigTypeIdFnFrame: buf_appendf(buf, "(TODO: async function frame value)"); return; @@ -6868,7 +7103,6 @@ uint32_t type_id_hash(TypeId x) { case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: zig_unreachable(); @@ -6876,16 +7110,19 @@ uint32_t type_id_hash(TypeId x) { return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); case ZigTypeIdPointer: return hash_ptr(x.data.pointer.child_type) + - ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) + + (uint32_t)x.data.pointer.ptr_len * 1120226602u + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) + - (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881); + (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) + + (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881) * + (x.data.pointer.sentinel ? hash_const_val(x.data.pointer.sentinel) : (uint32_t)2955491856); case ZigTypeIdArray: - return hash_ptr(x.data.array.child_type) + - ((uint32_t)x.data.array.size ^ (uint32_t)2122979968); + return hash_ptr(x.data.array.child_type) * + ((uint32_t)x.data.array.size ^ (uint32_t)2122979968) * + (x.data.array.sentinel ? hash_const_val(x.data.array.sentinel) : (uint32_t)1927201585); case ZigTypeIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); @@ -6917,7 +7154,6 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: @@ -6934,10 +7170,30 @@ bool type_id_eql(TypeId a, TypeId b) { a.data.pointer.allow_zero == b.data.pointer.allow_zero && a.data.pointer.alignment == b.data.pointer.alignment && a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host && - a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes; + a.data.pointer.vector_index == b.data.pointer.vector_index && + a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes && + ( + a.data.pointer.sentinel == b.data.pointer.sentinel || + (a.data.pointer.sentinel != nullptr && b.data.pointer.sentinel != nullptr && + const_values_equal(a.data.pointer.codegen, a.data.pointer.sentinel, b.data.pointer.sentinel)) + ) && + ( + a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field || + (a.data.pointer.inferred_struct_field != nullptr && + b.data.pointer.inferred_struct_field != nullptr && + a.data.pointer.inferred_struct_field->inferred_struct_type == + b.data.pointer.inferred_struct_field->inferred_struct_type && + buf_eql_buf(a.data.pointer.inferred_struct_field->field_name, + b.data.pointer.inferred_struct_field->field_name)) + ); case ZigTypeIdArray: return a.data.array.child_type == b.data.array.child_type && - a.data.array.size == b.data.array.size; + a.data.array.size == b.data.array.size && + ( + a.data.array.sentinel == b.data.array.sentinel || + (a.data.array.sentinel != nullptr && b.data.array.sentinel != nullptr && + const_values_equal(a.data.array.codegen, a.data.array.sentinel, b.data.array.sentinel)) + ); case ZigTypeIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; @@ -7008,7 +7264,7 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { zig_unreachable(); } -static void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { +static void init_const_undefined(CodeGen *g, ZigValue *const_val) { Error err; ZigType *wanted_type = const_val->type; if (wanted_type->id == ZigTypeIdArray) { @@ -7021,10 +7277,10 @@ static void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { const_val->special = ConstValSpecialStatic; size_t field_count = wanted_type->data.structure.src_field_count; - const_val->data.x_struct.fields = create_const_vals(field_count); + const_val->data.x_struct.fields = alloc_const_vals_ptrs(field_count); for (size_t i = 0; i < field_count; i += 1) { - ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; - field_val->type = wanted_type->data.structure.fields[i].type_entry; + ZigValue *field_val = const_val->data.x_struct.fields[i]; + field_val->type = resolve_struct_field_type(g, wanted_type->data.structure.fields[i]); assert(field_val->type); init_const_undefined(g, field_val); field_val->parent.id = ConstParentIdStruct; @@ -7036,14 +7292,14 @@ static void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { } } -void expand_undef_struct(CodeGen *g, ConstExprValue *const_val) { +void expand_undef_struct(CodeGen *g, ZigValue *const_val) { if (const_val->special == ConstValSpecialUndef) { init_const_undefined(g, const_val); } } // Canonicalize the array value as ConstArraySpecialNone -void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { +void expand_undef_array(CodeGen *g, ZigValue *const_val) { size_t elem_count; ZigType *elem_type; if (const_val->type->id == ZigTypeIdArray) { @@ -7066,7 +7322,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { const_val->data.x_array.special = ConstArraySpecialNone; const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count); for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i]; + ZigValue *element_val = &const_val->data.x_array.data.s_none.elements[i]; element_val->type = elem_type; init_const_undefined(g, element_val); element_val->parent.id = ConstParentIdArray; @@ -7085,7 +7341,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { assert(elem_count == buf_len(buf)); const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count); for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *this_char = &const_val->data.x_array.data.s_none.elements[i]; + ZigValue *this_char = &const_val->data.x_array.data.s_none.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]); @@ -7120,7 +7376,6 @@ static const ZigTypeId all_type_ids[] = { ZigTypeIdUnion, ZigTypeIdFn, ZigTypeIdBoundFn, - ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdFnFrame, ZigTypeIdAnyFrame, @@ -7158,7 +7413,7 @@ size_t type_id_index(ZigType *entry) { case ZigTypeIdArray: return 7; case ZigTypeIdStruct: - if (entry->data.structure.is_slice) + if (entry->data.structure.special == StructSpecialSlice) return 6; return 8; case ZigTypeIdComptimeFloat: @@ -7183,18 +7438,16 @@ size_t type_id_index(ZigType *entry) { return 18; case ZigTypeIdBoundFn: return 19; - case ZigTypeIdArgTuple: - return 20; case ZigTypeIdOpaque: - return 21; + return 20; case ZigTypeIdFnFrame: - return 22; + return 21; case ZigTypeIdAnyFrame: - return 23; + return 22; case ZigTypeIdVector: - return 24; + return 23; case ZigTypeIdEnumLiteral: - return 25; + return 24; } zig_unreachable(); } @@ -7245,8 +7498,6 @@ const char *type_id_name(ZigTypeId id) { return "Fn"; case ZigTypeIdBoundFn: return "BoundFn"; - case ZigTypeIdArgTuple: - return "ArgTuple"; case ZigTypeIdOpaque: return "Opaque"; case ZigTypeIdVector: @@ -7303,19 +7554,67 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) { return a == b; } -ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { - Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name)); +uint32_t pkg_ptr_hash(const ZigPackage *ptr) { + return hash_ptr((void*)ptr); +} + +bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) { + return a == b; +} + +uint32_t tld_ptr_hash(const Tld *ptr) { + return hash_ptr((void*)ptr); +} + +bool tld_ptr_eql(const Tld *a, const Tld *b) { + return a == b; +} + +uint32_t node_ptr_hash(const AstNode *ptr) { + return hash_ptr((void*)ptr); +} + +bool node_ptr_eql(const AstNode *a, const AstNode *b) { + return a == b; +} + +uint32_t fn_ptr_hash(const ZigFn *ptr) { + return hash_ptr((void*)ptr); +} + +bool fn_ptr_eql(const ZigFn *a, const ZigFn *b) { + return a == b; +} + +uint32_t err_ptr_hash(const ErrorTableEntry *ptr) { + return hash_ptr((void*)ptr); +} + +bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b) { + return a == b; +} + +ZigValue *get_builtin_value(CodeGen *codegen, const char *name) { + ScopeDecls *builtin_scope = get_container_scope(codegen->compile_var_import); + Tld *tld = find_container_decl(codegen, builtin_scope, buf_create_from_str(name)); + assert(tld != nullptr); resolve_top_level_decl(codegen, tld, nullptr, false); - assert(tld->id == TldIdVar); + assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk); TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->const_value; + ZigValue *var_value = tld_var->var->const_value; assert(var_value != nullptr); return var_value; } +ZigType *get_builtin_type(CodeGen *codegen, const char *name) { + ZigValue *type_val = get_builtin_value(codegen, name); + assert(type_val->type->id == ZigTypeIdMetaType); + return type_val->data.x_type; +} + bool type_is_global_error_set(ZigType *err_set_type) { assert(err_set_type->id == ZigTypeIdErrorSet); - assert(err_set_type->data.error_set.infer_fn == nullptr); + assert(!err_set_type->data.error_set.incomplete); return err_set_type->data.error_set.err_count == UINT32_MAX; } @@ -7416,7 +7715,7 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size } X64CABIClass working_class = X64CABIClass_Unknown; for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry); if (field_class == X64CABIClass_Unknown) return X64CABIClass_Unknown; if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { @@ -7463,6 +7762,11 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) { return type_windows_abi_x86_64_class(g, ty, ty_size); + } else if (g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_aarch64_be) + { + X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size); + return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result; } else { return type_system_V_abi_x86_64_class(g, ty, ty_size); } @@ -7481,16 +7785,18 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) { assert(struct_type->id == ZigTypeIdStruct); - assert(type_is_resolved(struct_type, ResolveStatusSizeKnown)); + if (struct_type->data.structure.layout != ContainerLayoutAuto) { + assert(type_is_resolved(struct_type, ResolveStatusSizeKnown)); + } if (struct_type->data.structure.host_int_bytes == nullptr) return 0; return struct_type->data.structure.host_int_bytes[field->gen_index]; } Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ConstExprValue *const_val, ZigType *wanted_type) + ZigValue *const_val, ZigType *wanted_type) { - ConstExprValue ptr_val = {}; + ZigValue ptr_val = {}; ptr_val.special = ConstValSpecialStatic; ptr_val.type = get_pointer_to_type(codegen, wanted_type, true); ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst; @@ -7541,13 +7847,14 @@ Buf *type_h_name(ZigType *t) { static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { if (type->data.structure.resolve_status >= wanted_resolve_status) return; - ZigType *ptr_type = type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *ptr_type = type->data.structure.fields[slice_ptr_index]->type_entry; ZigType *child_type = ptr_type->data.pointer.child_type; ZigType *usize_type = g->builtin_types.entry_usize; bool done = false; if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero) + ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero || + ptr_type->data.pointer.sentinel != nullptr) { ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, PtrLenUnknown, 0, 0, 0, false); @@ -7563,10 +7870,11 @@ static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wa // If the child type is []const T then we need to make sure the type ref // and debug info is the same as if the child type were []T. if (is_slice(child_type)) { - ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index]->type_entry; assert(child_ptr_type->id == ZigTypeIdPointer); if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || - child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero) + child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero || + child_ptr_type->data.pointer.sentinel != nullptr) { ZigType *grand_child_type = child_ptr_type->data.pointer.child_type; ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, @@ -7700,7 +8008,6 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS ZigLLVMDIScope *di_scope; unsigned line; if (decl_node != nullptr) { - assert(decl_node->type == NodeTypeContainerDecl); Scope *scope = &struct_type->data.structure.decls_scope->base; ZigType *import = get_scope_import(scope); di_file = import->data.structure.root_struct->di_file; @@ -7741,7 +8048,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS // trigger all the recursive get_llvm_type calls for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; ZigType *field_type = field->type_entry; if (!type_has_bits(field_type)) continue; @@ -7755,8 +8062,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS // inserting padding bytes where LLVM would do it automatically. size_t llvm_struct_abi_align = 0; for (size_t i = 0; i < field_count; i += 1) { - ZigType *field_type = struct_type->data.structure.fields[i].type_entry; - if (!type_has_bits(field_type)) + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = field->type_entry; + if (field->is_comptime || !type_has_bits(field_type)) continue; LLVMTypeRef field_llvm_type = get_llvm_type(g, field_type); size_t llvm_field_abi_align = LLVMABIAlignmentOfType(g->target_data_ref, field_llvm_type); @@ -7764,10 +8072,10 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS } for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; ZigType *field_type = field->type_entry; - if (!type_has_bits(field_type)) { + if (field->is_comptime || !type_has_bits(field_type)) { field->gen_index = SIZE_MAX; continue; } @@ -7814,23 +8122,23 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS // find the next non-zero-byte field for offset calculations size_t next_src_field_index = i + 1; for (; next_src_field_index < field_count; next_src_field_index += 1) { - if (type_has_bits(struct_type->data.structure.fields[next_src_field_index].type_entry)) + if (type_has_bits(struct_type->data.structure.fields[next_src_field_index]->type_entry)) break; } size_t next_abi_align; if (next_src_field_index == field_count) { next_abi_align = struct_type->abi_align; } else { - if (struct_type->data.structure.fields[next_src_field_index].align == 0) { - next_abi_align = struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align; + if (struct_type->data.structure.fields[next_src_field_index]->align == 0) { + next_abi_align = struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align; } else { - next_abi_align = struct_type->data.structure.fields[next_src_field_index].align; + next_abi_align = struct_type->data.structure.fields[next_src_field_index]->align; } } size_t llvm_next_abi_align = (next_src_field_index == field_count) ? llvm_struct_abi_align : LLVMABIAlignmentOfType(g->target_data_ref, - get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index].type_entry)); + get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index]->type_entry)); size_t next_offset = next_field_offset(field->offset, struct_type->abi_align, field_type->abi_size, next_abi_align); @@ -7869,7 +8177,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS ZigLLVMDIType **di_element_types = allocate(debug_field_count); size_t debug_field_index = 0; for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; + TypeStructField *field = struct_type->data.structure.fields[i]; size_t gen_field_index = field->gen_index; if (gen_field_index == SIZE_MAX) { continue; @@ -7903,7 +8211,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS } unsigned line; if (decl_node != nullptr) { - AstNode *field_node = decl_node->data.container_decl.fields.at(i); + AstNode *field_node = field->decl_node; line = field_node->line + 1; } else { line = 0; @@ -8077,7 +8385,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size; if (padding_bytes > 0) { ZigType *u8_type = get_int_type(g, false, 8); - ZigType *padding_array = get_array_type(g, u8_type, padding_bytes); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); LLVMTypeRef union_element_types[] = { most_aligned_union_member->type_entry->llvm_type, get_llvm_type(g, padding_array), @@ -8111,7 +8419,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta union_type_ref = get_llvm_type(g, most_aligned_union_member->type_entry); } else { ZigType *u8_type = get_int_type(g, false, 8); - ZigType *padding_array = get_array_type(g, u8_type, padding_bytes); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); LLVMTypeRef union_element_types[] = { get_llvm_type(g, most_aligned_union_member->type_entry), get_llvm_type(g, padding_array), @@ -8191,11 +8499,21 @@ static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus if (type->data.pointer.is_const || type->data.pointer.is_volatile || type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle || - type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero) + type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero || + type->data.pointer.vector_index != VECTOR_INDEX_NONE || type->data.pointer.sentinel != nullptr) { assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl)); - ZigType *peer_type = get_pointer_to_type_extra(g, elem_type, false, false, - PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false); + ZigType *peer_type; + if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) { + peer_type = get_pointer_to_type_extra2(g, elem_type, false, false, + PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false, + VECTOR_INDEX_NONE, nullptr, nullptr); + } else { + uint32_t host_vec_len = type->data.pointer.host_int_bytes; + ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type); + peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); + } type->llvm_type = get_llvm_type(g, peer_type); type->llvm_di_type = get_llvm_di_type(g, peer_type); assertNoError(type_resolve(g, elem_type, wanted_resolve_status)); @@ -8245,7 +8563,8 @@ static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) { } } - type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name), type->size_in_bits, dwarf_tag); + type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name), + type->abi_size * 8, dwarf_tag); type->llvm_type = LLVMIntType(type->size_in_bits); } @@ -8422,14 +8741,16 @@ static void resolve_llvm_types_array(CodeGen *g, ZigType *type) { ZigType *elem_type = type->data.array.child_type; + uint64_t extra_len_from_sentinel = (type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = type->data.array.len + extra_len_from_sentinel; // TODO https://github.com/ziglang/zig/issues/1424 - type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)type->data.array.len); + type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)full_len); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type); type->llvm_di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits, - debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)type->data.array.len); + debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)full_len); } static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { @@ -8534,11 +8855,15 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); - fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); + fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace); fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); fn_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type, LLVMStoreSizeOfType(g->target_data_ref, fn_type->llvm_type), LLVMABIAlignmentOfType(g->target_data_ref, fn_type->llvm_type), ""); + + gen_param_types.deinit(); + param_di_types.deinit(); } void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { @@ -8573,6 +8898,9 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), gen_param_types.items, gen_param_types.length, false); fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); + + param_di_types.deinit(); + gen_param_types.deinit(); } static void resolve_llvm_types_anyerror(CodeGen *g) { @@ -8597,6 +8925,8 @@ static void resolve_llvm_types_anyerror(CodeGen *g) { tag_debug_align_in_bits, err_enumerators.items, err_enumerators.length, get_llvm_di_type(g, g->err_tag_type), ""); + + err_enumerators.deinit(); } static void resolve_llvm_types_async_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) { @@ -8635,7 +8965,8 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re ZigType *result_type = any_frame_type->data.any_frame.result_type; ZigType *ptr_result_type = (result_type == nullptr) ? nullptr : get_pointer_to_type(g, result_type, false); - LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, 0); + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); + LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, fn_addrspace); if (result_type == nullptr) { g->anyframe_fn_type = ptr_fn_llvm_type; } @@ -8742,6 +9073,9 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re nullptr, di_element_types.items, di_element_types.length, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type); + + field_types.deinit(); + di_element_types.deinit(); } static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { @@ -8755,7 +9089,6 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: zig_unreachable(); case ZigTypeIdFloat: case ZigTypeIdOpaque: @@ -8765,7 +9098,7 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r assert(type->llvm_di_type != nullptr); return; case ZigTypeIdStruct: - if (type->data.structure.is_slice) + if (type->data.structure.special == StructSpecialSlice) return resolve_llvm_types_slice(g, type, wanted_resolve_status); else return resolve_llvm_types_struct(g, type, wanted_resolve_status, nullptr); @@ -8843,10 +9176,160 @@ IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, alloca_gen->base.id = IrInstructionIdAllocaGen; alloca_gen->base.source_node = source_node; alloca_gen->base.scope = scope; - alloca_gen->base.value.type = get_pointer_to_type(g, var_type, false); + alloca_gen->base.value = allocate(1, "ZigValue"); + alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false); alloca_gen->base.ref_count = 1; alloca_gen->name_hint = name_hint; fn->alloca_gen_list.append(alloca_gen); return &alloca_gen->base; } +Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str, + ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path) +{ + Error err; + + Buf *search_dir; + ZigPackage *cur_scope_pkg = source_import->data.structure.root_struct->package; + assert(cur_scope_pkg); + ZigPackage *target_package; + auto package_entry = cur_scope_pkg->package_table.maybe_get(import_target_str); + SourceKind source_kind; + if (package_entry) { + target_package = package_entry->value; + *out_import_target_path = &target_package->root_src_path; + search_dir = &target_package->root_src_dir; + source_kind = SourceKindPkgMain; + } else { + // try it as a filename + target_package = cur_scope_pkg; + *out_import_target_path = import_target_str; + + // search relative to importing file + search_dir = buf_alloc(); + os_path_dirname(source_import->data.structure.root_struct->path, search_dir); + + source_kind = SourceKindNonRoot; + } + + buf_resize(out_full_path, 0); + os_path_join(search_dir, *out_import_target_path, out_full_path); + + Buf *import_code = buf_alloc(); + Buf *resolved_path = buf_alloc(); + + Buf *resolve_paths[] = { out_full_path, }; + *resolved_path = os_path_resolve(resolve_paths, 1); + + auto import_entry = g->import_table.maybe_get(resolved_path); + if (import_entry) { + *out_import = import_entry->value; + return ErrorNone; + } + + if (source_kind == SourceKindNonRoot) { + Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir; + Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); + if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) { + return ErrorImportOutsidePkgPath; + } + } + + if ((err = file_fetch(g, resolved_path, import_code))) { + return err; + } + + *out_import = add_source_file(g, target_package, resolved_path, import_code, source_kind); + return ErrorNone; +} + + +void IrExecutable::src() { + IrExecutable *it; + for (it = this; it != nullptr && it->source_node != nullptr; it = it->parent_exec) { + it->source_node->src(); + } +} + +bool is_anon_container(ZigType *ty) { + return ty->id == ZigTypeIdStruct && ( + ty->data.structure.special == StructSpecialInferredTuple || + ty->data.structure.special == StructSpecialInferredStruct); +} + +bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + +// Returns whether the x_optional field of ZigValue is active. +bool type_has_optional_repr(ZigType *ty) { + if (ty->id != ZigTypeIdOptional) { + return false; + } else if (get_codegen_ptr_type(ty) != nullptr) { + return false; + } else if (is_opt_err_set(ty)) { + return false; + } else { + return true; + } +} + +void copy_const_val(ZigValue *dest, ZigValue *src) { + memcpy(dest, src, sizeof(ZigValue)); + if (src->special != ConstValSpecialStatic) + return; + dest->parent.id = ConstParentIdNone; + if (dest->type->id == ZigTypeIdStruct) { + dest->data.x_struct.fields = alloc_const_vals_ptrs(dest->type->data.structure.src_field_count); + for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) { + copy_const_val(dest->data.x_struct.fields[i], src->data.x_struct.fields[i]); + dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct; + dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest; + dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i; + } + } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) { + dest->data.x_optional = create_const_vals(1); + copy_const_val(dest->data.x_optional, src->data.x_optional); + dest->data.x_optional->parent.id = ConstParentIdOptionalPayload; + dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest; + } +} + +bool type_is_numeric(ZigType *ty) { + switch (ty->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdUndefined: + return true; + + case ZigTypeIdVector: + return type_is_numeric(ty->data.vector.elem_type); + + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + case ZigTypeIdEnumLiteral: + return false; + } + zig_unreachable(); +} diff --git a/src/analyze.hpp b/src/analyze.hpp index a1e483357..f79dbbda4 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -17,10 +17,15 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, ZigType *new_type_table_entry(ZigTypeId id); ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn); ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); -ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, - bool is_volatile, PtrLen ptr_len, +ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, + bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count, bool allow_zero); +ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, + bool is_const, bool is_volatile, PtrLen ptr_len, + uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count, + bool allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field, + ZigValue *sentinel); uint64_t type_size(CodeGen *g, ZigType *type_entry); uint64_t type_size_bits(CodeGen *g, ZigType *type_entry); ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); @@ -29,7 +34,7 @@ ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type); ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); ZigType *get_optional_type(CodeGen *g, ZigType *child_type); -ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size); +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel); ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type); ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout); @@ -42,6 +47,8 @@ ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type); bool handle_is_ptr(ZigType *type_entry); bool type_has_bits(ZigType *type_entry); +Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result); + Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result); bool ptr_allows_addr_zero(ZigType *ptr_type); bool type_is_nonnull_ptr(ZigType *type); @@ -88,20 +95,21 @@ ZigType *get_scope_import(Scope *scope); ScopeTypeOf *get_scope_typeof(Scope *scope); void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); + bool is_const, ZigValue *init_value, Tld *src_tld, ZigType *var_type); ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); +void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type); ZigFn *create_fn(CodeGen *g, AstNode *proto_node); ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value); -void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc); +void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc); AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index); Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status); void complete_enum(CodeGen *g, ZigType *enum_type); bool ir_get_var_is_comptime(ZigVar *var); -bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b); -void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_val, bool is_max); +bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b); +void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max); void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max); -void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val); +void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val); ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent); @@ -116,65 +124,67 @@ Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruct Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent); -void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str); -ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); +void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str); +ZigValue *create_const_str_lit(CodeGen *g, Buf *str); -void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *c_str); -ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *c_str); +void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint); +ZigValue *create_const_bigint(ZigType *type, const BigInt *bigint); -void init_const_bigint(ConstExprValue *const_val, ZigType *type, const BigInt *bigint); -ConstExprValue *create_const_bigint(ZigType *type, const BigInt *bigint); +void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative); +ZigValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative); -void init_const_unsigned_negative(ConstExprValue *const_val, ZigType *type, uint64_t x, bool negative); -ConstExprValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative); +void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x); +ZigValue *create_const_signed(ZigType *type, int64_t x); -void init_const_signed(ConstExprValue *const_val, ZigType *type, int64_t x); -ConstExprValue *create_const_signed(ZigType *type, int64_t x); +void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x); +ZigValue *create_const_usize(CodeGen *g, uint64_t x); -void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x); -ConstExprValue *create_const_usize(CodeGen *g, uint64_t x); +void init_const_float(ZigValue *const_val, ZigType *type, double value); +ZigValue *create_const_float(ZigType *type, double value); -void init_const_float(ConstExprValue *const_val, ZigType *type, double value); -ConstExprValue *create_const_float(ZigType *type, double value); +void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag); +ZigValue *create_const_enum(ZigType *type, const BigInt *tag); -void init_const_enum(ConstExprValue *const_val, ZigType *type, const BigInt *tag); -ConstExprValue *create_const_enum(ZigType *type, const BigInt *tag); +void init_const_bool(CodeGen *g, ZigValue *const_val, bool value); +ZigValue *create_const_bool(CodeGen *g, bool value); -void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value); -ConstExprValue *create_const_bool(CodeGen *g, bool value); +void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value); +ZigValue *create_const_type(CodeGen *g, ZigType *type_value); -void init_const_type(CodeGen *g, ConstExprValue *const_val, ZigType *type_value); -ConstExprValue *create_const_type(CodeGen *g, ZigType *type_value); +void init_const_runtime(ZigValue *const_val, ZigType *type); +ZigValue *create_const_runtime(ZigType *type); -void init_const_runtime(ConstExprValue *const_val, ZigType *type); -ConstExprValue *create_const_runtime(ZigType *type); +void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const); +ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const); -void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const); -ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const); - -void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, ZigType *pointee_type, +void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type, size_t addr, bool is_const); -ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type, +ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type, size_t addr, bool is_const); -void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, +void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len); -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, +ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len); -void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, +void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val, size_t start, size_t len, bool is_const); -ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const); +ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const); -void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end); -ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end); +void init_const_null(ZigValue *const_val, ZigType *type); +ZigValue *create_const_null(ZigType *type); -ConstExprValue *create_const_vals(size_t count); +ZigValue *create_const_vals(size_t count); +ZigValue **alloc_const_vals_ptrs(size_t count); +ZigValue **realloc_const_vals_ptrs(ZigValue **ptr, size_t old_count, size_t new_count); + +TypeStructField **alloc_type_struct_fields(size_t count); +TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count); ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); -void expand_undef_array(CodeGen *g, ConstExprValue *const_val); -void expand_undef_struct(CodeGen *g, ConstExprValue *const_val); -void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); +void expand_undef_array(CodeGen *g, ZigValue *const_val); +void expand_undef_struct(CodeGen *g, ZigValue *const_val); +void update_compile_var(CodeGen *g, Buf *name, ZigValue *value); const char *type_id_name(ZigTypeId id); ZigTypeId type_id_at_index(size_t index); @@ -188,12 +198,13 @@ uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); ZigType *get_align_amt_type(CodeGen *g); ZigPackage *new_anonymous_package(void); -Buf *const_value_to_buffer(ConstExprValue *const_val); -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, bool ccc); +Buf *const_value_to_buffer(ZigValue *const_val); +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc); void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage); -ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); +ZigValue *get_builtin_value(CodeGen *codegen, const char *name); +ZigType *get_builtin_type(CodeGen *codegen, const char *name); ZigType *get_stack_trace_type(CodeGen *g); bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node); @@ -229,7 +240,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ConstExprValue *const_val, ZigType *wanted_type); + ZigValue *const_val, ZigType *wanted_type); void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn); Buf *type_bare_name(ZigType *t); @@ -243,17 +254,18 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa void src_assert(bool ok, AstNode *source_node); bool is_container(ZigType *type_entry); -ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, +ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name, UndefAllowed undef); void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn); bool fn_is_async(ZigFn *fn); +CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto); -Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align); -Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val, +Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align); +Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val, size_t *abi_size, size_t *size_in_bits); -Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type, - ConstExprValue *parent_type_val, bool *is_zero_bits); +Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type, + ZigValue *parent_type_val, bool *is_zero_bits); ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field); ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field); @@ -261,5 +273,12 @@ void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn); IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, ZigType *var_type, const char *name_hint); - +Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_target_str, + ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path); +ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry); +bool is_anon_container(ZigType *ty); +void copy_const_val(ZigValue *dest, ZigValue *src); +bool type_has_optional_repr(ZigType *ty); +bool is_opt_err_set(ZigType *ty); +bool type_is_numeric(ZigType *ty); #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 537a74d7b..d8d0d6c0e 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -147,9 +147,9 @@ static const char *token_to_ptr_len_str(Token *tok) { case TokenIdStar: case TokenIdStarStar: return "*"; - case TokenIdBracketStarBracket: + case TokenIdLBracket: return "[*]"; - case TokenIdBracketStarCBracket: + case TokenIdSymbol: return "[*c]"; default: zig_unreachable(); @@ -266,6 +266,10 @@ static const char *node_type_str(NodeType node_type) { return "AnyFrameType"; case NodeTypeEnumLiteral: return "EnumLiteral"; + case NodeTypeErrorSetField: + return "ErrorSetField"; + case NodeTypeVarFieldType: + return "VarFieldType"; } zig_unreachable(); } @@ -484,6 +488,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_grouped(ar, node->data.fn_proto.section_expr); fprintf(ar->f, ")"); } + if (node->data.fn_proto.callconv_expr) { + fprintf(ar->f, " callconv("); + render_node_grouped(ar, node->data.fn_proto.callconv_expr); + fprintf(ar->f, ")"); + } if (node->data.fn_proto.return_var_token != nullptr) { fprintf(ar->f, "var"); @@ -617,9 +626,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { break; case NodeTypeStringLiteral: { - if (node->data.string_literal.c) { - fprintf(ar->f, "c"); - } Buf tmp_buf = BUF_INIT; string_literal_escape(node->data.string_literal.buf, &tmp_buf); fprintf(ar->f, "\"%s\"", buf_ptr(&tmp_buf)); @@ -701,14 +707,29 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { switch (node->data.fn_call_expr.modifier) { case CallModifierNone: break; - case CallModifierBuiltin: - fprintf(ar->f, "@"); + case CallModifierNoAsync: + fprintf(ar->f, "noasync "); break; case CallModifierAsync: fprintf(ar->f, "async "); break; - case CallModifierNoAsync: - fprintf(ar->f, "noasync "); + case CallModifierNeverTail: + fprintf(ar->f, "notail "); + break; + case CallModifierNeverInline: + fprintf(ar->f, "noinline "); + break; + case CallModifierAlwaysTail: + fprintf(ar->f, "tail "); + break; + case CallModifierAlwaysInline: + fprintf(ar->f, "inline "); + break; + case CallModifierCompileTime: + fprintf(ar->f, "comptime "); + break; + case CallModifierBuiltin: + fprintf(ar->f, "@"); break; } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; @@ -819,7 +840,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { break; } case NodeTypeContainerInitExpr: - render_node_ungrouped(ar, node->data.container_init_expr.type); + if (node->data.container_init_expr.type != nullptr) { + render_node_ungrouped(ar, node->data.container_init_expr.type); + } if (node->data.container_init_expr.kind == ContainerInitKindStruct) { fprintf(ar->f, "{\n"); ar->indent += ar->indent_size; @@ -881,7 +904,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { AstNodeAsmExpr *asm_expr = &node->data.asm_expr; const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : ""; - fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(&asm_expr->asm_template->data.str_lit.str)); + fprintf(ar->f, "asm%s (", volatile_str); + render_node_ungrouped(ar, asm_expr->asm_template); + fprintf(ar->f, ")"); print_indent(ar); fprintf(ar->f, ": "); for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { @@ -1135,10 +1160,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) { AstNode *field_node = node->data.err_set_decl.decls.at(i); - assert(field_node->type == NodeTypeSymbol); - print_indent(ar); - print_symbol(ar, field_node->data.symbol_expr.symbol); - fprintf(ar->f, ",\n"); + switch (field_node->type) { + case NodeTypeSymbol: + print_indent(ar); + print_symbol(ar, field_node->data.symbol_expr.symbol); + fprintf(ar->f, ",\n"); + break; + case NodeTypeErrorSetField: + print_indent(ar); + print_symbol(ar, field_node->data.err_set_field.field_name->data.symbol_expr.symbol); + fprintf(ar->f, ",\n"); + break; + default: + zig_unreachable(); + } } ar->indent -= ar->indent_size; @@ -1173,10 +1208,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str)); break; } + case NodeTypeVarFieldType: { + fprintf(ar->f, "var"); + break; + } case NodeTypeParamDecl: case NodeTypeTestDecl: case NodeTypeStructField: case NodeTypeUsingNamespace: + case NodeTypeErrorSetField: zig_panic("TODO more ast rendering"); } } diff --git a/src/bigint.cpp b/src/bigint.cpp index b227b3b4f..1127033d0 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -195,6 +195,11 @@ void bigint_init_bigint(BigInt *dest, const BigInt *src) { memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count); } +void bigint_deinit(BigInt *bi) { + if (bi->digit_count > 1) + deallocate(bi->data.digits, bi->digit_count); +} + void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { float128_t zero; ui32_to_f128M(0, &zero); @@ -1754,3 +1759,27 @@ void bigint_incr(BigInt *x) { bigint_add(x, ©, &one); } +void bigint_decr(BigInt *x) { + if (x->digit_count == 0) { + bigint_init_signed(x, -1); + return; + } + + if (x->digit_count == 1) { + if (x->is_negative && x->data.digit != UINT64_MAX) { + x->data.digit += 1; + return; + } else if (!x->is_negative && x->data.digit != 0) { + x->data.digit -= 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt neg_one; + bigint_init_signed(&neg_one, -1); + + bigint_add(x, ©, &neg_one); +} diff --git a/src/bigint.hpp b/src/bigint.hpp index 276ccd64f..044ea6642 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -34,6 +34,7 @@ void bigint_init_signed(BigInt *dest, int64_t x); void bigint_init_bigint(BigInt *dest, const BigInt *src); void bigint_init_bigfloat(BigInt *dest, const BigFloat *op); void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative); +void bigint_deinit(BigInt *bi); // panics if number won't fit uint64_t bigint_as_u64(const BigInt *bigint); @@ -94,6 +95,7 @@ size_t bigint_bits_needed(const BigInt *op); Cmp bigint_cmp_zero(const BigInt *op); void bigint_incr(BigInt *value); +void bigint_decr(BigInt *value); bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result); diff --git a/src/buffer.hpp b/src/buffer.hpp index 251b5c2f2..6442e4f12 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -38,6 +38,12 @@ static inline char *buf_ptr(Buf *buf) { return buf->list.items; } +static inline const char *buf_ptr(const Buf *buf) { + assert(buf); + assert(buf->list.length); + return buf->list.items; +} + static inline void buf_resize(Buf *buf, size_t new_len) { buf->list.resize(new_len + 1); buf->list.at(buf_len(buf)) = 0; diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp deleted file mode 100644 index 55fde1900..000000000 --- a/src/c_tokenizer.cpp +++ /dev/null @@ -1,840 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "c_tokenizer.hpp" -#include - -#define WHITESPACE_EXCEPT_N \ - ' ': \ - case '\t': \ - case '\v': \ - case '\f' - -#define DIGIT_NON_ZERO \ - '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9' - -#define DIGIT \ - '0': \ - case DIGIT_NON_ZERO - -#define ALPHA \ - 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f': \ - case 'g': \ - case 'h': \ - case 'i': \ - case 'j': \ - case 'k': \ - case 'l': \ - case 'm': \ - case 'n': \ - case 'o': \ - case 'p': \ - case 'q': \ - case 'r': \ - case 's': \ - case 't': \ - case 'u': \ - case 'v': \ - case 'w': \ - case 'x': \ - case 'y': \ - case 'z': \ - case 'A': \ - case 'B': \ - case 'C': \ - case 'D': \ - case 'E': \ - case 'F': \ - case 'G': \ - case 'H': \ - case 'I': \ - case 'J': \ - case 'K': \ - case 'L': \ - case 'M': \ - case 'N': \ - case 'O': \ - case 'P': \ - case 'Q': \ - case 'R': \ - case 'S': \ - case 'T': \ - case 'U': \ - case 'V': \ - case 'W': \ - case 'X': \ - case 'Y': \ - case 'Z' - -#define IDENT_START \ - ALPHA: \ - case '_' - -#define IDENT \ - IDENT_START: \ - case DIGIT - -#define LINE_ENDING \ - '\r': \ - case '\n' - -static void begin_token(CTokenize *ctok, CTokId id) { - assert(ctok->cur_tok == nullptr); - ctok->tokens.add_one(); - ctok->cur_tok = &ctok->tokens.last(); - ctok->cur_tok->id = id; - - switch (id) { - case CTokIdStrLit: - memset(&ctok->cur_tok->data.str_lit, 0, sizeof(Buf)); - buf_resize(&ctok->cur_tok->data.str_lit, 0); - break; - case CTokIdSymbol: - memset(&ctok->cur_tok->data.symbol, 0, sizeof(Buf)); - buf_resize(&ctok->cur_tok->data.symbol, 0); - break; - case CTokIdNumLitInt: - ctok->cur_tok->data.num_lit_int.x = 0; - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixNone; - break; - case CTokIdCharLit: - case CTokIdNumLitFloat: - case CTokIdMinus: - case CTokIdLParen: - case CTokIdRParen: - case CTokIdEOF: - case CTokIdDot: - case CTokIdAsterisk: - case CTokIdBang: - case CTokIdTilde: - case CTokIdShl: - case CTokIdLt: - break; - } -} - -static void end_token(CTokenize *ctok) { - ctok->cur_tok = nullptr; -} - -static void mark_error(CTokenize *ctok) { - ctok->error = true; -} - -static void add_char(CTokenize *ctok, uint8_t c) { - assert(ctok->cur_tok); - if (ctok->cur_tok->id == CTokIdCharLit) { - ctok->cur_tok->data.char_lit = c; - ctok->state = CTokStateExpectEndQuot; - } else if (ctok->cur_tok->id == CTokIdStrLit) { - buf_append_char(&ctok->cur_tok->data.str_lit, c); - ctok->state = CTokStateString; - } else { - zig_unreachable(); - } -} - -static void hex_digit(CTokenize *ctok, uint8_t value) { - // TODO @mul_with_overflow - ctok->cur_tok->data.num_lit_int.x *= 16; - // TODO @add_with_overflow - ctok->cur_tok->data.num_lit_int.x += value; - - static const uint8_t hex_digit[] = "0123456789abcdef"; - buf_append_char(&ctok->buf, hex_digit[value]); -} - -static void end_float(CTokenize *ctok) { - // TODO detect errors, overflow, and underflow - double value = strtod(buf_ptr(&ctok->buf), nullptr); - - ctok->cur_tok->data.num_lit_float = value; - - end_token(ctok); - ctok->state = CTokStateStart; - -} - -void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) { - ctok->tokens.resize(0); - ctok->state = CTokStateStart; - ctok->error = false; - ctok->cur_tok = nullptr; - - buf_resize(&ctok->buf, 0); - - for (; *c; c += 1) { - switch (ctok->state) { - case CTokStateStart: - switch (*c) { - case WHITESPACE_EXCEPT_N: - break; - case '\'': - ctok->state = CTokStateExpectChar; - begin_token(ctok, CTokIdCharLit); - break; - case '\"': - ctok->state = CTokStateString; - begin_token(ctok, CTokIdStrLit); - break; - case '/': - ctok->state = CTokStateOpenComment; - break; - case '\\': - ctok->state = CTokStateBackslash; - break; - case LINE_ENDING: - goto found_end_of_macro; - case IDENT_START: - ctok->state = CTokStateIdentifier; - begin_token(ctok, CTokIdSymbol); - buf_append_char(&ctok->cur_tok->data.symbol, *c); - break; - case DIGIT_NON_ZERO: - ctok->state = CTokStateDecimal; - begin_token(ctok, CTokIdNumLitInt); - ctok->cur_tok->data.num_lit_int.x = *c - '0'; - buf_resize(&ctok->buf, 0); - buf_append_char(&ctok->buf, *c); - break; - case '0': - ctok->state = CTokStateGotZero; - begin_token(ctok, CTokIdNumLitInt); - ctok->cur_tok->data.num_lit_int.x = 0; - buf_resize(&ctok->buf, 0); - buf_append_char(&ctok->buf, '0'); - break; - case '.': - begin_token(ctok, CTokIdDot); - end_token(ctok); - break; - case '<': - begin_token(ctok, CTokIdLt); - ctok->state = CTokStateGotLt; - break; - case '(': - begin_token(ctok, CTokIdLParen); - end_token(ctok); - break; - case ')': - begin_token(ctok, CTokIdRParen); - end_token(ctok); - break; - case '*': - begin_token(ctok, CTokIdAsterisk); - end_token(ctok); - break; - case '-': - begin_token(ctok, CTokIdMinus); - end_token(ctok); - break; - case '!': - begin_token(ctok, CTokIdBang); - end_token(ctok); - break; - case '~': - begin_token(ctok, CTokIdTilde); - end_token(ctok); - break; - default: - return mark_error(ctok); - } - break; - case CTokStateGotLt: - switch (*c) { - case '<': - ctok->cur_tok->id = CTokIdShl; - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateFloat: - switch (*c) { - case '.': - break; - case 'e': - case 'E': - buf_append_char(&ctok->buf, 'e'); - ctok->state = CTokStateExpSign; - break; - case 'f': - case 'F': - case 'l': - case 'L': - end_float(ctok); - break; - case DIGIT: - buf_append_char(&ctok->buf, *c); - break; - default: - c -= 1; - end_float(ctok); - continue; - } - break; - case CTokStateExpSign: - switch (*c) { - case '+': - case '-': - ctok->state = CTokStateFloatExpFirst; - buf_append_char(&ctok->buf, *c); - break; - case DIGIT: - ctok->state = CTokStateFloatExp; - buf_append_char(&ctok->buf, *c); - break; - default: - return mark_error(ctok); - } - break; - case CTokStateFloatExpFirst: - switch (*c) { - case DIGIT: - buf_append_char(&ctok->buf, *c); - ctok->state = CTokStateFloatExp; - break; - default: - return mark_error(ctok); - } - break; - case CTokStateFloatExp: - switch (*c) { - case DIGIT: - buf_append_char(&ctok->buf, *c); - break; - case 'f': - case 'F': - case 'l': - case 'L': - end_float(ctok); - break; - default: - c -= 1; - end_float(ctok); - continue; - } - break; - case CTokStateDecimal: - switch (*c) { - case DIGIT: - buf_append_char(&ctok->buf, *c); - - // TODO @mul_with_overflow - ctok->cur_tok->data.num_lit_int.x *= 10; - // TODO @add_with_overflow - ctok->cur_tok->data.num_lit_int.x += *c - '0'; - break; - case '\'': - break; - case 'u': - case 'U': - ctok->state = CTokStateNumLitIntSuffixU; - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixU; - break; - case 'l': - case 'L': - ctok->state = CTokStateNumLitIntSuffixL; - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixL; - break; - case '.': - buf_append_char(&ctok->buf, '.'); - ctok->cur_tok->id = CTokIdNumLitFloat; - ctok->state = CTokStateFloat; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateGotZero: - switch (*c) { - case 'x': - case 'X': - ctok->state = CTokStateHex; - break; - case '.': - ctok->state = CTokStateFloat; - ctok->cur_tok->id = CTokIdNumLitFloat; - buf_append_char(&ctok->buf, '.'); - break; - case 'l': - case 'L': - case 'u': - case 'U': - c -= 1; - ctok->state = CTokStateDecimal; - continue; - default: - c -= 1; - ctok->state = CTokStateOctal; - continue; - } - break; - case CTokStateOctal: - switch (*c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - // TODO @mul_with_overflow - ctok->cur_tok->data.num_lit_int.x *= 8; - // TODO @add_with_overflow - ctok->cur_tok->data.num_lit_int.x += *c - '0'; - break; - case '8': - case '9': - return mark_error(ctok); - case '\'': - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateHex: - switch (*c) { - case '0': - hex_digit(ctok, 0); - break; - case '1': - hex_digit(ctok, 1); - break; - case '2': - hex_digit(ctok, 2); - break; - case '3': - hex_digit(ctok, 3); - break; - case '4': - hex_digit(ctok, 4); - break; - case '5': - hex_digit(ctok, 5); - break; - case '6': - hex_digit(ctok, 6); - break; - case '7': - hex_digit(ctok, 7); - break; - case '8': - hex_digit(ctok, 8); - break; - case '9': - hex_digit(ctok, 9); - break; - case 'a': - case 'A': - hex_digit(ctok, 10); - break; - case 'b': - case 'B': - hex_digit(ctok, 11); - break; - case 'c': - case 'C': - hex_digit(ctok, 12); - break; - case 'd': - case 'D': - hex_digit(ctok, 13); - break; - case 'e': - case 'E': - hex_digit(ctok, 14); - break; - case 'f': - case 'F': - hex_digit(ctok, 15); - break; - case 'p': - case 'P': - ctok->cur_tok->id = CTokIdNumLitFloat; - ctok->state = CTokStateExpSign; - break; - case 'u': - case 'U': - // marks the number literal as unsigned - ctok->state = CTokStateNumLitIntSuffixU; - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixU; - break; - case 'l': - case 'L': - // marks the number literal as long - ctok->state = CTokStateNumLitIntSuffixL; - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixL; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateNumLitIntSuffixU: - switch (*c) { - case 'l': - case 'L': - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixLU; - ctok->state = CTokStateNumLitIntSuffixUL; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateNumLitIntSuffixL: - switch (*c) { - case 'l': - case 'L': - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixLL; - ctok->state = CTokStateNumLitIntSuffixLL; - break; - case 'u': - case 'U': - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixLU; - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateNumLitIntSuffixLL: - switch (*c) { - case 'u': - case 'U': - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixLLU; - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateNumLitIntSuffixUL: - switch (*c) { - case 'l': - case 'L': - ctok->cur_tok->data.num_lit_int.suffix = CNumLitSuffixLLU; - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateIdentifier: - switch (*c) { - case IDENT: - buf_append_char(&ctok->cur_tok->data.symbol, *c); - break; - default: - c -= 1; - end_token(ctok); - ctok->state = CTokStateStart; - continue; - } - break; - case CTokStateString: - switch (*c) { - case '\\': - ctok->state = CTokStateCharEscape; - break; - case '\"': - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - buf_append_char(&ctok->cur_tok->data.str_lit, *c); - } - break; - case CTokStateExpectChar: - switch (*c) { - case '\\': - ctok->state = CTokStateCharEscape; - break; - case '\'': - return mark_error(ctok); - default: - ctok->cur_tok->data.char_lit = *c; - ctok->state = CTokStateExpectEndQuot; - } - break; - case CTokStateCharEscape: - switch (*c) { - case '\'': - case '"': - case '?': - case '\\': - add_char(ctok, *c); - break; - case 'a': - add_char(ctok, '\a'); - break; - case 'b': - add_char(ctok, '\b'); - break; - case 'f': - add_char(ctok, '\f'); - break; - case 'n': - add_char(ctok, '\n'); - break; - case 'r': - add_char(ctok, '\r'); - break; - case 't': - add_char(ctok, '\t'); - break; - case 'v': - add_char(ctok, '\v'); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - ctok->state = CTokStateStrOctal; - ctok->cur_char = (uint8_t)(*c - '0'); - ctok->octal_index = 1; - break; - case 'x': - ctok->state = CTokStateStrHex; - ctok->cur_char = 0; - break; - case 'u': - zig_panic("TODO unicode"); - break; - case 'U': - zig_panic("TODO Unicode"); - break; - default: - return mark_error(ctok); - } - break; - case CTokStateStrHex: { - uint8_t value = 0; - switch (*c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - value = *c - '0'; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - value = (*c - 'a') + 10; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - value = (*c - 'A') + 10; - break; - default: - c -= 1; - add_char(ctok, ctok->cur_char); - continue; - } - // TODO @mul_with_overflow - if (((long)ctok->cur_char) * 16 >= 256) { - zig_panic("TODO str hex mul overflow"); - } - ctok->cur_char = (uint8_t)(ctok->cur_char * (uint8_t)16); - // TODO @add_with_overflow - if (((long)ctok->cur_char) + (long)(value) >= 256) { - zig_panic("TODO str hex add overflow"); - } - ctok->cur_char = (uint8_t)(ctok->cur_char + value); - break; - } - case CTokStateStrOctal: - switch (*c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - // TODO @mul_with_overflow - if (((long)ctok->cur_char) * 8 >= 256) { - zig_panic("TODO"); - } - ctok->cur_char = (uint8_t)(ctok->cur_char * (uint8_t)8); - // TODO @add_with_overflow - if (((long)ctok->cur_char) + (long)(*c - '0') >= 256) { - zig_panic("TODO"); - } - ctok->cur_char = (uint8_t)(ctok->cur_char + (uint8_t)(*c - '0')); - ctok->octal_index += 1; - if (ctok->octal_index == 3) { - add_char(ctok, ctok->cur_char); - } - break; - default: - c -= 1; - add_char(ctok, ctok->cur_char); - continue; - } - break; - case CTokStateExpectEndQuot: - switch (*c) { - case '\'': - end_token(ctok); - ctok->state = CTokStateStart; - break; - default: - return mark_error(ctok); - } - break; - case CTokStateOpenComment: - switch (*c) { - case '/': - ctok->state = CTokStateLineComment; - break; - case '*': - ctok->state = CTokStateComment; - break; - default: - return mark_error(ctok); - } - break; - case CTokStateLineComment: - if (*c == '\n') { - ctok->state = CTokStateStart; - goto found_end_of_macro; - } - break; - case CTokStateComment: - switch (*c) { - case '*': - ctok->state = CTokStateCommentStar; - break; - default: - break; - } - break; - case CTokStateCommentStar: - switch (*c) { - case '/': - ctok->state = CTokStateStart; - break; - case '*': - break; - default: - ctok->state = CTokStateComment; - break; - } - break; - case CTokStateBackslash: - switch (*c) { - case '\n': - ctok->state = CTokStateStart; - break; - default: - return mark_error(ctok); - } - break; - } - } -found_end_of_macro: - - switch (ctok->state) { - case CTokStateStart: - break; - case CTokStateIdentifier: - case CTokStateDecimal: - case CTokStateHex: - case CTokStateOctal: - case CTokStateGotZero: - case CTokStateNumLitIntSuffixU: - case CTokStateNumLitIntSuffixL: - case CTokStateNumLitIntSuffixUL: - case CTokStateNumLitIntSuffixLL: - case CTokStateGotLt: - end_token(ctok); - break; - case CTokStateFloat: - case CTokStateFloatExp: - end_float(ctok); - break; - case CTokStateExpectChar: - case CTokStateExpectEndQuot: - case CTokStateOpenComment: - case CTokStateLineComment: - case CTokStateComment: - case CTokStateCommentStar: - case CTokStateCharEscape: - case CTokStateBackslash: - case CTokStateString: - case CTokStateExpSign: - case CTokStateFloatExpFirst: - case CTokStateStrHex: - case CTokStateStrOctal: - return mark_error(ctok); - } - - assert(ctok->cur_tok == nullptr); - - begin_token(ctok, CTokIdEOF); - end_token(ctok); -} diff --git a/src/c_tokenizer.hpp b/src/c_tokenizer.hpp deleted file mode 100644 index eaca09098..000000000 --- a/src/c_tokenizer.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - - -#ifndef ZIG_C_TOKENIZER_HPP -#define ZIG_C_TOKENIZER_HPP - -#include "buffer.hpp" - -enum CTokId { - CTokIdCharLit, - CTokIdStrLit, - CTokIdNumLitInt, - CTokIdNumLitFloat, - CTokIdSymbol, - CTokIdMinus, - CTokIdLParen, - CTokIdRParen, - CTokIdEOF, - CTokIdDot, - CTokIdAsterisk, - CTokIdBang, - CTokIdTilde, - CTokIdShl, - CTokIdLt, -}; - -enum CNumLitSuffix { - CNumLitSuffixNone, - CNumLitSuffixL, - CNumLitSuffixU, - CNumLitSuffixLU, - CNumLitSuffixLL, - CNumLitSuffixLLU, -}; - -struct CNumLitInt { - uint64_t x; - CNumLitSuffix suffix; -}; - -struct CTok { - enum CTokId id; - union { - uint8_t char_lit; - Buf str_lit; - CNumLitInt num_lit_int; - double num_lit_float; - Buf symbol; - } data; -}; - -enum CTokState { - CTokStateStart, - CTokStateExpectChar, - CTokStateCharEscape, - CTokStateExpectEndQuot, - CTokStateOpenComment, - CTokStateLineComment, - CTokStateComment, - CTokStateCommentStar, - CTokStateBackslash, - CTokStateString, - CTokStateIdentifier, - CTokStateDecimal, - CTokStateOctal, - CTokStateGotZero, - CTokStateHex, - CTokStateFloat, - CTokStateExpSign, - CTokStateFloatExp, - CTokStateFloatExpFirst, - CTokStateStrHex, - CTokStateStrOctal, - CTokStateNumLitIntSuffixU, - CTokStateNumLitIntSuffixL, - CTokStateNumLitIntSuffixLL, - CTokStateNumLitIntSuffixUL, - CTokStateGotLt, -}; - -struct CTokenize { - ZigList tokens; - CTokState state; - bool error; - CTok *cur_tok; - Buf buf; - uint8_t cur_char; - int octal_index; -}; - -void tokenize_c_macro(CTokenize *ctok, const uint8_t *c); - -#endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f32e3dd3..cb7bbaec4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -15,11 +15,11 @@ #include "hash_map.hpp" #include "ir.hpp" #include "os.hpp" -#include "translate_c.hpp" #include "target.hpp" #include "util.hpp" #include "zig_llvm.h" #include "userland.h" +#include "dump_analysis.hpp" #include #include @@ -184,16 +184,17 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) { } -static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); -static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name); -static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); +static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name); +static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name); +static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name); static void generate_error_name_table(CodeGen *g); -static bool value_is_all_undef(CodeGen *g, ConstExprValue *const_val); +static bool value_is_all_undef(CodeGen *g, ZigValue *const_val); static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment); static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_instr, LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, LLVMValueRef result_loc, bool non_async); +static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -262,36 +263,66 @@ static const char *get_mangled_name(CodeGen *g, const char *original_name, bool } } -static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { +static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { switch (cc) { - case CallingConventionUnspecified: return LLVMFastCallConv; - case CallingConventionC: return LLVMCCallConv; + case CallingConventionUnspecified: + return ZigLLVM_Fast; + case CallingConventionC: + return ZigLLVM_C; case CallingConventionCold: - // cold calling convention only works on x86. - if (g->zig_target->arch == ZigLLVM_x86 || - g->zig_target->arch == ZigLLVM_x86_64) - { - // cold calling convention is not supported on windows - if (g->zig_target->os == OsWindows) { - return LLVMCCallConv; - } else { - return LLVMColdCallConv; - } - } else { - return LLVMCCallConv; - } - break; + if ((g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64) && + g->zig_target->os != OsWindows) + return ZigLLVM_Cold; + return ZigLLVM_C; case CallingConventionNaked: zig_unreachable(); case CallingConventionStdcall: - // stdcall calling convention only works on x86. - if (g->zig_target->arch == ZigLLVM_x86) { - return LLVMX86StdcallCallConv; - } else { - return LLVMCCallConv; - } + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_StdCall; + return ZigLLVM_C; + case CallingConventionFastcall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_FastCall; + return ZigLLVM_C; + case CallingConventionVectorcall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_VectorCall; + if (target_is_arm(g->zig_target) && + target_arch_pointer_bit_width(g->zig_target->arch) == 64) + return ZigLLVM_AArch64_VectorCall; + return ZigLLVM_C; + case CallingConventionThiscall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_ThisCall; + return ZigLLVM_C; case CallingConventionAsync: - return LLVMFastCallConv; + return ZigLLVM_Fast; + case CallingConventionAPCS: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_APCS; + return ZigLLVM_C; + case CallingConventionAAPCS: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_AAPCS; + return ZigLLVM_C; + case CallingConventionAAPCSVFP: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_AAPCS_VFP; + return ZigLLVM_C; + case CallingConventionInterrupt: + if (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64) + return ZigLLVM_X86_INTR; + if (g->zig_target->arch == ZigLLVM_avr) + return ZigLLVM_AVR_INTR; + if (g->zig_target->arch == ZigLLVM_msp430) + return ZigLLVM_MSP430_INTR; + return ZigLLVM_C; + case CallingConventionSignal: + if (g->zig_target->arch == ZigLLVM_avr) + return ZigLLVM_AVR_SIGNAL; + return ZigLLVM_C; } zig_unreachable(); } @@ -383,7 +414,15 @@ static bool cc_want_sret_attr(CallingConvention cc) { zig_unreachable(); case CallingConventionC: case CallingConventionCold: + case CallingConventionInterrupt: + case CallingConventionSignal: case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: return true; case CallingConventionAsync: case CallingConventionUnspecified: @@ -412,27 +451,19 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { linkage = fn_export->linkage; } - bool external_linkage = linkage != GlobalLinkageIdInternal; CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; - if (cc == CallingConventionStdcall && external_linkage && - g->zig_target->arch == ZigLLVM_x86) - { - // prevent llvm name mangling - symbol_name = buf_ptr(buf_sprintf("\x01_%s", symbol_name)); - } - bool is_async = fn_is_async(fn); - ZigType *fn_type = fn->type_entry; // Make the raw_type_ref populated resolve_llvm_types_fn(g, fn); LLVMTypeRef fn_llvm_type = fn->raw_type_ref; LLVMValueRef llvm_fn = nullptr; if (fn->body_node == nullptr) { + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, symbol_name); if (existing_llvm_fn) { - return LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, 0)); + return LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, fn_addrspace)); } else { Buf *buf_symbol_name = buf_create_from_str(symbol_name); auto entry = g->exported_symbol_names.maybe_get(buf_symbol_name); @@ -455,7 +486,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { resolve_llvm_types_fn(g, tld_fn->fn_entry); tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, symbol_name, tld_fn->fn_entry->raw_type_ref); - llvm_fn = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, LLVMPointerType(fn_llvm_type, 0)); + llvm_fn = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, LLVMPointerType(fn_llvm_type, fn_addrspace)); return llvm_fn; } } @@ -488,7 +519,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { if (cc == CallingConventionNaked) { addLLVMFnAttr(llvm_fn, "naked"); } else { - LLVMSetFunctionCallConv(llvm_fn, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); + ZigLLVMFunctionSetCallingConv(llvm_fn, get_llvm_cc(g, cc)); } bool want_cold = fn->is_cold || cc == CallingConventionCold; @@ -772,7 +803,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn name = "fma"; num_args = 3; } else if (fn_id == ZigLLVMFnIdFloatOp) { - name = float_op_to_name(op, true); + name = float_op_to_name(op); num_args = 1; } else { zig_unreachable(); @@ -846,6 +877,11 @@ static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type } } +static void ir_assert(bool ok, IrInstruction *source_instruction) { + if (ok) return; + src_assert(ok, source_instruction->source_node); +} + static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { // TODO memoize Scope *scope = instruction->scope; @@ -941,28 +977,32 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("resumed an async function which can only be awaited"); case PanicMsgIdBadNoAsyncCall: return buf_create_from_str("async function called with noasync suspended"); + case PanicMsgIdResumeNotSuspendedFn: + return buf_create_from_str("resumed a non-suspended function"); + case PanicMsgIdBadSentinel: + return buf_create_from_str("sentinel mismatch"); } zig_unreachable(); } static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { - ConstExprValue *val = &g->panic_msg_vals[msg_id]; - if (!val->global_refs->llvm_global) { + ZigValue *val = &g->panic_msg_vals[msg_id]; + if (!val->llvm_global) { Buf *buf_msg = panic_msg_buf(msg_id); - ConstExprValue *array_val = create_const_str_lit(g, buf_msg); + ZigValue *array_val = create_const_str_lit(g, buf_msg)->data.x_ptr.data.ref.pointee; init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true); render_const_val(g, val, ""); render_const_val_global(g, val, ""); - assert(val->global_refs->llvm_global); + assert(val->llvm_global); } ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); ZigType *str_type = get_slice_type(g, u8_ptr_type); - return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(get_llvm_type(g, str_type), 0)); + return LLVMConstBitCast(val->llvm_global, LLVMPointerType(get_llvm_type(g, str_type), 0)); } static ZigType *ptr_to_stack_trace_type(CodeGen *g) { @@ -974,7 +1014,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace { assert(g->panic_fn != nullptr); LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); - LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); + ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); if (stack_trace_arg == nullptr) { stack_trace_arg = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); } @@ -982,7 +1022,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace msg_arg, stack_trace_arg, }; - ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); + ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_CallAttrAuto, ""); if (!stack_trace_is_llvm_alloca) { // The stack trace argument is not in the stack of the caller, so // we'd like to set tail call here, but because slices (the type of msg_arg) are @@ -1085,7 +1125,7 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); addLLVMFnAttr(fn_val, "alwaysinline"); LLVMSetLinkage(fn_val, LLVMInternalLinkage); - LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); // Error return trace memory is in the stack, which is impossible to be at address 0 @@ -1109,15 +1149,15 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); LLVMValueRef address_value = LLVMGetParam(fn_val, 1); - size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); - size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, ""); - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); - size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); @@ -1166,7 +1206,7 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address addLLVMFnAttr(fn_val, "cold"); LLVMSetLinkage(fn_val, LLVMInternalLinkage); - LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); if (codegen_have_frame_pointer(g)) { @@ -1202,7 +1242,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { LLVMPositionBuilderAtEnd(g->builder, dest_non_null_block); LLVMValueRef args[] = { err_ret_trace_ptr, return_address }; - ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, ""); + ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); LLVMBuildRetVoid(g->builder); LLVMPositionBuilderAtEnd(g->builder, prev_block); @@ -1228,7 +1269,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef msg_prefix_init = LLVMConstString(unwrap_err_msg_text, strlen(unwrap_err_msg_text), 1); LLVMValueRef msg_prefix = LLVMAddGlobal(g->module, LLVMTypeOf(msg_prefix_init), ""); LLVMSetInitializer(msg_prefix, msg_prefix_init); - LLVMSetLinkage(msg_prefix, LLVMInternalLinkage); + LLVMSetLinkage(msg_prefix, LLVMPrivateLinkage); LLVMSetGlobalConstant(msg_prefix, true); const char *fn_name = get_mangled_name(g, "__zig_fail_unwrap", false); @@ -1249,7 +1290,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { addLLVMFnAttr(fn_val, "noreturn"); addLLVMFnAttr(fn_val, "cold"); LLVMSetLinkage(fn_val, LLVMInternalLinkage); - LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); if (codegen_have_frame_pointer(g)) { @@ -1371,13 +1412,13 @@ static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *sc err_val, }; call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); } else { LLVMValueRef args[] = { err_val, }; call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); } if (!is_llvm_alloca) { LLVMSetTailCall(call_instruction, true); @@ -1418,6 +1459,27 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMPositionBuilderAtEnd(g->builder, ok_block); } +static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) { + LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, ""); + + LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, ""); + LLVMValueRef ok_bit; + if (sentinel->type->id == ZigTypeIdFloat) { + ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, actual_sentinel, expected_sentinel, ""); + } else { + ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, ""); + } + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdBadSentinel); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); +} + static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); @@ -1641,6 +1703,17 @@ static void gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, return; } + assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME); + if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), + ptr_type->data.pointer.vector_index, false); + LLVMValueRef loaded_vector = gen_load(g, ptr, ptr_type, ""); + LLVMValueRef new_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, + index_val, ""); + gen_store(g, new_vector, ptr, ptr_type); + return; + } + uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; if (host_int_bytes == 0) { gen_store(g, value, ptr, ptr_type); @@ -1689,42 +1762,48 @@ static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { } static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { - if (!type_has_bits(instruction->value.type)) + Error err; + + bool value_has_bits; + if ((err = type_has_bits2(g, instruction->value->type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (!value_has_bits) return nullptr; + if (!instruction->llvm_value) { if (instruction->id == IrInstructionIdAwaitGen) { IrInstructionAwaitGen *await = reinterpret_cast(instruction); if (await->result_loc != nullptr) { return get_handle_value(g, ir_llvm_value(g, await->result_loc), - await->result_loc->value.type->data.pointer.child_type, await->result_loc->value.type); + await->result_loc->value->type->data.pointer.child_type, await->result_loc->value->type); } } if (instruction->spill != nullptr) { - ZigType *ptr_type = instruction->spill->value.type; - src_assert(ptr_type->id == ZigTypeIdPointer, instruction->source_node); + ZigType *ptr_type = instruction->spill->value->type; + ir_assert(ptr_type->id == ZigTypeIdPointer, instruction); return get_handle_value(g, ir_llvm_value(g, instruction->spill), - ptr_type->data.pointer.child_type, instruction->spill->value.type); + ptr_type->data.pointer.child_type, instruction->spill->value->type); } - src_assert(instruction->value.special != ConstValSpecialRuntime, instruction->source_node); - assert(instruction->value.type); - render_const_val(g, &instruction->value, ""); + ir_assert(instruction->value->special != ConstValSpecialRuntime, instruction); + assert(instruction->value->type); + render_const_val(g, instruction->value, ""); // we might have to do some pointer casting here due to the way union // values are rendered with a type other than the one we expect - if (handle_is_ptr(instruction->value.type)) { - render_const_val_global(g, &instruction->value, ""); - ZigType *ptr_type = get_pointer_to_type(g, instruction->value.type, true); - instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value.global_refs->llvm_global, get_llvm_type(g, ptr_type), ""); + if (handle_is_ptr(instruction->value->type)) { + render_const_val_global(g, instruction->value, ""); + ZigType *ptr_type = get_pointer_to_type(g, instruction->value->type, true); + instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_global, get_llvm_type(g, ptr_type), ""); } else { - instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value.global_refs->llvm_value, - get_llvm_type(g, instruction->value.type), ""); + instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_value, + get_llvm_type(g, instruction->value->type), ""); } assert(instruction->llvm_value); } return instruction->llvm_value; } -ATTRIBUTE_NORETURN -static void report_errors_and_exit(CodeGen *g) { +void codegen_report_errors_and_exit(CodeGen *g) { assert(g->errors.length != 0); for (size_t i = 0; i < g->errors.length; i += 1) { ErrorMsg *err = g->errors.at(i); @@ -1735,7 +1814,7 @@ static void report_errors_and_exit(CodeGen *g) { static void report_errors_and_maybe_exit(CodeGen *g) { if (g->errors.length != 0) { - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } } @@ -1745,7 +1824,7 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); add_error_note(g, msg, source_node, buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { @@ -1778,7 +1857,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ if (src_i >= fn_walk->data.call.inst->arg_count) return false; IrInstruction *arg = fn_walk->data.call.inst->args[src_i]; - ty = arg->value.type; + ty = arg->value->type; source_node = arg->source_node; val = ir_llvm_value(g, arg); break; @@ -1854,51 +1933,64 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ return true; } - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - assert(handle_is_ptr(ty)); - switch (fn_walk->id) { - case FnWalkIdAttrs: - // arrays passed to C ABI functions may not be at address 0 - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); - break; - } - case FnWalkIdVars: { - var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); - di_arg_index = fn_walk->data.vars.gen_i; - dest_ty = get_pointer_to_type(g, ty, false); - fn_walk->data.vars.gen_i += 1; - goto var_ok; - } - case FnWalkIdInits: - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } - return true; - } - - if (g->zig_target->arch == ZigLLVM_x86_64) { - X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); - size_t ty_size = type_size(g, ty); - if (abi_class == X64CABIClass_MEMORY) { + { + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { assert(handle_is_ptr(ty)); switch (fn_walk->id) { case FnWalkIdAttrs: - ZigLLVMAddByValAttr(llvm_fn, fn_walk->data.attrs.gen_i + 1, get_llvm_type(g, ty)); + // arrays passed to C ABI functions may not be at address 0 + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); + break; + } + case FnWalkIdVars: { + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + di_arg_index = fn_walk->data.vars.gen_i; + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } + + X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); + size_t ty_size = type_size(g, ty); + if (abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval) { + assert(handle_is_ptr(ty)); + switch (fn_walk->id) { + case FnWalkIdAttrs: + if (abi_class != X64CABIClass_MEMORY_nobyval) { + ZigLLVMAddByValAttr(llvm_fn, fn_walk->data.attrs.gen_i + 1, get_llvm_type(g, ty)); + addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + } else if (g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_aarch64_be) + { + // no attrs needed + } else { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + // Byvalue parameters must not have address 0 addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); fn_walk->data.attrs.gen_i += 1; @@ -2003,7 +2095,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { bool is_var_args = fn_walk->data.call.is_var_args; for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { IrInstruction *param_instruction = instruction->args[call_i]; - ZigType *param_type = param_instruction->value.type; + ZigType *param_type = param_instruction->value->type; if (is_var_args || type_has_bits(param_type)) { LLVMValueRef param_value = ir_llvm_value(g, param_instruction); assert(param_value); @@ -2094,7 +2186,7 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { const char *fn_name = get_mangled_name(g, "__zig_merge_error_return_traces", false); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); LLVMSetLinkage(fn_val, LLVMInternalLinkage); - LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); @@ -2150,16 +2242,16 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { LLVMBuildCondBr(g->builder, null_bit, return_block, non_null_block); LLVMPositionBuilderAtEnd(g->builder, non_null_block); - size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; - size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; + size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, (unsigned)src_index_field_index, ""); LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, (unsigned)src_addresses_field_index, ""); - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, ""); - size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, ""); LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, ""); LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, ""); @@ -2190,7 +2282,7 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, ""); LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, ""); LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val}; - ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, ""); + ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, ""); LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, ""); LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, ""); @@ -2227,7 +2319,7 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope, &is_llvm_alloca); ZigLLVMBuildCall(g->builder, return_err_fn, &my_err_trace_val, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; if (fn_is_async(g->cur_fn) && codegen_fn_has_err_ret_tracing_arg(g, ret_type)) { @@ -2243,6 +2335,12 @@ static void gen_assert_resume_id(CodeGen *g, IrInstruction *source_instr, Resume LLVMBasicBlockRef end_bb) { LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + + if (ir_want_runtime_safety(g, source_instr)) { + // Write a value to the resume index which indicates the function was resumed while not suspended. + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); + } + LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); if (end_bb == nullptr) end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "OkResume"); LLVMValueRef expected_value = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), @@ -2265,7 +2363,7 @@ static LLVMValueRef gen_resume(CodeGen *g, LLVMValueRef fn_val, LLVMValueRef tar LLVMValueRef arg_val = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), LLVMConstInt(usize_type_ref, resume_id, false)); LLVMValueRef args[] = {target_frame_ptr, arg_val}; - return ZigLLVMBuildCall(g->builder, fn_val, args, 2, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); + return ZigLLVMBuildCall(g->builder, fn_val, args, 2, ZigLLVM_Fast, ZigLLVM_CallAttrAuto, ""); } static LLVMBasicBlockRef gen_suspend_begin(CodeGen *g, const char *name_hint) { @@ -2313,13 +2411,13 @@ static LLVMValueRef gen_maybe_atomic_op(CodeGen *g, LLVMAtomicRMWBinOp op, LLVMV static void gen_async_return(CodeGen *g, IrInstructionReturn *instruction) { LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - ZigType *operand_type = (instruction->operand != nullptr) ? instruction->operand->value.type : nullptr; + ZigType *operand_type = (instruction->operand != nullptr) ? instruction->operand->value->type : nullptr; bool operand_has_bits = (operand_type != nullptr) && type_has_bits(operand_type); ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; bool ret_type_has_bits = type_has_bits(ret_type); if (operand_has_bits && instruction->operand != nullptr) { - bool need_store = instruction->operand->value.special != ConstValSpecialRuntime || !handle_is_ptr(ret_type); + bool need_store = instruction->operand->value->special != ConstValSpecialRuntime || !handle_is_ptr(ret_type); if (need_store) { // It didn't get written to the result ptr. We do that now. ZigType *ret_ptr_type = get_pointer_to_type(g, ret_type, true); @@ -2392,7 +2490,7 @@ static void gen_async_return(CodeGen *g, IrInstructionReturn *instruction) { LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope, &is_llvm_alloca); LLVMValueRef args[] = { dest_trace_ptr, my_err_trace_val }; ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); } } @@ -2416,10 +2514,9 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns return nullptr; } assert(g->cur_ret_ptr); - src_assert(instruction->operand->value.special != ConstValSpecialRuntime, - instruction->base.source_node); + ir_assert(instruction->operand->value->special != ConstValSpecialRuntime, &instruction->base); LLVMValueRef value = ir_llvm_value(g, instruction->operand); - ZigType *return_type = instruction->operand->value.type; + ZigType *return_type = instruction->operand->value->type; gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); LLVMBuildRetVoid(g->builder); } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync && @@ -2753,15 +2850,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, IrInstruction *op1 = bin_op_instruction->op1; IrInstruction *op2 = bin_op_instruction->op2; - assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy || - op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || - op_id == IrBinOpBitShiftRightExact || - (op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) || - (op1->value.type->id == ZigTypeIdPointer && - (op_id == IrBinOpAdd || op_id == IrBinOpSub) && - op1->value.type->data.pointer.ptr_len != PtrLenSingle) - ); - ZigType *operand_type = op1->value.type; + ZigType *operand_type = op1->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; bool want_runtime_safety = bin_op_instruction->safety_check_on && @@ -2776,7 +2865,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: - case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBoolOr: return LLVMBuildOr(g->builder, op1_value, op2_value, ""); @@ -2818,7 +2906,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, AddSubMulMul; if (scalar_type->id == ZigTypeIdPointer) { - assert(scalar_type->data.pointer.ptr_len != PtrLenSingle); LLVMValueRef subscript_value; if (operand_type->id == ZigTypeIdVector) zig_panic("TODO: Implement vector operations on pointers."); @@ -2863,7 +2950,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpBitShiftLeftExact: { assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); + LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); if (is_sloppy) { return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); @@ -2879,7 +2966,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpBitShiftRightExact: { assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); + LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); if (is_sloppy) { if (scalar_type->data.integral.is_signed) { @@ -2969,32 +3056,32 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in static LLVMValueRef ir_render_resize_slice(CodeGen *g, IrExecutable *executable, IrInstructionResizeSlice *instruction) { - ZigType *actual_type = instruction->operand->value.type; - ZigType *wanted_type = instruction->base.value.type; + ZigType *actual_type = instruction->operand->value->type; + ZigType *wanted_type = instruction->base.value->type; LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand); assert(expr_val); LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); + assert(wanted_type->data.structure.special == StructSpecialSlice); assert(actual_type->id == ZigTypeIdStruct); - assert(actual_type->data.structure.is_slice); + assert(actual_type->data.structure.special == StructSpecialSlice); - ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; + ZigType *actual_pointer_type = actual_type->data.structure.fields[0]->type_entry; ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0]->type_entry; ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; + size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index]->gen_index; + size_t actual_len_index = actual_type->data.structure.fields[slice_len_index]->gen_index; + size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index]->gen_index; + size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - get_llvm_type(g, wanted_type->data.structure.fields[0].type_entry), ""); + get_llvm_type(g, wanted_type->data.structure.fields[0]->type_entry), ""); LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)wanted_ptr_index, ""); gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); @@ -3037,17 +3124,24 @@ static LLVMValueRef ir_render_resize_slice(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { - ZigType *actual_type = cast_instruction->value->value.type; - ZigType *wanted_type = cast_instruction->base.value.type; + ZigType *actual_type = cast_instruction->value->value->type; + ZigType *wanted_type = cast_instruction->base.value->type; LLVMValueRef expr_val = ir_llvm_value(g, cast_instruction->value); - assert(expr_val); + ir_assert(expr_val, &cast_instruction->base); switch (cast_instruction->cast_op) { case CastOpNoCast: case CastOpNumLitToConcrete: zig_unreachable(); case CastOpNoop: - return expr_val; + if (actual_type->id == ZigTypeIdPointer && wanted_type->id == ZigTypeIdPointer && + actual_type->data.pointer.child_type->id == ZigTypeIdArray && + wanted_type->data.pointer.child_type->id == ZigTypeIdArray) + { + return LLVMBuildBitCast(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + return expr_val; + } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); if (actual_type->data.integral.is_signed) { @@ -3108,11 +3202,11 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, IrInstructionPtrOfArrayToSlice *instruction) { - ZigType *actual_type = instruction->operand->value.type; - ZigType *slice_type = instruction->base.value.type; - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; - size_t ptr_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; - size_t len_index = slice_type->data.structure.fields[slice_len_index].gen_index; + ZigType *actual_type = instruction->operand->value->type; + ZigType *slice_type = instruction->base.value->type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + size_t ptr_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; + size_t len_index = slice_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); @@ -3145,7 +3239,7 @@ static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *ex static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, IrInstructionPtrCastGen *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; if (!type_has_bits(wanted_type)) { return nullptr; } @@ -3171,8 +3265,8 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, IrInstructionBitCastGen *instruction) { - ZigType *wanted_type = instruction->base.value.type; - ZigType *actual_type = instruction->operand->value.type; + ZigType *wanted_type = instruction->base.value->type; + ZigType *actual_type = instruction->operand->value->type; LLVMValueRef value = ir_llvm_value(g, instruction->operand); bool wanted_is_ptr = handle_is_ptr(wanted_type); @@ -3195,7 +3289,7 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, IrInstructionWidenOrShorten *instruction) { - ZigType *actual_type = instruction->target->value.type; + ZigType *actual_type = instruction->target->value->type; // TODO instead of this logic, use the Noop instruction to change the type from // enum_tag to the underlying int type ZigType *int_type; @@ -3206,43 +3300,63 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executa } LLVMValueRef target_val = ir_llvm_value(g, instruction->target); return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), int_type, - instruction->base.value.type, target_val); + instruction->base.value->type, target_val); } static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutable *executable, IrInstructionIntToPtr *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - if (!ptr_allows_addr_zero(wanted_type) && ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(target_val)); - LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, ""); - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk"); - LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block); - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdPtrCastNull); + if (ir_want_runtime_safety(g, &instruction->base)) { + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef zero = LLVMConstNull(usize->llvm_type); - LLVMPositionBuilderAtEnd(g->builder, ok_block); + if (!ptr_allows_addr_zero(wanted_type)) { + LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, ""); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk"); + LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdPtrCastNull); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + { + const uint32_t align_bytes = get_ptr_align(g, wanted_type); + LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false); + LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, ""); + LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, ""); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignBad"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignOk"); + LLVMBuildCondBr(g->builder, is_ok_bit, ok_block, bad_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdIncorrectAlignment); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } } return LLVMBuildIntToPtr(g->builder, target_val, get_llvm_type(g, wanted_type), ""); } static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutable *executable, IrInstructionPtrToInt *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); return LLVMBuildPtrToInt(g->builder, target_val, get_llvm_type(g, wanted_type), ""); } static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, IrInstructionIntToEnum *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdEnum); ZigType *tag_int_type = wanted_type->data.enumeration.tag_int_type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), - instruction->target->value.type, tag_int_type, target_val); + instruction->target->value->type, tag_int_type, target_val); - if (ir_want_runtime_safety(g, &instruction->base)) { + if (ir_want_runtime_safety(g, &instruction->base) && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); size_t field_count = wanted_type->data.enumeration.src_field_count; @@ -3261,10 +3375,10 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdErrorSet); - ZigType *actual_type = instruction->target->value.type; + ZigType *actual_type = instruction->target->value->type; assert(actual_type->id == ZigTypeIdInt); assert(!actual_type->data.integral.is_signed); @@ -3278,11 +3392,11 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I } static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, IrInstructionErrToInt *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdInt); assert(!wanted_type->data.integral.is_signed); - ZigType *actual_type = instruction->target->value.type; + ZigType *actual_type = instruction->target->value->type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); if (actual_type->id == ZigTypeIdErrorSet) { @@ -3332,7 +3446,7 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) { IrUnOp op_id = un_op_instruction->op_id; LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value); - ZigType *operand_type = un_op_instruction->value->value.type; + ZigType *operand_type = un_op_instruction->value->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; switch (op_id) { @@ -3388,15 +3502,25 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrI return nullptr; } -static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) { - ZigType *child_type = instruction->base.value.type; +static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionLoadPtrGen *instruction) +{ + ZigType *child_type = instruction->base.value->type; if (!type_has_bits(child_type)) return nullptr; LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - ZigType *ptr_type = instruction->ptr->value.type; + ZigType *ptr_type = instruction->ptr->value->type; assert(ptr_type->id == ZigTypeIdPointer); + ir_assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME, &instruction->base); + if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), + ptr_type->data.pointer.vector_index, false); + LLVMValueRef loaded_vector = LLVMBuildLoad(g->builder, ptr, ""); + return LLVMBuildExtractElement(g->builder, loaded_vector, index_val, ""); + } + uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; if (host_int_bytes == 0) return get_handle_value(g, ptr, child_type, ptr_type); @@ -3436,7 +3560,7 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); } -static bool value_is_all_undef_array(CodeGen *g, ConstExprValue *const_val, size_t len) { +static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { switch (const_val->data.x_array.special) { case ConstArraySpecialUndef: return true; @@ -3452,11 +3576,11 @@ static bool value_is_all_undef_array(CodeGen *g, ConstExprValue *const_val, size zig_unreachable(); } -static bool value_is_all_undef(CodeGen *g, ConstExprValue *const_val) { +static bool value_is_all_undef(CodeGen *g, ZigValue *const_val) { Error err; if (const_val->special == ConstValSpecialLazy && (err = ir_resolve_lazy(g, nullptr, const_val))) - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); switch (const_val->special) { case ConstValSpecialLazy: @@ -3468,7 +3592,7 @@ static bool value_is_all_undef(CodeGen *g, ConstExprValue *const_val) { case ConstValSpecialStatic: if (const_val->type->id == ZigTypeIdStruct) { for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { - if (!value_is_all_undef(g, &const_val->data.x_struct.fields[i])) + if (!value_is_all_undef(g, const_val->data.x_struct.fields[i])) return false; } return true; @@ -3582,9 +3706,14 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_ } static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - ZigType *ptr_type = instruction->ptr->value.type; + Error err; + + ZigType *ptr_type = instruction->ptr->value->type; assert(ptr_type->id == ZigTypeIdPointer); - if (!type_has_bits(ptr_type)) + bool ptr_type_has_bits; + if ((err = type_has_bits2(g, ptr_type, &ptr_type_has_bits))) + codegen_report_errors_and_exit(g); + if (!ptr_type_has_bits) return nullptr; if (instruction->ptr->ref_count == 0) { // In this case, this StorePtr instruction should be elided. Something happened like this: @@ -3596,20 +3725,33 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir return nullptr; } - bool have_init_expr = !value_is_all_undef(g, &instruction->value->value); + bool have_init_expr = !value_is_all_undef(g, instruction->value->value); if (have_init_expr) { LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); LLVMValueRef value = ir_llvm_value(g, instruction->value); gen_assign_raw(g, ptr, ptr_type, value); } else if (ir_want_runtime_safety(g, &instruction->base)) { - gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type, + gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value->type, ir_llvm_value(g, instruction->ptr)); } return nullptr; } +static LLVMValueRef ir_render_vector_store_elem(CodeGen *g, IrExecutable *executable, + IrInstructionVectorStoreElem *instruction) +{ + LLVMValueRef vector_ptr = ir_llvm_value(g, instruction->vector_ptr); + LLVMValueRef index = ir_llvm_value(g, instruction->index); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + + LLVMValueRef loaded_vector = gen_load(g, vector_ptr, instruction->vector_ptr->value->type, ""); + LLVMValueRef modified_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, index, ""); + gen_store(g, modified_vector, vector_ptr, instruction->vector_ptr->value->type); + return nullptr; +} + static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) { - if (instruction->base.value.special != ConstValSpecialRuntime) + if (instruction->base.value->special != ConstValSpecialRuntime) return ir_llvm_value(g, &instruction->base); ZigVar *var = instruction->var; if (type_has_bits(var->var_type)) { @@ -3623,18 +3765,17 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable, IrInstructionReturnPtr *instruction) { - if (!type_has_bits(instruction->base.value.type)) + if (!type_has_bits(instruction->base.value->type)) return nullptr; - src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node); + ir_assert(g->cur_ret_ptr != nullptr, &instruction->base); return g->cur_ret_ptr; } static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrInstructionElemPtr *instruction) { LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr); - ZigType *array_ptr_type = instruction->array_ptr->value.type; + ZigType *array_ptr_type = instruction->array_ptr->value->type; assert(array_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index); assert(subscript_value); @@ -3646,13 +3787,15 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) { + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.child_type->id == ZigTypeIdArray); array_type = array_type->data.pointer.child_type; } if (safety_check_on) { - LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - array_type->data.array.len, false); + uint64_t extra_len_from_sentinel = (array_type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = array_type->data.array.len + extra_len_from_sentinel; + LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, full_len, false); add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); } if (array_ptr_type->data.pointer.host_int_bytes != 0) { @@ -3662,7 +3805,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI if (child_type->id == ZigTypeIdStruct && child_type->data.structure.layout == ContainerLayoutPacked) { - ZigType *ptr_type = instruction->base.value.type; + ZigType *ptr_type = instruction->base.value->type; size_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; if (host_int_bytes != 0) { uint32_t size_in_bits = type_size_bits(g, ptr_type->data.pointer.child_type); @@ -3685,15 +3828,17 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI }; return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); } else if (array_type->id == ZigTypeIdPointer) { + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); LLVMValueRef indices[] = { subscript_value }; return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); } else if (array_type->id == ZigTypeIdStruct) { - assert(array_type->data.structure.is_slice); + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + assert(array_type->data.structure.special == StructSpecialSlice); - ZigType *ptr_type = instruction->base.value.type; + ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; if (!type_has_bits(ptr_type)) { if (safety_check_on) { assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); @@ -3706,18 +3851,21 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); if (safety_check_on) { - size_t len_index = array_type->data.structure.fields[slice_len_index].gen_index; + size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; assert(len_index != SIZE_MAX); LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); - add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); + LLVMIntPredicate upper_op = (ptr_type->data.pointer.sentinel != nullptr) ? LLVMIntULE : LLVMIntULT; + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, upper_op, len); } - size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; + size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; assert(ptr_index != SIZE_MAX); LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, ""); return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); + } else if (array_type->id == ZigTypeIdVector) { + return array_ptr_ptr; } else { zig_unreachable(); } @@ -3803,7 +3951,7 @@ static void render_async_spills(CodeGen *g) { if (instruction->field_index == SIZE_MAX) continue; - size_t gen_index = frame_type->data.structure.fields[instruction->field_index].gen_index; + size_t gen_index = frame_type->data.structure.fields[instruction->field_index]->gen_index; instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, gen_index, instruction->name_hint); } @@ -3879,7 +4027,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else { assert(instruction->fn_ref); fn_val = ir_llvm_value(g, instruction->fn_ref); - fn_type = instruction->fn_ref->value.type; + fn_type = instruction->fn_ref->value->type; callee_is_async = fn_type->data.fn.fn_type_id.cc == CallingConventionAsync; } @@ -3906,14 +4054,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->modifier == CallModifierAsync) { frame_result_loc = result_loc; } else { + ir_assert(instruction->frame_result_loc != nullptr, &instruction->base); frame_result_loc_uncasted = ir_llvm_value(g, instruction->frame_result_loc); - src_assert(instruction->fn_entry != nullptr, instruction->base.source_node); + ir_assert(instruction->fn_entry != nullptr, &instruction->base); frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), ""); } } else { - if (instruction->new_stack->value.type->id == ZigTypeIdPointer && - instruction->new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + if (instruction->new_stack->value->type->id == ZigTypeIdPointer && + instruction->new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { frame_result_loc = ir_llvm_value(g, instruction->new_stack); } else { @@ -4079,20 +4228,32 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr fn_walk.data.call.gen_param_types = &gen_param_types; walk_function_params(g, fn_type, &fn_walk); - ZigLLVM_FnInline fn_inline; - switch (instruction->fn_inline) { - case FnInlineAuto: - fn_inline = ZigLLVM_FnInlineAuto; + ZigLLVM_CallAttr call_attr; + switch (instruction->modifier) { + case CallModifierBuiltin: + case CallModifierCompileTime: + zig_unreachable(); + case CallModifierNone: + case CallModifierNoAsync: + case CallModifierAsync: + call_attr = ZigLLVM_CallAttrAuto; break; - case FnInlineAlways: - fn_inline = (instruction->fn_entry == nullptr) ? ZigLLVM_FnInlineAuto : ZigLLVM_FnInlineAlways; + case CallModifierNeverTail: + call_attr = ZigLLVM_CallAttrNeverTail; break; - case FnInlineNever: - fn_inline = ZigLLVM_FnInlineNever; + case CallModifierNeverInline: + call_attr = ZigLLVM_CallAttrNeverInline; + break; + case CallModifierAlwaysTail: + call_attr = ZigLLVM_CallAttrAlwaysTail; + break; + case CallModifierAlwaysInline: + ir_assert(instruction->fn_entry != nullptr, &instruction->base); + call_attr = ZigLLVM_CallAttrAlwaysInline; break; } - LLVMCallConv llvm_cc = get_llvm_cc(g, cc); + ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, cc); LLVMValueRef result; if (callee_is_async) { @@ -4126,7 +4287,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); if (instruction->new_stack != nullptr) { return LLVMBuildBitCast(g->builder, frame_result_loc, - get_llvm_type(g, instruction->base.value.type), ""); + get_llvm_type(g, instruction->base.value->type), ""); } return nullptr; } else if (instruction->modifier == CallModifierNoAsync && !fn_is_async(g->cur_fn)) { @@ -4151,7 +4312,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr LLVMPositionBuilderAtEnd(g->builder, ok_block); } - ZigType *result_type = instruction->base.value.type; + ZigType *result_type = instruction->base.value->type; ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); return gen_await_early_return(g, &instruction->base, frame_result_loc, result_type, ptr_result_type, result_loc, true); @@ -4194,19 +4355,23 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); } else if (instruction->modifier == CallModifierAsync) { zig_panic("TODO @asyncCall of non-async function"); } else { - LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); - LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); - LLVMValueRef new_stack_addr = get_new_stack_addr(g, ir_llvm_value(g, instruction->new_stack)); - LLVMValueRef old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); + LLVMValueRef old_stack_ref; + if (src_return_type->id != ZigTypeIdUnreachable) { + LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); + old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); + } gen_set_stack_pointer(g, new_stack_addr); result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); - LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); + if (src_return_type->id != ZigTypeIdUnreachable) { + LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); + LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); + } } if (src_return_type->id == ZigTypeIdUnreachable) { @@ -4218,7 +4383,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr return result_loc; } else if (handle_is_ptr(src_return_type)) { LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc); - LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value.type)); + LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type)); return result_loc; } else if (!callee_is_async && instruction->modifier == CallModifierAsync) { LLVMBuildStore(g->builder, result, ret_ptr); @@ -4233,12 +4398,12 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa { Error err; - if (instruction->base.value.special != ConstValSpecialRuntime) + if (instruction->base.value->special != ConstValSpecialRuntime) return nullptr; LLVMValueRef struct_ptr = ir_llvm_value(g, instruction->struct_ptr); // not necessarily a pointer. could be ZigTypeIdStruct - ZigType *struct_ptr_type = instruction->struct_ptr->value.type; + ZigType *struct_ptr_type = instruction->struct_ptr->value->type; TypeStructField *field = instruction->field; if (!type_has_bits(field->type_entry)) @@ -4250,22 +4415,42 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa return struct_ptr; } - ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ? - struct_ptr_type->data.pointer.child_type : struct_ptr_type; - if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) - report_errors_and_exit(g); + ZigType *struct_type; + if (struct_ptr_type->id == ZigTypeIdPointer) { + if (struct_ptr_type->data.pointer.inferred_struct_field != nullptr) { + struct_type = struct_ptr_type->data.pointer.inferred_struct_field->inferred_struct_type; + } else { + struct_type = struct_ptr_type->data.pointer.child_type; + } + } else { + struct_type = struct_ptr_type; + } - assert(field->gen_index != SIZE_MAX); - return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); + if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) + codegen_report_errors_and_exit(g); + + ir_assert(field->gen_index != SIZE_MAX, &instruction->base); + LLVMValueRef field_ptr_val = LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); + ZigType *res_type = instruction->base.value->type; + ir_assert(res_type->id == ZigTypeIdPointer, &instruction->base); + if (res_type->data.pointer.host_int_bytes != 0) { + // We generate packed structs with get_llvm_type_of_n_bytes, which is + // u8 for 1 byte or [n]u8 for multiple bytes. But the pointer to the type + // is supposed to be a pointer to the integer. So we bitcast it here. + LLVMTypeRef int_elem_type = LLVMIntType(8*res_type->data.pointer.host_int_bytes); + LLVMTypeRef integer_ptr_type = LLVMPointerType(int_elem_type, 0); + return LLVMBuildBitCast(g->builder, field_ptr_val, integer_ptr_type, ""); + } + return field_ptr_val; } static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable, IrInstructionUnionFieldPtr *instruction) { - if (instruction->base.value.special != ConstValSpecialRuntime) + if (instruction->base.value->special != ConstValSpecialRuntime) return nullptr; - ZigType *union_ptr_type = instruction->union_ptr->value.type; + ZigType *union_ptr_type = instruction->union_ptr->value->type; assert(union_ptr_type->id == ZigTypeIdPointer); ZigType *union_type = union_ptr_type->data.pointer.child_type; assert(union_type->id == ZigTypeIdUnion); @@ -4273,17 +4458,32 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab TypeUnionField *field = instruction->field; if (!type_has_bits(field->type_entry)) { - if (union_type->data.unionation.gen_tag_index == SIZE_MAX) { + ZigType *tag_type = union_type->data.unionation.tag_type; + if (!instruction->initializing || !type_has_bits(tag_type)) return nullptr; + + // The field has no bits but we still have to change the discriminant + // value here + LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); + + LLVMTypeRef tag_type_ref = get_llvm_type(g, tag_type); + LLVMValueRef tag_field_ptr = nullptr; + if (union_type->data.unionation.gen_field_count == 0) { + assert(union_type->data.unionation.gen_tag_index == SIZE_MAX); + // The whole union is collapsed into the discriminant + tag_field_ptr = LLVMBuildBitCast(g->builder, union_ptr, + LLVMPointerType(tag_type_ref, 0), ""); + } else { + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_tag_index, ""); } - if (instruction->initializing) { - LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, - union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), - &field->enum_field->value); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - } + + LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, + &field->enum_field->value); + assert(tag_field_ptr != nullptr); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + return nullptr; } @@ -4344,7 +4544,7 @@ static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_ return SIZE_MAX; } -static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstructionAsm *instruction) { +static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutable *executable, IrInstructionAsmGen *instruction) { AstNode *asm_node = instruction->base.source_node; assert(asm_node->type == NodeTypeAsmExpr); AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr; @@ -4435,7 +4635,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru buf_append_char(&constraint_buf, ','); } - ZigType *const type = ir_input->value.type; + ZigType *const type = ir_input->value->type; LLVMTypeRef type_ref = get_llvm_type(g, type); LLVMValueRef value_ref = ir_llvm_value(g, ir_input); // Handle integers of non pot bitsize by widening them. @@ -4465,7 +4665,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru if (instruction->return_count == 0) { ret_type = LLVMVoidType(); } else { - ret_type = get_llvm_type(g, instruction->base.value.type); + ret_type = get_llvm_type(g, instruction->base.value->type); } LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false); @@ -4495,16 +4695,16 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable, IrInstructionTestNonNull *instruction) { - return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); + return gen_non_null_bit(g, instruction->value->value->type, ir_llvm_value(g, instruction->value)); } static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable, IrInstructionOptionalUnwrapPtr *instruction) { - if (instruction->base.value.special != ConstValSpecialRuntime) + if (instruction->base.value->special != ConstValSpecialRuntime) return nullptr; - ZigType *ptr_type = instruction->base_ptr->value.type; + ZigType *ptr_type = instruction->base_ptr->value->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *maybe_type = ptr_type->data.pointer.child_type; assert(maybe_type->id == ZigTypeIdOptional); @@ -4602,7 +4802,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFn } static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutable *executable, IrInstructionClz *instruction) { - ZigType *int_type = instruction->op->value.type; + ZigType *int_type = instruction->op->value->type; LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdClz); LLVMValueRef operand = ir_llvm_value(g, instruction->op); LLVMValueRef params[] { @@ -4610,11 +4810,11 @@ static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutable *executable, IrInstru LLVMConstNull(LLVMInt1Type()), }; LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); } static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstructionCtz *instruction) { - ZigType *int_type = instruction->op->value.type; + ZigType *int_type = instruction->op->value->type; LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdCtz); LLVMValueRef operand = ir_llvm_value(g, instruction->op); LLVMValueRef params[] { @@ -4622,12 +4822,12 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru LLVMConstNull(LLVMInt1Type()), }; LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); } static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutable *executable, IrInstructionShuffleVector *instruction) { - uint64_t len_a = instruction->a->value.type->data.vector.len; - uint64_t len_mask = instruction->mask->value.type->data.vector.len; + uint64_t len_a = instruction->a->value->type->data.vector.len; + uint64_t len_mask = instruction->mask->value->type->data.vector.len; // LLVM uses integers larger than the length of the first array to // index into the second array. This was deemed unnecessarily fragile @@ -4637,10 +4837,10 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutable *executabl IrInstruction *mask = instruction->mask; LLVMValueRef *values = allocate(len_mask); for (uint64_t i = 0; i < len_mask; i++) { - if (mask->value.data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) { + if (mask->value->data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) { values[i] = LLVMGetUndef(LLVMInt32Type()); } else { - int32_t v = bigint_as_signed(&mask->value.data.x_array.data.s_none.elements[i].data.x_bigint); + int32_t v = bigint_as_signed(&mask->value->data.x_array.data.s_none.elements[i].data.x_bigint); uint32_t index_val = (v >= 0) ? (uint32_t)v : (uint32_t)~v + (uint32_t)len_a; values[i] = LLVMConstInt(LLVMInt32Type(), index_val, false); } @@ -4656,10 +4856,10 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutable *executabl } static LLVMValueRef ir_render_splat(CodeGen *g, IrExecutable *executable, IrInstructionSplatGen *instruction) { - ZigType *result_type = instruction->base.value.type; - src_assert(result_type->id == ZigTypeIdVector, instruction->base.source_node); + ZigType *result_type = instruction->base.value->type; + ir_assert(result_type->id == ZigTypeIdVector, &instruction->base); uint32_t len = result_type->data.vector.len; - LLVMTypeRef op_llvm_type = LLVMVectorType(get_llvm_type(g, instruction->scalar->value.type), 1); + LLVMTypeRef op_llvm_type = LLVMVectorType(get_llvm_type(g, instruction->scalar->value->type), 1); LLVMTypeRef mask_llvm_type = LLVMVectorType(LLVMInt32Type(), len); LLVMValueRef undef_vector = LLVMGetUndef(op_llvm_type); LLVMValueRef op_vector = LLVMBuildInsertElement(g->builder, undef_vector, @@ -4668,34 +4868,50 @@ static LLVMValueRef ir_render_splat(CodeGen *g, IrExecutable *executable, IrInst } static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, IrInstructionPopCount *instruction) { - ZigType *int_type = instruction->op->value.type; + ZigType *int_type = instruction->op->value->type; LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); LLVMValueRef operand = ir_llvm_value(g, instruction->op); LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); } static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { - LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + ZigType *target_type = instruction->target_value->value->type; LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; + + LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + target_value = LLVMBuildPtrToInt(g->builder, target_value, usize->llvm_type, ""); + } + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, - (unsigned)instruction->case_count); + (unsigned)instruction->case_count); + for (size_t i = 0; i < instruction->case_count; i += 1) { IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; - LLVMAddCase(switch_instr, ir_llvm_value(g, this_case->value), this_case->block->llvm_block); + + LLVMValueRef case_value = ir_llvm_value(g, this_case->value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + case_value = LLVMBuildPtrToInt(g->builder, case_value, usize->llvm_type, ""); + } + + LLVMAddCase(switch_instr, case_value, this_case->block->llvm_block); } + return nullptr; } static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstructionPhi *instruction) { - if (!type_has_bits(instruction->base.value.type)) + if (!type_has_bits(instruction->base.value->type)) return nullptr; LLVMTypeRef phi_type; - if (handle_is_ptr(instruction->base.value.type)) { - phi_type = LLVMPointerType(get_llvm_type(g,instruction->base.value.type), 0); + if (handle_is_ptr(instruction->base.value->type)) { + phi_type = LLVMPointerType(get_llvm_type(g,instruction->base.value->type), 0); } else { - phi_type = get_llvm_type(g, instruction->base.value.type); + phi_type = get_llvm_type(g, instruction->base.value->type); } LLVMValueRef phi = LLVMBuildPhi(g->builder, phi_type, ""); @@ -4710,11 +4926,11 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru } static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstructionRefGen *instruction) { - if (!type_has_bits(instruction->base.value.type)) { + if (!type_has_bits(instruction->base.value->type)) { return nullptr; } LLVMValueRef value = ir_llvm_value(g, instruction->operand); - if (handle_is_ptr(instruction->operand->value.type)) { + if (handle_is_ptr(instruction->operand->value->type)) { return value; } else { LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); @@ -4763,7 +4979,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { buf_ptr(buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name))), false); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); LLVMSetLinkage(fn_val, LLVMInternalLinkage); - LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); if (codegen_have_frame_pointer(g)) { @@ -4847,20 +5063,20 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, IrInstructionTagName *instruction) { - ZigType *enum_type = instruction->target->value.type; + ZigType *enum_type = instruction->target->value->type; assert(enum_type->id == ZigTypeIdEnum); LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); } static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, IrInstructionFieldParentPtr *instruction) { - ZigType *container_ptr_type = instruction->base.value.type; + ZigType *container_ptr_type = instruction->base.value->type; assert(container_ptr_type->id == ZigTypeIdPointer); ZigType *container_type = container_ptr_type->data.pointer.child_type; @@ -4893,7 +5109,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I return target_val; } - ZigType *target_type = instruction->base.value.type; + ZigType *target_type = instruction->base.value->type; uint32_t align_bytes; LLVMValueRef ptr_val; @@ -4913,11 +5129,13 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I { align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; ptr_val = target_val; - } else if (target_type->id == ZigTypeIdStruct && target_type->data.structure.is_slice) { - ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; + } else if (target_type->id == ZigTypeIdStruct && + target_type->data.structure.special == StructSpecialSlice) + { + ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; align_bytes = get_ptr_align(g, slice_ptr_type); - size_t ptr_index = target_type->data.structure.fields[slice_ptr_index].gen_index; + size_t ptr_index = target_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, ""); ptr_val = gen_load_untyped(g, ptr_val_ptr, 0, false, ""); } else { @@ -4968,19 +5186,21 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { zig_unreachable(); } -static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) { +static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) { switch (op) { - case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg; - case AtomicRmwOp_add: return LLVMAtomicRMWBinOpAdd; - case AtomicRmwOp_sub: return LLVMAtomicRMWBinOpSub; - case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd; - case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand; - case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr; - case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor; + case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg; + case AtomicRmwOp_add: + return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd; + case AtomicRmwOp_sub: + return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub; + case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd; + case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand; + case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr; + case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor; case AtomicRmwOp_max: - return is_signed ? LLVMAtomicRMWBinOpMax : LLVMAtomicRMWBinOpUMax; + return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax; case AtomicRmwOp_min: - return is_signed ? LLVMAtomicRMWBinOpMin : LLVMAtomicRMWBinOpUMin; + return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin; } zig_unreachable(); } @@ -4996,7 +5216,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); - ZigType *optional_type = instruction->base.value.type; + ZigType *optional_type = instruction->base.value->type; assert(optional_type->id == ZigTypeIdOptional); ZigType *child_type = optional_type->data.maybe.child_type; @@ -5007,13 +5227,13 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn } // When the cmpxchg is discarded, the result location will have no bits. - if (!type_has_bits(instruction->result_loc->value.type)) { + if (!type_has_bits(instruction->result_loc->value->type)) { return nullptr; } LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - src_assert(result_loc != nullptr, instruction->base.source_node); - src_assert(type_has_bits(child_type), instruction->base.source_node); + ir_assert(result_loc != nullptr, &instruction->base); + ir_assert(type_has_bits(child_type), &instruction->base); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); @@ -5034,8 +5254,8 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) { LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - ZigType *dest_type = instruction->base.value.type; - ZigType *src_type = instruction->target->value.type; + ZigType *dest_type = instruction->base.value->type; + ZigType *src_type = instruction->target->value->type; if (dest_type == src_type) { // no-op return target_val; @@ -5054,12 +5274,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrIns LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); - ZigType *ptr_type = instruction->dest_ptr->value.type; + ZigType *ptr_type = instruction->dest_ptr->value->type; assert(ptr_type->id == ZigTypeIdPointer); - bool val_is_undef = value_is_all_undef(g, &instruction->byte->value); + bool val_is_undef = value_is_all_undef(g, instruction->byte->value); LLVMValueRef fill_char; - if (val_is_undef) { + if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.scope)) { fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); } else { fill_char = ir_llvm_value(g, instruction->byte); @@ -5083,8 +5303,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, ""); - ZigType *dest_ptr_type = instruction->dest_ptr->value.type; - ZigType *src_ptr_type = instruction->src_ptr->value.type; + ZigType *dest_ptr_type = instruction->dest_ptr->value->type; + ZigType *src_ptr_type = instruction->src_ptr->value->type; assert(dest_ptr_type->id == ZigTypeIdPointer); assert(src_ptr_type->id == ZigTypeIdPointer); @@ -5097,7 +5317,7 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSliceGen *instruction) { LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); - ZigType *array_ptr_type = instruction->ptr->value.type; + ZigType *array_ptr_type = instruction->ptr->value->type; assert(array_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = array_ptr_type->data.pointer.child_type; LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); @@ -5106,6 +5326,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry; + ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel; + if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) { @@ -5120,13 +5343,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); } if (want_runtime_safety) { - if (instruction->start->value.special == ConstValSpecialRuntime || instruction->end) { + if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } if (instruction->end) { LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); + + if (sentinel != nullptr) { + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + end_val, + }; + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } } } if (!type_has_bits(array_type)) { @@ -5159,30 +5391,34 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst if (want_runtime_safety) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + if (sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } } if (type_has_bits(array_type)) { - size_t gen_ptr_index = instruction->base.value.type->data.structure.fields[slice_ptr_index].gen_index; + size_t gen_ptr_index = instruction->base.value->type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); } - size_t gen_len_index = instruction->base.value.type->data.structure.fields[slice_len_index].gen_index; + size_t gen_len_index = instruction->base.value->type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); return tmp_struct_ptr; } else if (array_type->id == ZigTypeIdStruct) { - assert(array_type->data.structure.is_slice); + assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); - size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; + size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; assert(ptr_index != SIZE_MAX); - size_t len_index = array_type->data.structure.fields[slice_len_index].gen_index; + size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; assert(len_index != SIZE_MAX); LLVMValueRef prev_end = nullptr; @@ -5199,18 +5435,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst end_val = prev_end; } + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + if (want_runtime_safety) { assert(prev_end); add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); if (instruction->end) { add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); + + if (sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } } } - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, (unsigned)len_index, ""); + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); @@ -5282,8 +5524,8 @@ static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, instruction->op2->value.type, - instruction->op1->value.type, op2); + LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, instruction->op2->value->type, + instruction->op1->value->type, op2); LLVMValueRef result = LLVMBuildShl(g->builder, op1, op2_casted, ""); LLVMValueRef orig_val; @@ -5294,7 +5536,7 @@ static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp } LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, op1, orig_val, ""); - gen_store(g, result, ptr_result, instruction->result_ptr->value.type); + gen_store(g, result, ptr_result, instruction->result_ptr->value->type); return overflow_bit; } @@ -5332,13 +5574,13 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable, LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); - gen_store(g, result, ptr_result, instruction->result_ptr->value.type); + gen_store(g, result, ptr_result, instruction->result_ptr->value->type); return overflow_bit; } static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErrGen *instruction) { - ZigType *err_union_type = instruction->err_union->value.type; + ZigType *err_union_type = instruction->err_union->value->type; ZigType *payload_type = err_union_type->data.error_union.payload_type; LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->err_union); @@ -5357,10 +5599,10 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { - if (instruction->base.value.special != ConstValSpecialRuntime) + if (instruction->base.value->special != ConstValSpecialRuntime) return nullptr; - ZigType *ptr_type = instruction->err_union_ptr->value.type; + ZigType *ptr_type = instruction->err_union_ptr->value->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; ZigType *payload_type = err_union_type->data.error_union.payload_type; @@ -5377,14 +5619,22 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { - if (instruction->base.value.special != ConstValSpecialRuntime) + Error err; + + if (instruction->base.value->special != ConstValSpecialRuntime) return nullptr; bool want_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base) && g->errors_by_index.length > 1; - if (!want_safety && !type_has_bits(instruction->base.value.type)) + + bool value_has_bits; + if ((err = type_has_bits2(g, instruction->base.value->type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (!want_safety && !value_has_bits) return nullptr; - ZigType *ptr_type = instruction->value->value.type; + + ZigType *ptr_type = instruction->value->value->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; ZigType *payload_type = err_union_type->data.error_union.payload_type; @@ -5428,7 +5678,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu } static LLVMValueRef ir_render_optional_wrap(CodeGen *g, IrExecutable *executable, IrInstructionOptionalWrap *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdOptional); @@ -5455,7 +5705,7 @@ static LLVMValueRef ir_render_optional_wrap(CodeGen *g, IrExecutable *executable LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); - // child_type and instruction->value->value.type may differ by constness + // child_type and instruction->value->value->type may differ by constness gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); @@ -5464,7 +5714,7 @@ static LLVMValueRef ir_render_optional_wrap(CodeGen *g, IrExecutable *executable } static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdErrorUnion); @@ -5484,7 +5734,7 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable } static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapPayload *instruction) { - ZigType *wanted_type = instruction->base.value.type; + ZigType *wanted_type = instruction->base.value->type; assert(wanted_type->id == ZigTypeIdErrorUnion); @@ -5515,7 +5765,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa } static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { - ZigType *union_type = instruction->value->value.type; + ZigType *union_type = instruction->value->value->type; ZigType *tag_type = union_type->data.unionation.tag_type; if (!type_has_bits(tag_type)) @@ -5543,26 +5793,27 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, IrInstructionAtomicRmw *instruction) { bool is_signed; - ZigType *operand_type = instruction->operand->value.type; + ZigType *operand_type = instruction->operand->value->type; + bool is_float = operand_type->id == ZigTypeIdFloat; if (operand_type->id == ZigTypeIdInt) { is_signed = operand_type->data.integral.is_signed; } else { is_signed = false; } - LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed); + enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->resolved_op, is_signed, is_float); LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); LLVMValueRef operand = ir_llvm_value(g, instruction->operand); if (get_codegen_ptr_type(operand_type) == nullptr) { - return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); + return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); } // it's a pointer but we need to treat it as an int LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), ""); LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, ""); - LLVMValueRef uncasted_result = LLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, + LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, g->is_single_threaded); return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); } @@ -5572,25 +5823,35 @@ static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, { LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, ""); + LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value->type, ""); LLVMSetOrdering(load_inst, ordering); return load_inst; } +static LLVMValueRef ir_render_atomic_store(CodeGen *g, IrExecutable *executable, + IrInstructionAtomicStore *instruction) +{ + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + LLVMValueRef store_inst = gen_store(g, value, ptr, instruction->ptr->value->type); + LLVMSetOrdering(store_inst, ordering); + return nullptr; +} + static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { - LLVMValueRef op = ir_llvm_value(g, instruction->op1); - assert(instruction->base.value.type->id == ZigTypeIdFloat); - LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op); - return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); + LLVMValueRef operand = ir_llvm_value(g, instruction->operand); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); + return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); } static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrInstructionMulAdd *instruction) { LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); - assert(instruction->base.value.type->id == ZigTypeIdFloat || - instruction->base.value.type->id == ZigTypeIdVector); - LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); + assert(instruction->base.value->type->id == ZigTypeIdFloat || + instruction->base.value->type->id == ZigTypeIdVector); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); LLVMValueRef args[3] = { op1, op2, @@ -5601,7 +5862,7 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { LLVMValueRef op = ir_llvm_value(g, instruction->op); - ZigType *expr_type = instruction->base.value.type; + ZigType *expr_type = instruction->base.value->type; bool is_vector = expr_type->id == ZigTypeIdVector; ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; assert(int_type->id == ZigTypeIdInt); @@ -5635,16 +5896,16 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable, IrInstructionBitReverse *instruction) { LLVMValueRef op = ir_llvm_value(g, instruction->op); - ZigType *int_type = instruction->base.value.type; + ZigType *int_type = instruction->base.value->type; assert(int_type->id == ZigTypeIdInt); - LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value.type, BuiltinFnIdBitReverse); + LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value->type, BuiltinFnIdBitReverse); return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable, IrInstructionVectorToArray *instruction) { - ZigType *array_type = instruction->base.value.type; + ZigType *array_type = instruction->base.value->type; assert(array_type->id == ZigTypeIdArray); assert(handle_is_ptr(array_type)); LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); @@ -5654,8 +5915,8 @@ static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executab bool bitcast_ok = elem_type->size_in_bits == elem_type->abi_size * 8; if (bitcast_ok) { LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, - LLVMPointerType(get_llvm_type(g, instruction->vector->value.type), 0), ""); - uint32_t alignment = get_ptr_align(g, instruction->result_loc->value.type); + LLVMPointerType(get_llvm_type(g, instruction->vector->value->type), 0), ""); + uint32_t alignment = get_ptr_align(g, instruction->result_loc->value->type); gen_store_untyped(g, vector, casted_ptr, alignment, false); } else { // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast @@ -5663,7 +5924,7 @@ static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executab LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; LLVMTypeRef u32_type_ref = LLVMInt32Type(); LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); - for (uintptr_t i = 0; i < instruction->vector->value.type->data.vector.len; i++) { + for (uintptr_t i = 0; i < instruction->vector->value->type->data.vector.len; i++) { LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); LLVMValueRef indexes[] = { zero, index_usize }; @@ -5678,7 +5939,7 @@ static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executab static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable, IrInstructionArrayToVector *instruction) { - ZigType *vector_type = instruction->base.value.type; + ZigType *vector_type = instruction->base.value->type; assert(vector_type->id == ZigTypeIdVector); assert(!handle_is_ptr(vector_type)); LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); @@ -5689,7 +5950,7 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab if (bitcast_ok) { LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, LLVMPointerType(vector_type_ref, 0), ""); - ZigType *array_type = instruction->array->value.type; + ZigType *array_type = instruction->array->value->type; assert(array_type->id == ZigTypeIdArray); uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); return gen_load_untyped(g, casted_ptr, alignment, false, ""); @@ -5700,7 +5961,7 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab LLVMTypeRef u32_type_ref = LLVMInt32Type(); LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); LLVMValueRef vector = LLVMGetUndef(vector_type_ref); - for (uintptr_t i = 0; i < instruction->base.value.type->data.vector.len; i++) { + for (uintptr_t i = 0; i < instruction->base.value->type->data.vector.len; i++) { LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); LLVMValueRef indexes[] = { zero, index_usize }; @@ -5716,7 +5977,7 @@ static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, IrInstructionAssertZero *instruction) { LLVMValueRef target = ir_llvm_value(g, instruction->target); - ZigType *int_type = instruction->target->value.type; + ZigType *int_type = instruction->target->value->type; if (ir_want_runtime_safety(g, &instruction->base)) { return gen_assert_zero(g, target, int_type); } @@ -5727,7 +5988,7 @@ static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executab IrInstructionAssertNonNull *instruction) { LLVMValueRef target = ir_llvm_value(g, instruction->target); - ZigType *target_type = instruction->target->value.type; + ZigType *target_type = instruction->target->value->type; if (target_type->id == ZigTypeIdPointer) { assert(target_type->data.pointer.ptr_len == PtrLenC); @@ -5763,6 +6024,9 @@ static LLVMValueRef ir_render_suspend_finish(CodeGen *g, IrExecutable *executabl LLVMBuildRetVoid(g->builder); LLVMPositionBuilderAtEnd(g->builder, instruction->begin->resume_bb); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); + } render_async_var_decls(g, instruction->base.scope); return nullptr; } @@ -5796,7 +6060,7 @@ static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_ins LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->scope, &is_llvm_alloca); LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr }; ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); } if (non_async && type_has_bits(result_type)) { LLVMValueRef result_ptr = (result_loc == nullptr) ? their_result_ptr : result_loc; @@ -5810,7 +6074,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; LLVMValueRef zero = LLVMConstNull(usize_type_ref); LLVMValueRef target_frame_ptr = ir_llvm_value(g, instruction->frame); - ZigType *result_type = instruction->base.value.type; + ZigType *result_type = instruction->base.value->type; ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); LLVMValueRef result_loc = (instruction->result_loc == nullptr) ? @@ -5893,7 +6157,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst static LLVMValueRef ir_render_resume(CodeGen *g, IrExecutable *executable, IrInstructionResume *instruction) { LLVMValueRef frame = ir_llvm_value(g, instruction->frame); - ZigType *frame_type = instruction->frame->value.type; + ZigType *frame_type = instruction->frame->value->type; assert(frame_type->id == ZigTypeIdAnyFrame); gen_resume(g, nullptr, frame, ResumeIdManual); @@ -5943,6 +6207,14 @@ static LLVMValueRef ir_render_spill_end(CodeGen *g, IrExecutable *executable, Ir zig_unreachable(); } +static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutable *executable, + IrInstructionVectorExtractElem *instruction) +{ + LLVMValueRef vector = ir_llvm_value(g, instruction->vector); + LLVMValueRef index = ir_llvm_value(g, instruction->index); + return LLVMBuildExtractElement(g->builder, vector, index, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -6020,10 +6292,11 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrCastSrc: case IrInstructionIdCmpxchgSrc: case IrInstructionIdLoadPtr: - case IrInstructionIdGlobalAsm: case IrInstructionIdHasDecl: case IrInstructionIdUndeclaredIdent: + case IrInstructionIdCallExtra: case IrInstructionIdCallSrc: + case IrInstructionIdCallSrcArgs: case IrInstructionIdAllocaSrc: case IrInstructionIdEndExpr: case IrInstructionIdImplicitCast: @@ -6040,6 +6313,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdAllocaGen: case IrInstructionIdAwaitSrc: case IrInstructionIdSplatSrc: + case IrInstructionIdMergeErrSets: + case IrInstructionIdAsmSrc: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -6062,6 +6337,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction); case IrInstructionIdStorePtr: return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction); + case IrInstructionIdVectorStoreElem: + return ir_render_vector_store_elem(g, executable, (IrInstructionVectorStoreElem *)instruction); case IrInstructionIdVarPtr: return ir_render_var_ptr(g, executable, (IrInstructionVarPtr *)instruction); case IrInstructionIdReturnPtr: @@ -6074,8 +6351,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); case IrInstructionIdUnionFieldPtr: return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction); - case IrInstructionIdAsm: - return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); + case IrInstructionIdAsmGen: + return ir_render_asm_gen(g, executable, (IrInstructionAsmGen *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); case IrInstructionIdOptionalUnwrapPtr: @@ -6166,6 +6443,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); case IrInstructionIdAtomicLoad: return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); + case IrInstructionIdAtomicStore: + return ir_render_atomic_store(g, executable, (IrInstructionAtomicStore *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); case IrInstructionIdFloatOp: @@ -6202,6 +6481,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_shuffle_vector(g, executable, (IrInstructionShuffleVector *) instruction); case IrInstructionIdSplatGen: return ir_render_splat(g, executable, (IrInstructionSplatGen *) instruction); + case IrInstructionIdVectorExtractElem: + return ir_render_vector_extract_elem(g, executable, (IrInstructionVectorExtractElem *) instruction); } zig_unreachable(); } @@ -6232,7 +6513,7 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { instruction->llvm_value = ir_render_instruction(g, executable, instruction); if (instruction->spill != nullptr) { LLVMValueRef spill_ptr = ir_llvm_value(g, instruction->spill); - gen_assign_raw(g, spill_ptr, instruction->spill->value.type, instruction->llvm_value); + gen_assign_raw(g, spill_ptr, instruction->spill->value->type, instruction->llvm_value); instruction->llvm_value = nullptr; } } @@ -6240,19 +6521,19 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { } } -static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); -static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); -static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); -static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val); -static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val); +static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index); +static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index); +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val); +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val); -static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { +static LLVMValueRef gen_parent_ptr(CodeGen *g, ZigValue *val, ConstParent *parent) { switch (parent->id) { case ConstParentIdNone: render_const_val(g, val, ""); render_const_val_global(g, val, ""); - return val->global_refs->llvm_global; + return val->llvm_global; case ConstParentIdStruct: return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, parent->data.p_struct.field_index); @@ -6270,12 +6551,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent case ConstParentIdScalar: render_const_val(g, parent->data.p_scalar.scalar_val, ""); render_const_val_global(g, parent->data.p_scalar.scalar_val, ""); - return parent->data.p_scalar.scalar_val->global_refs->llvm_global; + return parent->data.p_scalar.scalar_val->llvm_global; } zig_unreachable(); } -static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) { +static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index) { expand_undef_array(g, array_const_val); ConstParent *parent = &array_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); @@ -6301,7 +6582,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar } } -static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) { +static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index) { ConstParent *parent = &struct_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); @@ -6313,7 +6594,7 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s return LLVMConstInBoundsGEP(base_ptr, indices, 2); } -static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val) { ConstParent *parent = &err_union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); @@ -6325,7 +6606,7 @@ static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExpr return LLVMConstInBoundsGEP(base_ptr, indices, 2); } -static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val) { ConstParent *parent = &err_union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); @@ -6337,7 +6618,7 @@ static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstE return LLVMConstInBoundsGEP(base_ptr, indices, 2); } -static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) { +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val) { ConstParent *parent = &optional_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); @@ -6349,19 +6630,24 @@ static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstEx return LLVMConstInBoundsGEP(base_ptr, indices, 2); } -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val) { ConstParent *parent = &union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); + // Slot in the structure where the payload is stored, if equal to SIZE_MAX + // the union has no tag and a single field and is collapsed into the field + // itself + size_t union_payload_index = union_const_val->type->data.unionation.gen_union_index; + ZigType *u32 = g->builtin_types.entry_u32; LLVMValueRef indices[] = { LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), 0, false), // TODO test const union with more aligned tag type than payload + LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false), }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); + return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1); } -static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) { +static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ZigValue *const_val) { switch (const_val->special) { case ConstValSpecialLazy: case ConstValSpecialRuntime: @@ -6386,7 +6672,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdVoid: case ZigTypeIdOpaque: zig_unreachable(); @@ -6428,7 +6713,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type); size_t used_bits = 0; for (size_t i = 0; i < type_entry->data.array.len; i += 1) { - ConstExprValue *elem_val = &const_val->data.x_array.data.s_none.elements[i]; + ZigValue *elem_val = &const_val->data.x_array.data.s_none.elements[i]; LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); if (is_big_endian) { @@ -6456,11 +6741,11 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); size_t used_bits = 0; for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *field = &type_entry->data.structure.fields[i]; + TypeStructField *field = type_entry->data.structure.fields[i]; if (field->gen_index == SIZE_MAX) { continue; } - LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, &const_val->data.x_struct.fields[i]); + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, const_val->data.x_struct.fields[i]); uint32_t packed_bits_size = type_size_bits(g, field->type_entry); if (is_big_endian) { LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); @@ -6489,118 +6774,120 @@ static bool is_llvm_value_unnamed_type(CodeGen *g, ZigType *type_entry, LLVMValu return LLVMTypeOf(val) != get_llvm_type(g, type_entry); } -static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) { +static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const char *name) { switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: { - assert(const_val->global_refs != nullptr); - ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; + ZigValue *pointee = const_val->data.x_ptr.data.ref.pointee; render_const_val(g, pointee, ""); render_const_val_global(g, pointee, ""); - const_val->global_refs->llvm_value = LLVMConstBitCast(pointee->global_refs->llvm_global, + const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global, get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } case ConstPtrSpecialBaseArray: { - assert(const_val->global_refs != nullptr); - ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; + ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; assert(array_const_val->type->id == ZigTypeIdArray); if (!type_has_bits(array_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + if (array_const_val->type->data.array.sentinel != nullptr) { + ZigValue *pointee = array_const_val->type->data.array.sentinel; + render_const_val(g, pointee, ""); + render_const_val_global(g, pointee, ""); + const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global, + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } else { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } } size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->global_refs->llvm_value = ptr_val; + const_val->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialBaseStruct: { - assert(const_val->global_refs != nullptr); - ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; + ZigValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; assert(struct_const_val->type->id == ZigTypeIdStruct); if (!type_has_bits(struct_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; - size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index].gen_index; + size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index]->gen_index; LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, gen_field_index); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->global_refs->llvm_value = ptr_val; + const_val->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialBaseErrorUnionCode: { - assert(const_val->global_refs != nullptr); - ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; + ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); if (!type_has_bits(err_union_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->global_refs->llvm_value = ptr_val; + const_val->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialBaseErrorUnionPayload: { - assert(const_val->global_refs != nullptr); - ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; + ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); if (!type_has_bits(err_union_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->global_refs->llvm_value = ptr_val; + const_val->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialBaseOptionalPayload: { - assert(const_val->global_refs != nullptr); - ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; + ZigValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; assert(optional_const_val->type->id == ZigTypeIdOptional); if (!type_has_bits(optional_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->global_refs->llvm_value = ptr_val; + const_val->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialHardCodedAddr: { - assert(const_val->global_refs != nullptr); uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr( + const_val->llvm_value = LLVMConstIntToPtr( LLVMConstInt(usize->llvm_type, addr_value, false), get_llvm_type(g, const_val->type)); - return const_val->global_refs->llvm_value; + return const_val->llvm_value; } case ConstPtrSpecialFunction: return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), @@ -6611,12 +6898,12 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con zig_unreachable(); } -static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) { +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ZigValue *const_val, const char *name) { uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; return LLVMConstInt(get_llvm_type(g, g->builtin_types.entry_global_error_set), value, false); } -static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { +static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name) { Error err; ZigType *type_entry = const_val->type; @@ -6625,7 +6912,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c check: switch (const_val->special) { case ConstValSpecialLazy: if ((err = ir_resolve_lazy(g, nullptr, const_val))) { - report_errors_and_exit(g); + codegen_report_errors_and_exit(g); } goto check; case ConstValSpecialRuntime: @@ -6714,7 +7001,7 @@ check: switch (const_val->special) { if (type_entry->data.structure.layout == ContainerLayoutPacked) { size_t src_field_index = 0; while (src_field_index < src_field_count) { - TypeStructField *type_struct_field = &type_entry->data.structure.fields[src_field_index]; + TypeStructField *type_struct_field = type_entry->data.structure.fields[src_field_index]; if (type_struct_field->gen_index == SIZE_MAX) { src_field_index += 1; continue; @@ -6722,13 +7009,13 @@ check: switch (const_val->special) { size_t src_field_index_end = src_field_index + 1; for (; src_field_index_end < src_field_count; src_field_index_end += 1) { - TypeStructField *it_field = &type_entry->data.structure.fields[src_field_index_end]; + TypeStructField *it_field = type_entry->data.structure.fields[src_field_index_end]; if (it_field->gen_index != type_struct_field->gen_index) break; } if (src_field_index + 1 == src_field_index_end) { - ConstExprValue *field_val = &const_val->data.x_struct.fields[src_field_index]; + ZigValue *field_val = const_val->data.x_struct.fields[src_field_index]; LLVMValueRef val = gen_const_val(g, field_val, ""); fields[type_struct_field->gen_index] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val); @@ -6741,12 +7028,12 @@ check: switch (const_val->special) { LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); size_t used_bits = 0; for (size_t i = src_field_index; i < src_field_index_end; i += 1) { - TypeStructField *it_field = &type_entry->data.structure.fields[i]; + TypeStructField *it_field = type_entry->data.structure.fields[i]; if (it_field->gen_index == SIZE_MAX) { continue; } LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, - &const_val->data.x_struct.fields[i]); + const_val->data.x_struct.fields[i]); uint32_t packed_bits_size = type_size_bits(g, it_field->type_entry); if (is_big_endian) { LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, @@ -6781,11 +7068,11 @@ check: switch (const_val->special) { } } else { for (uint32_t i = 0; i < src_field_count; i += 1) { - TypeStructField *type_struct_field = &type_entry->data.structure.fields[i]; + TypeStructField *type_struct_field = type_entry->data.structure.fields[i]; if (type_struct_field->gen_index == SIZE_MAX) { continue; } - ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; + ZigValue *field_val = const_val->data.x_struct.fields[i]; assert(field_val->type != nullptr); if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, type_struct_field->type_entry))) @@ -6798,10 +7085,10 @@ check: switch (const_val->special) { make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val); size_t end_pad_gen_index = (i + 1 < src_field_count) ? - type_entry->data.structure.fields[i + 1].gen_index : + type_entry->data.structure.fields[i + 1]->gen_index : type_entry->data.structure.gen_field_count; size_t next_offset = (i + 1 < src_field_count) ? - type_entry->data.structure.fields[i + 1].offset : type_entry->abi_size; + type_entry->data.structure.fields[i + 1]->offset : type_entry->abi_size; if (end_pad_gen_index != SIZE_MAX) { for (size_t gen_i = type_struct_field->gen_index + 1; gen_i < end_pad_gen_index; gen_i += 1) @@ -6828,24 +7115,30 @@ check: switch (const_val->special) { case ConstArraySpecialUndef: return LLVMGetUndef(get_llvm_type(g, type_entry)); case ConstArraySpecialNone: { - LLVMValueRef *values = allocate(len); + uint64_t extra_len_from_sentinel = (type_entry->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = len + extra_len_from_sentinel; + LLVMValueRef *values = allocate(full_len); LLVMTypeRef element_type_ref = get_llvm_type(g, type_entry->data.array.child_type); bool make_unnamed_struct = false; for (uint64_t i = 0; i < len; i += 1) { - ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; + ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; LLVMValueRef val = gen_const_val(g, elem_value, ""); values[i] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, elem_value->type, val); } + if (type_entry->data.array.sentinel != nullptr) { + values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); + } if (make_unnamed_struct) { - return LLVMConstStruct(values, len, true); + return LLVMConstStruct(values, full_len, true); } else { - return LLVMConstArray(element_type_ref, values, (unsigned)len); + return LLVMConstArray(element_type_ref, values, (unsigned)full_len); } } case ConstArraySpecialBuf: { Buf *buf = const_val->data.x_array.data.s_buf; - return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true); + return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), + type_entry->data.array.sentinel == nullptr); } } zig_unreachable(); @@ -6858,7 +7151,7 @@ check: switch (const_val->special) { case ConstArraySpecialNone: { LLVMValueRef *values = allocate(len); for (uint64_t i = 0; i < len; i += 1) { - ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; + ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; values[i] = gen_const_val(g, elem_value, ""); } return LLVMConstVector(values, len); @@ -6894,7 +7187,7 @@ check: switch (const_val->special) { LLVMValueRef union_value_ref; bool make_unnamed_struct; - ConstExprValue *payload_value = const_val->data.x_union.payload; + ZigValue *payload_value = const_val->data.x_union.payload; if (payload_value == nullptr || !type_has_bits(payload_value->type)) { if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) return LLVMGetUndef(get_llvm_type(g, type_entry)); @@ -6902,7 +7195,7 @@ check: switch (const_val->special) { union_value_ref = LLVMGetUndef(union_type_ref); make_unnamed_struct = false; } else { - uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, + uint64_t field_type_bytes = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, payload_value->type)); uint64_t pad_bytes = type_entry->data.unionation.union_abi_size - field_type_bytes; LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, ""); @@ -6942,7 +7235,7 @@ check: switch (const_val->special) { uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); uint64_t end_offset = last_field_offset + LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); - uint64_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); + uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); unsigned pad_sz = expected_sz - end_offset; if (pad_sz != 0) { fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); @@ -6960,16 +7253,12 @@ check: switch (const_val->special) { case ZigTypeIdEnum: return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_enum_tag); case ZigTypeIdFn: - if (const_val->data.x_ptr.special == ConstPtrSpecialFunction) { - assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); - return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); - } else if (const_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - uint64_t addr = const_val->data.x_ptr.data.hard_coded_addr.addr; - return LLVMConstIntToPtr(LLVMConstInt(usize_type_ref, addr, false), get_llvm_type(g, type_entry)); - } else { + if (const_val->data.x_ptr.special == ConstPtrSpecialFunction && + const_val->data.x_ptr.mut != ConstPtrMutComptimeConst) { zig_unreachable(); } + // Treat it the same as we do for pointers + return gen_const_val_ptr(g, const_val, name); case ZigTypeIdPointer: return gen_const_val_ptr(g, const_val, name); case ZigTypeIdErrorUnion: @@ -6995,7 +7284,7 @@ check: switch (const_val->special) { make_unnamed_struct = false; } else { err_tag_value = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - ConstExprValue *payload_val = const_val->data.x_err_union.payload; + ZigValue *payload_val = const_val->data.x_err_union.payload; err_payload_value = gen_const_val(g, payload_val, ""); make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_val->type, err_payload_value); } @@ -7025,7 +7314,6 @@ check: switch (const_val->special) { case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: zig_unreachable(); case ZigTypeIdFnFrame: @@ -7036,35 +7324,30 @@ check: switch (const_val->special) { zig_unreachable(); } -static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { - if (!const_val->global_refs) - const_val->global_refs = allocate(1); - if (!const_val->global_refs->llvm_value) - const_val->global_refs->llvm_value = gen_const_val(g, const_val, name); +static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name) { + if (!const_val->llvm_value) + const_val->llvm_value = gen_const_val(g, const_val, name); - if (const_val->global_refs->llvm_global) - LLVMSetInitializer(const_val->global_refs->llvm_global, const_val->global_refs->llvm_value); + if (const_val->llvm_global) + LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); } -static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name) { - if (!const_val->global_refs) - const_val->global_refs = allocate(1); - - if (!const_val->global_refs->llvm_global) { - LLVMTypeRef type_ref = const_val->global_refs->llvm_value ? - LLVMTypeOf(const_val->global_refs->llvm_value) : get_llvm_type(g, const_val->type); +static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name) { + if (!const_val->llvm_global) { + LLVMTypeRef type_ref = const_val->llvm_value ? + LLVMTypeOf(const_val->llvm_value) : get_llvm_type(g, const_val->type); LLVMValueRef global_value = LLVMAddGlobal(g->module, type_ref, name); - LLVMSetLinkage(global_value, LLVMInternalLinkage); + LLVMSetLinkage(global_value, (name == nullptr) ? LLVMPrivateLinkage : LLVMInternalLinkage); LLVMSetGlobalConstant(global_value, true); LLVMSetUnnamedAddr(global_value, true); - LLVMSetAlignment(global_value, (const_val->global_refs->align == 0) ? - get_abi_alignment(g, const_val->type) : const_val->global_refs->align); + LLVMSetAlignment(global_value, (const_val->llvm_align == 0) ? + get_abi_alignment(g, const_val->type) : const_val->llvm_align); - const_val->global_refs->llvm_global = global_value; + const_val->llvm_global = global_value; } - if (const_val->global_refs->llvm_value) - LLVMSetInitializer(const_val->global_refs->llvm_global, const_val->global_refs->llvm_value); + if (const_val->llvm_value) + LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); } static void generate_error_name_table(CodeGen *g) { @@ -7186,7 +7469,7 @@ static void do_code_gen(CodeGen *g) { if (var->var_type->id == ZigTypeIdComptimeFloat) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->const_value; + ZigValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) zig_unreachable(); @@ -7194,7 +7477,7 @@ static void do_code_gen(CodeGen *g) { zig_panic("TODO debug info for var with ptr casted value"); } ZigType *var_type = g->builtin_types.entry_f128; - ConstExprValue coerced_value = {}; + ZigValue coerced_value = {}; coerced_value.special = ConstValSpecialStatic; coerced_value.type = var_type; coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); @@ -7205,7 +7488,7 @@ static void do_code_gen(CodeGen *g) { if (var->var_type->id == ZigTypeIdComptimeInt) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->const_value; + ZigValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) zig_unreachable(); @@ -7265,14 +7548,14 @@ static void do_code_gen(CodeGen *g) { bool exported = (linkage != GlobalLinkageIdInternal); render_const_val(g, var->const_value, symbol_name); render_const_val_global(g, var->const_value, symbol_name); - global_value = var->const_value->global_refs->llvm_global; + global_value = var->const_value->llvm_global; if (exported) { LLVMSetLinkage(global_value, to_llvm_linkage(linkage)); maybe_export_dll(g, global_value, GlobalLinkageIdStrong); } - if (tld_var->section_name) { - LLVMSetSection(global_value, buf_ptr(tld_var->section_name)); + if (var->section_name) { + LLVMSetSection(global_value, buf_ptr(var->section_name)); } LLVMSetAlignment(global_value, var->align_bytes); @@ -7280,7 +7563,7 @@ static void do_code_gen(CodeGen *g) { // Here we use const_value->type because that's the type of the llvm global, // which we const ptr cast upon use to whatever it needs to be. if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type); + gen_global_var(g, var, var->const_value->llvm_value, var->const_value->type); } LLVMSetGlobalConstant(global_value, var->gen_is_const); @@ -7296,8 +7579,12 @@ static void do_code_gen(CodeGen *g) { } // Generate function definitions. + stage2_progress_update_node(g->sub_progress_node, 0, g->fn_defs.length); for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { ZigFn *fn_table_entry = g->fn_defs.at(fn_i); + Stage2ProgressNode *fn_prog_node = stage2_progress_start(g->sub_progress_node, + buf_ptr(&fn_table_entry->symbol_name), buf_len(&fn_table_entry->symbol_name), 0); + FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; CallingConvention cc = fn_type_id->cc; bool is_c_abi = cc == CallingConventionC; @@ -7338,7 +7625,7 @@ static void do_code_gen(CodeGen *g) { !is_async && !have_err_ret_trace_arg; LLVMValueRef err_ret_array_val = nullptr; if (have_err_ret_trace_stack) { - ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count); + ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr); err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); (void)get_llvm_type(g, get_stack_trace_type(g)); @@ -7372,12 +7659,12 @@ static void do_code_gen(CodeGen *g) { call->frame_result_loc = all_calls_alloca; } if (largest_call_frame_type != nullptr) { - all_calls_alloca->value.type = get_pointer_to_type(g, largest_call_frame_type, false); + all_calls_alloca->value->type = get_pointer_to_type(g, largest_call_frame_type, false); } // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_gen_list.length; alloca_i += 1) { IrInstructionAllocaGen *instruction = fn_table_entry->alloca_gen_list.at(alloca_i); - ZigType *ptr_type = instruction->base.value.type; + ZigType *ptr_type = instruction->base.value->type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; if (type_resolve(g, child_type, ResolveStatusSizeKnown)) @@ -7386,8 +7673,8 @@ static void do_code_gen(CodeGen *g) { continue; if (instruction->base.ref_count == 0) continue; - if (instruction->base.value.special != ConstValSpecialRuntime) { - if (const_ptr_pointee(nullptr, g, &instruction->base.value, nullptr)->special != + if (instruction->base.value->special != ConstValSpecialRuntime) { + if (const_ptr_pointee(nullptr, g, instruction->base.value, nullptr)->special != ConstValSpecialRuntime) { continue; @@ -7464,15 +7751,15 @@ static void do_code_gen(CodeGen *g) { // finishing error return trace setup. we have to do this after all the allocas. if (have_err_ret_trace_stack) { ZigType *usize = g->builtin_types.entry_usize; - size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, ""); gen_store_untyped(g, LLVMConstNull(usize->llvm_type), index_field_ptr, 0, false); - size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, ""); - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); LLVMValueRef zero = LLVMConstNull(usize->llvm_type); LLVMValueRef indices[] = {zero, zero}; @@ -7481,7 +7768,7 @@ static void do_code_gen(CodeGen *g) { ZigType *ptr_ptr_usize_type = get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false); gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, ptr_ptr_usize_type); - size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); gen_store(g, LLVMConstInt(usize->llvm_type, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); } @@ -7531,7 +7818,20 @@ static void do_code_gen(CodeGen *g) { IrBasicBlock *entry_block = executable->basic_block_list.at(0); LLVMAddCase(switch_instr, zero, entry_block->llvm_block); g->cur_resume_block_count += 1; + + { + LLVMBasicBlockRef bad_not_suspended_bb = LLVMAppendBasicBlock(g->cur_fn_val, "NotSuspended"); + size_t new_block_index = g->cur_resume_block_count; + g->cur_resume_block_count += 1; + g->cur_bad_not_suspended_index = LLVMConstInt(usize_type_ref, new_block_index, false); + LLVMAddCase(g->cur_async_switch_instr, g->cur_bad_not_suspended_index, bad_not_suspended_bb); + + LLVMPositionBuilderAtEnd(g->builder, bad_not_suspended_bb); + gen_assertion_scope(g, PanicMsgIdResumeNotSuspendedFn, fn_table_entry->child_scope); + } + LLVMPositionBuilderAtEnd(g->builder, entry_block->llvm_block); + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); if (trace_field_index_stack != UINT32_MAX) { if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) { LLVMValueRef trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, @@ -7561,6 +7861,7 @@ static void do_code_gen(CodeGen *g) { ir_render(g, fn_table_entry); + stage2_progress_end(fn_prog_node); } assert(!g->errors.length); @@ -7584,7 +7885,7 @@ static void do_code_gen(CodeGen *g) { char *error = nullptr; if (LLVMVerifyModule(g->module, LLVMReturnStatusAction, &error)) { - zig_panic("broken LLVM module found: %s", error); + zig_panic("broken LLVM module found: %s\nThis is a bug in the Zig compiler.", error); } } @@ -7595,6 +7896,8 @@ static void zig_llvm_emit_output(CodeGen *g) { char *err_msg = nullptr; switch (g->emit_file_type) { case EmitFileTypeBinary: + if (g->disable_bin_generation) + return; if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small, g->enable_time_report)) @@ -7606,7 +7909,7 @@ static void zig_llvm_emit_output(CodeGen *g) { if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) { - zig_link_add_compiler_rt(g); + zig_link_add_compiler_rt(g, g->sub_progress_node); } break; @@ -7659,13 +7962,6 @@ struct GlobalLinkageValue { const char *name; }; -static const GlobalLinkageValue global_linkage_values[] = { - {GlobalLinkageIdInternal, "Internal"}, - {GlobalLinkageIdStrong, "Strong"}, - {GlobalLinkageIdWeak, "Weak"}, - {GlobalLinkageIdLinkOnce, "LinkOnce"}, -}; - static void add_fp_entry(CodeGen *g, const char *name, uint32_t bit_count, LLVMTypeRef type_ref, ZigType **field) { @@ -7718,9 +8014,9 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_null = entry; } { - ZigType *entry = new_type_table_entry(ZigTypeIdArgTuple); - buf_init_from_str(&entry->name, "(args)"); - g->builtin_types.entry_arg_tuple = entry; + ZigType *entry = new_type_table_entry(ZigTypeIdOpaque); + buf_init_from_str(&entry->name, "(var)"); + g->builtin_types.entry_var = entry; } for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { @@ -7737,7 +8033,8 @@ static void define_builtin_types(CodeGen *g) { buf_init_from_str(&entry->name, info->name); entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - size_in_bits, is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), + is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); entry->data.integral.is_signed = is_signed; entry->data.integral.bit_count = size_in_bits; g->primitive_type_table.put(&entry->name, entry); @@ -7753,7 +8050,8 @@ static void define_builtin_types(CodeGen *g) { entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); buf_init_from_str(&entry->name, "bool"); entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - entry->size_in_bits, ZigLLVMEncoding_DW_ATE_boolean()); + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), + ZigLLVMEncoding_DW_ATE_boolean()); g->builtin_types.entry_bool = entry; g->primitive_type_table.put(&entry->name, entry); } @@ -7775,7 +8073,7 @@ static void define_builtin_types(CodeGen *g) { entry->data.integral.bit_count = g->pointer_size_bytes * 8; entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - entry->size_in_bits, + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); g->primitive_type_table.put(&entry->name, entry); @@ -7852,6 +8150,35 @@ static void define_builtin_types(CodeGen *g) { } } +static void define_intern_values(CodeGen *g) { + { + auto& value = g->intern.x_undefined; + value.type = g->builtin_types.entry_undef; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_void; + value.type = g->builtin_types.entry_void; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_null; + value.type = g->builtin_types.entry_null; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_unreachable; + value.type = g->builtin_types.entry_unreachable; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.zero_byte; + value.type = g->builtin_types.entry_u8; + value.special = ConstValSpecialStatic; + bigint_init_unsigned(&value.data.x_bigint, 0); + } +} + static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name, size_t count) { BuiltinFnEntry *builtin_fn = allocate(1); buf_init_from_str(&builtin_fn->name, name); @@ -7875,7 +8202,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); create_builtin_fn(g, BuiltinFnIdType, "Type", 1); create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); - create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf + create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", 1); create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); @@ -7930,23 +8257,21 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); - create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); - create_builtin_fn(g, BuiltinFnIdSin, "sin", 2); - create_builtin_fn(g, BuiltinFnIdCos, "cos", 2); - create_builtin_fn(g, BuiltinFnIdExp, "exp", 2); - create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2); - create_builtin_fn(g, BuiltinFnIdLn, "ln", 2); - create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2); - create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2); - create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2); - create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2); - create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2); - create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2); - create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2); - create_builtin_fn(g, BuiltinFnIdRound, "round", 2); + create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1); + create_builtin_fn(g, BuiltinFnIdSin, "sin", 1); + create_builtin_fn(g, BuiltinFnIdCos, "cos", 1); + create_builtin_fn(g, BuiltinFnIdExp, "exp", 1); + create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1); + create_builtin_fn(g, BuiltinFnIdLog, "log", 1); + create_builtin_fn(g, BuiltinFnIdLog2, "log2", 1); + create_builtin_fn(g, BuiltinFnIdLog10, "log10", 1); + create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 1); + create_builtin_fn(g, BuiltinFnIdFloor, "floor", 1); + create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 1); + create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 1); + create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 1); + create_builtin_fn(g, BuiltinFnIdRound, "round", 1); create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); - create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); @@ -7957,10 +8282,11 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0); create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); - create_builtin_fn(g, BuiltinFnIdExport, "export", 3); + create_builtin_fn(g, BuiltinFnIdExport, "export", 2); create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); + create_builtin_fn(g, BuiltinFnIdAtomicStore, "atomicStore", 4); create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); @@ -7971,6 +8297,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1); create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); + create_builtin_fn(g, BuiltinFnIdAs, "as", 2); + create_builtin_fn(g, BuiltinFnIdCall, "call", 3); } static const char *bool_to_str(bool b) { @@ -8047,6 +8375,20 @@ static bool detect_stack_probing(CodeGen *g) { zig_unreachable(); } +static bool detect_sanitize_c(CodeGen *g) { + if (!target_supports_sanitize_c(g->zig_target)) + return false; + switch (g->want_sanitize_c) { + case WantCSanitizeDisabled: + return false; + case WantCSanitizeEnabled: + return true; + case WantCSanitizeAuto: + return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug; + } + zig_unreachable(); +} + // Returns TargetSubsystemAuto to mean "no subsystem" TargetSubsystem detect_subsystem(CodeGen *g) { if (g->subsystem != TargetSubsystemAuto) @@ -8054,9 +8396,9 @@ TargetSubsystem detect_subsystem(CodeGen *g) { if (g->zig_target->os == OsWindows) { if (g->have_dllmain_crt_startup || (g->out_type == OutTypeLib && g->is_dynamic)) return TargetSubsystemAuto; - if (g->have_c_main || g->have_pub_main || g->is_test_build) + if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup) return TargetSubsystemConsole; - if (g->have_winmain || g->have_winmain_crt_startup) + if (g->have_winmain) return TargetSubsystemWindows; } else if (g->zig_target->os == OsUefi) { return TargetSubsystemEfiApplication; @@ -8083,59 +8425,42 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { g->have_dynamic_link = detect_dynamic_link(g); g->have_pic = detect_pic(g); g->have_stack_probing = detect_stack_probing(g); + g->have_sanitize_c = detect_sanitize_c(g); g->is_single_threaded = detect_single_threaded(g); g->have_err_ret_tracing = detect_err_ret_tracing(g); Buf *contents = buf_alloc(); - - // NOTE: when editing this file, you may need to make modifications to the - // cache input parameters in define_builtin_compile_vars - - // Modifications to this struct must be coordinated with code that does anything with - // g->stack_trace_type. There are hard-coded references to the field indexes. - buf_append_str(contents, - "pub const StackTrace = struct {\n" - " index: usize,\n" - " instruction_addresses: []usize,\n" - "};\n\n"); - - buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n"); + buf_appendf(contents, "usingnamespace @import(\"std\").builtin;\n\n"); const char *cur_os = nullptr; { - buf_appendf(contents, "pub const Os = enum {\n"); uint32_t field_count = (uint32_t)target_os_count(); for (uint32_t i = 0; i < field_count; i += 1) { Os os_type = target_os_enum(i); const char *name = target_os_name(os_type); - buf_appendf(contents, " %s,\n", name); if (os_type == g->zig_target->os) { g->target_os_index = i; cur_os = name; } } - buf_appendf(contents, "};\n\n"); } assert(cur_os != nullptr); const char *cur_arch = nullptr; { - buf_appendf(contents, "pub const Arch = union(enum) {\n"); uint32_t field_count = (uint32_t)target_arch_count(); for (uint32_t arch_i = 0; arch_i < field_count; arch_i += 1) { ZigLLVM_ArchType arch = target_arch_enum(arch_i); const char *arch_name = target_arch_name(arch); SubArchList sub_arch_list = target_subarch_list(arch); if (sub_arch_list == SubArchListNone) { - buf_appendf(contents, " %s,\n", arch_name); if (arch == g->zig_target->arch) { g->target_arch_index = arch_i; cur_arch = buf_ptr(buf_sprintf("Arch.%s", arch_name)); } } else { const char *sub_arch_list_name = target_subarch_list_name(sub_arch_list); - buf_appendf(contents, " %s: %s,\n", arch_name, sub_arch_list_name); if (arch == g->zig_target->arch) { size_t sub_count = target_subarch_count(sub_arch_list); for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) { @@ -8149,50 +8474,30 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { } } } - - uint32_t list_count = target_subarch_list_count(); - // start at index 1 to skip None - for (uint32_t list_i = 1; list_i < list_count; list_i += 1) { - SubArchList sub_arch_list = target_subarch_list_enum(list_i); - const char *subarch_list_name = target_subarch_list_name(sub_arch_list); - buf_appendf(contents, " pub const %s = enum {\n", subarch_list_name); - size_t sub_count = target_subarch_count(sub_arch_list); - for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) { - ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i); - buf_appendf(contents, " %s,\n", target_subarch_name(sub)); - } - buf_appendf(contents, " };\n"); - } - buf_appendf(contents, "};\n\n"); } assert(cur_arch != nullptr); const char *cur_abi = nullptr; { - buf_appendf(contents, "pub const Abi = enum {\n"); uint32_t field_count = (uint32_t)target_abi_count(); for (uint32_t i = 0; i < field_count; i += 1) { ZigLLVM_EnvironmentType abi = target_abi_enum(i); const char *name = target_abi_name(abi); - buf_appendf(contents, " %s,\n", name); if (abi == g->zig_target->abi) { g->target_abi_index = i; cur_abi = name; } } - buf_appendf(contents, "};\n\n"); } assert(cur_abi != nullptr); const char *cur_obj_fmt = nullptr; { - buf_appendf(contents, "pub const ObjectFormat = enum {\n"); uint32_t field_count = (uint32_t)target_oformat_count(); for (uint32_t i = 0; i < field_count; i += 1) { ZigLLVM_ObjectFormatType oformat = target_oformat_enum(i); const char *name = target_oformat_name(oformat); - buf_appendf(contents, " %s,\n", name); ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); if (oformat == target_oformat) { @@ -8201,315 +8506,67 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { } } - buf_appendf(contents, "};\n\n"); } assert(cur_obj_fmt != nullptr); - { - buf_appendf(contents, "pub const GlobalLinkage = enum {\n"); - uint32_t field_count = array_length(global_linkage_values); - for (uint32_t i = 0; i < field_count; i += 1) { - const GlobalLinkageValue *value = &global_linkage_values[i]; - buf_appendf(contents, " %s,\n", value->name); - } - buf_appendf(contents, "};\n\n"); - } - { - buf_appendf(contents, - "pub const AtomicOrder = enum {\n" - " Unordered,\n" - " Monotonic,\n" - " Acquire,\n" - " Release,\n" - " AcqRel,\n" - " SeqCst,\n" - "};\n\n"); - } - { - buf_appendf(contents, - "pub const AtomicRmwOp = enum {\n" - " Xchg,\n" - " Add,\n" - " Sub,\n" - " And,\n" - " Nand,\n" - " Or,\n" - " Xor,\n" - " Max,\n" - " Min,\n" - "};\n\n"); - } - { - buf_appendf(contents, - "pub const Mode = enum {\n" - " Debug,\n" - " ReleaseSafe,\n" - " ReleaseFast,\n" - " ReleaseSmall,\n" - "};\n\n"); - } - { - buf_appendf(contents, "pub const TypeId = enum {\n"); - size_t field_count = type_id_len(); - for (size_t i = 0; i < field_count; i += 1) { - const ZigTypeId id = type_id_at_index(i); - buf_appendf(contents, " %s,\n", type_id_name(id)); - } - buf_appendf(contents, "};\n\n"); - } - { - buf_appendf(contents, - "pub const TypeInfo = union(TypeId) {\n" - " Type: void,\n" - " Void: void,\n" - " Bool: void,\n" - " NoReturn: void,\n" - " Int: Int,\n" - " Float: Float,\n" - " Pointer: Pointer,\n" - " Array: Array,\n" - " Struct: Struct,\n" - " ComptimeFloat: void,\n" - " ComptimeInt: void,\n" - " Undefined: void,\n" - " Null: void,\n" - " Optional: Optional,\n" - " ErrorUnion: ErrorUnion,\n" - " ErrorSet: ErrorSet,\n" - " Enum: Enum,\n" - " Union: Union,\n" - " Fn: Fn,\n" - " BoundFn: Fn,\n" - " ArgTuple: void,\n" - " Opaque: void,\n" - " Frame: void,\n" - " AnyFrame: AnyFrame,\n" - " Vector: Vector,\n" - " EnumLiteral: void,\n" - "\n\n" - " pub const Int = struct {\n" - " is_signed: bool,\n" - " bits: comptime_int,\n" - " };\n" - "\n" - " pub const Float = struct {\n" - " bits: comptime_int,\n" - " };\n" - "\n" - " pub const Pointer = struct {\n" - " size: Size,\n" - " is_const: bool,\n" - " is_volatile: bool,\n" - " alignment: comptime_int,\n" - " child: type,\n" - " is_allowzero: bool,\n" - "\n" - " pub const Size = enum {\n" - " One,\n" - " Many,\n" - " Slice,\n" - " C,\n" - " };\n" - " };\n" - "\n" - " pub const Array = struct {\n" - " len: comptime_int,\n" - " child: type,\n" - " };\n" - "\n" - " pub const ContainerLayout = enum {\n" - " Auto,\n" - " Extern,\n" - " Packed,\n" - " };\n" - "\n" - " pub const StructField = struct {\n" - " name: []const u8,\n" - " offset: ?comptime_int,\n" - " field_type: type,\n" - " };\n" - "\n" - " pub const Struct = struct {\n" - " layout: ContainerLayout,\n" - " fields: []StructField,\n" - " decls: []Declaration,\n" - " };\n" - "\n" - " pub const Optional = struct {\n" - " child: type,\n" - " };\n" - "\n" - " pub const ErrorUnion = struct {\n" - " error_set: type,\n" - " payload: type,\n" - " };\n" - "\n" - " pub const Error = struct {\n" - " name: []const u8,\n" - " value: comptime_int,\n" - " };\n" - "\n" - " pub const ErrorSet = ?[]Error;\n" - "\n" - " pub const EnumField = struct {\n" - " name: []const u8,\n" - " value: comptime_int,\n" - " };\n" - "\n" - " pub const Enum = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: type,\n" - " fields: []EnumField,\n" - " decls: []Declaration,\n" - " };\n" - "\n" - " pub const UnionField = struct {\n" - " name: []const u8,\n" - " enum_field: ?EnumField,\n" - " field_type: type,\n" - " };\n" - "\n" - " pub const Union = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: ?type,\n" - " fields: []UnionField,\n" - " decls: []Declaration,\n" - " };\n" - "\n" - " pub const CallingConvention = enum {\n" - " Unspecified,\n" - " C,\n" - " Cold,\n" - " Naked,\n" - " Stdcall,\n" - " Async,\n" - " };\n" - "\n" - " pub const FnArg = struct {\n" - " is_generic: bool,\n" - " is_noalias: bool,\n" - " arg_type: ?type,\n" - " };\n" - "\n" - " pub const Fn = struct {\n" - " calling_convention: CallingConvention,\n" - " is_generic: bool,\n" - " is_var_args: bool,\n" - " return_type: ?type,\n" - " args: []FnArg,\n" - " };\n" - "\n" - " pub const AnyFrame = struct {\n" - " child: ?type,\n" - " };\n" - "\n" - " pub const Vector = struct {\n" - " len: comptime_int,\n" - " child: type,\n" - " };\n" - "\n" - " pub const Declaration = struct {\n" - " name: []const u8,\n" - " is_pub: bool,\n" - " data: Data,\n" - "\n" - " pub const Data = union(enum) {\n" - " Type: type,\n" - " Var: type,\n" - " Fn: FnDecl,\n" - "\n" - " pub const FnDecl = struct {\n" - " fn_type: type,\n" - " inline_type: Inline,\n" - " calling_convention: CallingConvention,\n" - " is_var_args: bool,\n" - " is_extern: bool,\n" - " is_export: bool,\n" - " lib_name: ?[]const u8,\n" - " return_type: type,\n" - " arg_names: [][] const u8,\n" - "\n" - " pub const Inline = enum {\n" - " Auto,\n" - " Always,\n" - " Never,\n" - " };\n" - " };\n" - " };\n" - " };\n" - "};\n\n"); - static_assert(ContainerLayoutAuto == 0, ""); - static_assert(ContainerLayoutExtern == 1, ""); - static_assert(ContainerLayoutPacked == 2, ""); + // If any of these asserts trip then you need to either fix the internal compiler enum + // or the corresponding one in std.Target or std.builtin. + static_assert(ContainerLayoutAuto == 0, ""); + static_assert(ContainerLayoutExtern == 1, ""); + static_assert(ContainerLayoutPacked == 2, ""); - static_assert(CallingConventionUnspecified == 0, ""); - static_assert(CallingConventionC == 1, ""); - static_assert(CallingConventionCold == 2, ""); - static_assert(CallingConventionNaked == 3, ""); - static_assert(CallingConventionStdcall == 4, ""); - static_assert(CallingConventionAsync == 5, ""); + static_assert(CallingConventionUnspecified == 0, ""); + static_assert(CallingConventionC == 1, ""); + static_assert(CallingConventionCold == 2, ""); + static_assert(CallingConventionNaked == 3, ""); + static_assert(CallingConventionAsync == 4, ""); + static_assert(CallingConventionInterrupt == 5, ""); + static_assert(CallingConventionSignal == 6, ""); + static_assert(CallingConventionStdcall == 7, ""); + static_assert(CallingConventionFastcall == 8, ""); + static_assert(CallingConventionVectorcall == 9, ""); + static_assert(CallingConventionThiscall == 10, ""); + static_assert(CallingConventionAPCS == 11, ""); + static_assert(CallingConventionAAPCS == 12, ""); + static_assert(CallingConventionAAPCSVFP == 13, ""); - static_assert(FnInlineAuto == 0, ""); - static_assert(FnInlineAlways == 1, ""); - static_assert(FnInlineNever == 2, ""); + static_assert(FnInlineAuto == 0, ""); + static_assert(FnInlineAlways == 1, ""); + static_assert(FnInlineNever == 2, ""); - static_assert(BuiltinPtrSizeOne == 0, ""); - static_assert(BuiltinPtrSizeMany == 1, ""); - static_assert(BuiltinPtrSizeSlice == 2, ""); - static_assert(BuiltinPtrSizeC == 3, ""); - } - { - buf_appendf(contents, - "pub const FloatMode = enum {\n" - " Strict,\n" - " Optimized,\n" - "};\n\n"); - assert(FloatModeStrict == 0); - assert(FloatModeOptimized == 1); - } - { - buf_appendf(contents, - "pub const Endian = enum {\n" - " Big,\n" - " Little,\n" - "};\n\n"); - //assert(EndianBig == 0); - //assert(EndianLittle == 1); - } - { - buf_appendf(contents, - "pub const Version = struct {\n" - " major: u32,\n" - " minor: u32,\n" - " patch: u32,\n" - "};\n\n"); - } - { - buf_appendf(contents, - "pub const SubSystem = enum {\n" - " Console,\n" - " Windows,\n" - " Posix,\n" - " Native,\n" - " EfiApplication,\n" - " EfiBootServiceDriver,\n" - " EfiRom,\n" - " EfiRuntimeDriver,\n" - "};\n\n"); + static_assert(BuiltinPtrSizeOne == 0, ""); + static_assert(BuiltinPtrSizeMany == 1, ""); + static_assert(BuiltinPtrSizeSlice == 2, ""); + static_assert(BuiltinPtrSizeC == 3, ""); - assert(TargetSubsystemConsole == 0); - assert(TargetSubsystemWindows == 1); - assert(TargetSubsystemPosix == 2); - assert(TargetSubsystemNative == 3); - assert(TargetSubsystemEfiApplication == 4); - assert(TargetSubsystemEfiBootServiceDriver == 5); - assert(TargetSubsystemEfiRom == 6); - assert(TargetSubsystemEfiRuntimeDriver == 7); - } + static_assert(TargetSubsystemConsole == 0, ""); + static_assert(TargetSubsystemWindows == 1, ""); + static_assert(TargetSubsystemPosix == 2, ""); + static_assert(TargetSubsystemNative == 3, ""); + static_assert(TargetSubsystemEfiApplication == 4, ""); + static_assert(TargetSubsystemEfiBootServiceDriver == 5, ""); + static_assert(TargetSubsystemEfiRom == 6, ""); + static_assert(TargetSubsystemEfiRuntimeDriver == 7, ""); { const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; buf_appendf(contents, "pub const endian = %s;\n", endian_str); } + const char *out_type = nullptr; + switch (g->out_type) { + case OutTypeExe: + out_type = "Exe"; + break; + case OutTypeLib: + out_type = "Lib"; + break; + case OutTypeObj: + case OutTypeUnknown: // This happens when running the `zig builtin` command. + out_type = "Obj"; + break; + } + buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type); + const char *link_type = g->is_dynamic ? "Dynamic" : "Static"; + buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); @@ -8535,17 +8592,13 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { { TargetSubsystem detected_subsystem = detect_subsystem(g); if (detected_subsystem != TargetSubsystemAuto) { - buf_appendf(contents, "pub const subsystem = SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); + buf_appendf(contents, "pub const explicit_subsystem = SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); } } if (g->is_test_build) { buf_appendf(contents, - "const TestFn = struct {\n" - "name: []const u8,\n" - "func: fn()anyerror!void,\n" - "};\n" - "pub const test_functions = {}; // overwritten later\n" + "pub var test_functions: []TestFn = undefined; // overwritten later\n" ); } @@ -8556,10 +8609,6 @@ static ZigPackage *create_test_runner_pkg(CodeGen *g) { return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); } -static ZigPackage *create_panic_pkg(CodeGen *g) { - return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "panic.zig", "std.special"); -} - static Error define_builtin_compile_vars(CodeGen *g) { if (g->std_package == nullptr) return ErrorNone; @@ -8567,7 +8616,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { Error err; Buf *manifest_dir = buf_alloc(); - os_path_join(get_stage1_cache_path(), buf_create_from_str("builtin"), manifest_dir); + os_path_join(get_global_cache_dir(), buf_create_from_str("builtin"), manifest_dir); CacheHash cache_hash; cache_init(&cache_hash, manifest_dir); @@ -8580,6 +8629,8 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_buf(&cache_hash, compiler_id); cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->strip_debug_symbols); + cache_int(&cache_hash, g->out_type); + cache_bool(&cache_hash, g->is_dynamic); cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target->is_native); @@ -8596,6 +8647,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); cache_bool(&cache_hash, g->valgrind_support); + cache_bool(&cache_hash, g->link_eh_frame_hdr); cache_int(&cache_hash, detect_subsystem(g)); Buf digest = BUF_INIT; @@ -8638,22 +8690,23 @@ static Error define_builtin_compile_vars(CodeGen *g) { } } - assert(g->root_package); + assert(g->main_pkg); assert(g->std_package); g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin"); - g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); - ZigPackage *root_pkg; if (g->is_test_build) { if (g->test_runner_package == nullptr) { g->test_runner_package = create_test_runner_pkg(g); } - root_pkg = g->test_runner_package; + g->root_pkg = g->test_runner_package; } else { - root_pkg = g->root_package; + g->root_pkg = g->main_pkg; } - g->std_package->package_table.put(buf_create_from_str("root"), root_pkg); + g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package); + g->main_pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + g->main_pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); + g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); + g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg); g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents, SourceKindPkgMain); @@ -8667,6 +8720,7 @@ static void init(CodeGen *g) { g->have_dynamic_link = detect_dynamic_link(g); g->have_pic = detect_pic(g); g->have_stack_probing = detect_stack_probing(g); + g->have_sanitize_c = detect_sanitize_c(g); g->is_single_threaded = detect_single_threaded(g); g->have_err_ret_tracing = detect_err_ret_tracing(g); @@ -8727,6 +8781,14 @@ static void init(CodeGen *g) { // Be aware of https://github.com/ziglang/zig/issues/3275 target_specific_cpu_args = ""; target_specific_features = riscv_default_features; + } else if (g->zig_target->arch == ZigLLVM_x86) { + // This is because we're really targeting i686 rather than i386. + // It's pretty much impossible to use many of the language features + // such as fp16 if you stick use the x87 only. This is also what clang + // uses as base cpu. + // TODO https://github.com/ziglang/zig/issues/2883 + target_specific_cpu_args = "pentium4"; + target_specific_features = (g->zig_target->os == OsFreestanding) ? "-sse": ""; } else { target_specific_cpu_args = ""; target_specific_features = ""; @@ -8764,7 +8826,7 @@ static void init(CodeGen *g) { // no longer reference DW_AT_comp_dir, for the purpose of being able to support the // common practice of stripping all but the line number sections from an executable. const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." : - buf_ptr(&g->root_package->root_src_dir); + buf_ptr(&g->main_pkg->root_src_dir); ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name), compile_unit_dir); @@ -8776,26 +8838,16 @@ static void init(CodeGen *g) { g->dummy_di_file = nullptr; define_builtin_types(g); + define_intern_values(g); IrInstruction *sentinel_instructions = allocate(2); g->invalid_instruction = &sentinel_instructions[0]; - g->invalid_instruction->value.type = g->builtin_types.entry_invalid; - g->invalid_instruction->value.global_refs = allocate(1); + g->invalid_instruction->value = allocate(1, "ZigValue"); + g->invalid_instruction->value->type = g->builtin_types.entry_invalid; g->unreach_instruction = &sentinel_instructions[1]; - g->unreach_instruction->value.type = g->builtin_types.entry_unreachable; - g->unreach_instruction->value.global_refs = allocate(1); - - g->const_void_val.special = ConstValSpecialStatic; - g->const_void_val.type = g->builtin_types.entry_void; - g->const_void_val.global_refs = allocate(1); - - { - ConstGlobalRefs *global_refs = allocate(PanicMsgIdCount); - for (size_t i = 0; i < PanicMsgIdCount; i += 1) { - g->panic_msg_vals[i].global_refs = &global_refs[i]; - } - } + g->unreach_instruction->value = allocate(1, "ZigValue"); + g->unreach_instruction->value->type = g->builtin_types.entry_unreachable; define_builtin_fns(g); Error err; @@ -8888,10 +8940,50 @@ static void detect_libc(CodeGen *g) { if (g->zig_target->is_native) { g->libc = allocate(1); - // Look for zig-cache/native_libc.txt - Buf *native_libc_txt = buf_alloc(); - os_path_join(g->cache_dir, buf_create_from_str("native_libc.txt"), native_libc_txt); - if ((err = zig_libc_parse(g->libc, native_libc_txt, g->zig_target, false))) { + // search for native_libc.txt in following dirs: + // - LOCAL_CACHE_DIR + // - GLOBAL_CACHE_DIR + // if not found create at: + // - GLOBAL_CACHE_DIR + // be mindful local/global caches may be the same dir + + Buf basename = BUF_INIT; + buf_init_from_str(&basename, "native_libc.txt"); + + Buf local_libc_txt = BUF_INIT; + os_path_join(g->cache_dir, &basename, &local_libc_txt); + + Buf global_libc_txt = BUF_INIT; + os_path_join(get_global_cache_dir(), &basename, &global_libc_txt); + + Buf *pathnames[3] = { nullptr }; + size_t pathnames_idx = 0; + + pathnames[pathnames_idx] = &local_libc_txt; + pathnames_idx += 1; + + if (!buf_eql_buf(pathnames[0], &global_libc_txt)) { + pathnames[pathnames_idx] = &global_libc_txt; + pathnames_idx += 1; + } + + Buf* libc_txt = nullptr; + for (auto name : pathnames) { + if (name == nullptr) + break; + + bool result; + if (os_file_exists(name, &result) != ErrorNone || !result) + continue; + + libc_txt = name; + break; + } + + if (libc_txt == nullptr) + libc_txt = &global_libc_txt; + + if ((err = zig_libc_parse(g->libc, libc_txt, g->zig_target, false))) { if ((err = zig_libc_find_native(g->libc, true))) { fprintf(stderr, "Unable to link against libc: Unable to find libc installation: %s\n" @@ -8903,7 +8995,7 @@ static void detect_libc(CodeGen *g) { buf_ptr(g->cache_dir), err_str(err)); exit(1); } - Buf *native_libc_tmp = buf_sprintf("%s.tmp", buf_ptr(native_libc_txt)); + Buf *native_libc_tmp = buf_sprintf("%s.tmp", buf_ptr(libc_txt)); FILE *file = fopen(buf_ptr(native_libc_tmp), "wb"); if (file == nullptr) { fprintf(stderr, "Unable to open %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); @@ -8914,8 +9006,8 @@ static void detect_libc(CodeGen *g) { fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); exit(1); } - if ((err = os_rename(native_libc_tmp, native_libc_txt))) { - fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), err_str(err)); + if ((err = os_rename(native_libc_tmp, libc_txt))) { + fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(libc_txt), err_str(err)); exit(1); } } @@ -8943,6 +9035,10 @@ static void detect_libc(CodeGen *g) { g->libc_include_dir_len += 1; } assert(g->libc_include_dir_len == dir_count); + + buf_deinit(&global_libc_txt); + buf_deinit(&local_libc_txt); + buf_deinit(&basename); } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) && !target_os_is_darwin(g->zig_target->os)) { @@ -9016,7 +9112,9 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa } if (g->zig_target->is_native) { - args.append("-march=native"); + if (target_supports_clang_march_native(g->zig_target)) { + args.append("-march=native"); + } } else { args.append("-target"); args.append(buf_ptr(&g->llvm_triple_str)); @@ -9029,6 +9127,11 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa args.append("-target-feature"); args.append("-Xclang"); args.append(riscv_default_features); + } else if (g->zig_target->os == OsFreestanding && g->zig_target->arch == ZigLLVM_x86) { + args.append("-Xclang"); + args.append("-target-feature"); + args.append("-Xclang"); + args.append("-sse"); } } if (g->zig_target->os == OsFreestanding) { @@ -9051,6 +9154,11 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa args.append("-fomit-frame-pointer"); } + if (g->have_sanitize_c) { + args.append("-fsanitize=undefined"); + args.append("-fsanitize-trap=undefined"); + } + switch (g->build_mode) { case BuildModeDebug: // windows c runtime requires -D_DEBUG if using debug libraries @@ -9104,8 +9212,9 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa } -void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_userland_implementation) { +void codegen_translate_c(CodeGen *g, Buf *full_path) { Error err; + Buf *src_basename = buf_alloc(); Buf *src_dirname = buf_alloc(); os_path_split(full_path, src_dirname, src_basename); @@ -9113,16 +9222,61 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_us Buf noextname = BUF_INIT; os_path_extname(src_basename, &noextname, nullptr); + Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname)); + detect_libc(g); + Buf cache_digest = BUF_INIT; + buf_resize(&cache_digest, 0); + + CacheHash *cache_hash = nullptr; + if (g->enable_cache) { + if ((err = create_c_object_cache(g, &cache_hash, true))) { + // Already printed error; verbose = true + exit(1); + } + cache_file(cache_hash, full_path); + // to distinguish from generating a C object + cache_buf(cache_hash, buf_create_from_str("translate-c")); + + if ((err = cache_hit(cache_hash, &cache_digest))) { + if (err != ErrorInvalidFormat) { + fprintf(stderr, "unable to check cache: %s\n", err_str(err)); + exit(1); + } + } + if (cache_hash->manifest_file_path != nullptr) { + g->caches_to_release.append(cache_hash); + } + } + + if (g->enable_cache && buf_len(&cache_digest) != 0) { + // cache hit + Buf *cached_path = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s" OS_SEP "%s", + buf_ptr(g->cache_dir), buf_ptr(&cache_digest), buf_ptr(zig_basename)); + fprintf(stdout, "%s\n", buf_ptr(cached_path)); + return; + } + + // cache miss or cache disabled init(g); - Stage2TranslateMode trans_mode = buf_ends_with_str(full_path, ".h") ? - Stage2TranslateModeImport : Stage2TranslateModeTranslate; + Buf *out_dep_path = nullptr; + const char *out_dep_path_cstr = nullptr; + if (g->enable_cache) { + buf_alloc();// we can't know the digest until we do the C compiler invocation, so we + // need a tmp filename. + out_dep_path = buf_alloc(); + if ((err = get_tmp_filename(g, out_dep_path, buf_sprintf("%s.d", buf_ptr(zig_basename))))) { + fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); + exit(1); + } + out_dep_path_cstr = buf_ptr(out_dep_path); + } ZigList clang_argv = {0}; - add_cc_args(g, clang_argv, nullptr, true); + add_cc_args(g, clang_argv, out_dep_path_cstr, true); clang_argv.append(buf_ptr(full_path)); @@ -9140,24 +9294,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_us Stage2ErrorMsg *errors_ptr; size_t errors_len; Stage2Ast *ast; - AstNode *root_node; - if (use_userland_implementation) { - err = stage2_translate_c(&ast, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), trans_mode, resources_path); - } else { - err = parse_h_file(g, &root_node, &errors_ptr, &errors_len, &clang_argv.at(0), &clang_argv.last(), - trans_mode, resources_path); - } + err = stage2_translate_c(&ast, &errors_ptr, &errors_len, + &clang_argv.at(0), &clang_argv.last(), resources_path); if (err == ErrorCCompileErrors && errors_len > 0) { for (size_t i = 0; i < errors_len; i += 1) { Stage2ErrorMsg *clang_err = &errors_ptr[i]; + ErrorMsg *err_msg = err_msg_create_with_offset( - clang_err->filename_ptr ? - buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), - clang_err->line, clang_err->column, clang_err->offset, clang_err->source, - buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); + clang_err->filename_ptr ? + buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : nullptr, + clang_err->line, clang_err->column, clang_err->offset, clang_err->source, + buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); print_err_msg(err_msg, g->err_color); } exit(1); @@ -9168,38 +9317,53 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_us exit(1); } - - if (use_userland_implementation) { - stage2_render_ast(ast, out_file); - } else { - ast_render(out_file, root_node, 4); - } -} - -static ZigType *add_special_code(CodeGen *g, ZigPackage *package, const char *basename) { - Buf *code_basename = buf_create_from_str(basename); - Buf path_to_code_src = BUF_INIT; - os_path_join(g->zig_std_special_dir, code_basename, &path_to_code_src); - - Buf *resolve_paths[] = {&path_to_code_src}; - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(resolve_paths, 1); - Buf *import_code = buf_alloc(); - Error err; - if ((err = file_fetch(g, resolved_path, import_code))) { - zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); + if (!g->enable_cache) { + stage2_render_ast(ast, stdout); + return; } - return add_source_file(g, package, resolved_path, import_code, SourceKindPkgMain); + // add the files depended on to the cache system + if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { + // Don't treat the absence of the .d file as a fatal error, the + // compiler may not produce one eg. when compiling .s files + if (err != ErrorFileNotFound) { + fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); + exit(1); + } + } + if (err != ErrorFileNotFound) { + os_delete_file(out_dep_path); + } + + if ((err = cache_final(cache_hash, &cache_digest))) { + fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); + exit(1); + } + + Buf *artifact_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", + buf_ptr(g->cache_dir), buf_ptr(&cache_digest)); + + if ((err = os_make_path(artifact_dir))) { + fprintf(stderr, "Unable to make dir: %s\n", err_str(err)); + exit(1); + } + + Buf *cached_path = buf_sprintf("%s" OS_SEP "%s", buf_ptr(artifact_dir), buf_ptr(zig_basename)); + + FILE *out_file = fopen(buf_ptr(cached_path), "wb"); + if (out_file == nullptr) { + fprintf(stderr, "Unable to open output file: %s\n", strerror(errno)); + exit(1); + } + stage2_render_ast(ast, out_file); + if (fclose(out_file) != 0) { + fprintf(stderr, "Unable to write to output file: %s\n", strerror(errno)); + exit(1); + } + fprintf(stdout, "%s\n", buf_ptr(cached_path)); } -static ZigPackage *create_start_pkg(CodeGen *g, ZigPackage *pkg_with_main) { - ZigPackage *package = codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "start.zig", "std.special"); - package->package_table.put(buf_create_from_str("root"), pkg_with_main); - return package; -} - -static void create_test_compile_var_and_add_test_runner(CodeGen *g) { +static void update_test_functions_builtin_decl(CodeGen *g) { Error err; assert(g->is_test_build); @@ -9211,14 +9375,14 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ZigType *fn_type = get_test_fn_type(g); - ConstExprValue *test_fn_type_val = get_builtin_value(g, "TestFn"); + ZigValue *test_fn_type_val = get_builtin_value(g, "TestFn"); assert(test_fn_type_val->type->id == ZigTypeIdMetaType); ZigType *struct_type = test_fn_type_val->data.x_type; if ((err = type_resolve(g, struct_type, ResolveStatusSizeKnown))) zig_unreachable(); - ConstExprValue *test_fn_array = create_const_vals(1); - test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length); + ZigValue *test_fn_array = create_const_vals(1); + test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, nullptr); test_fn_array->special = ConstValSpecialStatic; test_fn_array->data.x_array.data.s_none.elements = create_const_vals(g->test_fns.length); @@ -9234,19 +9398,19 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { continue; } - ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; + ZigValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; this_val->parent.id = ConstParentIdArray; this_val->parent.data.p_array.array_val = test_fn_array; this_val->parent.data.p_array.elem_index = i; - this_val->data.x_struct.fields = create_const_vals(2); + this_val->data.x_struct.fields = alloc_const_vals_ptrs(2); - ConstExprValue *name_field = &this_val->data.x_struct.fields[0]; - ConstExprValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name); + ZigValue *name_field = this_val->data.x_struct.fields[0]; + ZigValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name)->data.x_ptr.data.ref.pointee; init_const_slice(g, name_field, name_array_val, 0, buf_len(&test_fn_entry->symbol_name), true); - ConstExprValue *fn_field = &this_val->data.x_struct.fields[1]; + ZigValue *fn_field = this_val->data.x_struct.fields[1]; fn_field->type = fn_type; fn_field->special = ConstValSpecialStatic; fn_field->data.x_ptr.special = ConstPtrSpecialFunction; @@ -9255,20 +9419,19 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { } report_errors_and_maybe_exit(g); - ConstExprValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); + ZigValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); update_compile_var(g, buf_create_from_str("test_functions"), test_fn_slice); assert(g->test_runner_package != nullptr); - g->test_runner_import = add_special_code(g, g->test_runner_package, "test_runner.zig"); } static Buf *get_resolved_root_src_path(CodeGen *g) { // TODO memoize - if (buf_len(&g->root_package->root_src_path) == 0) + if (buf_len(&g->main_pkg->root_src_path) == 0) return nullptr; Buf rel_full_path = BUF_INIT; - os_path_join(&g->root_package->root_src_dir, &g->root_package->root_src_path, &rel_full_path); + os_path_join(&g->main_pkg->root_src_dir, &g->main_pkg->root_src_path, &rel_full_path); Buf *resolved_path = buf_alloc(); Buf *resolve_paths[] = {&rel_full_path}; @@ -9277,38 +9440,6 @@ static Buf *get_resolved_root_src_path(CodeGen *g) { return resolved_path; } -static bool want_startup_code(CodeGen *g) { - // Test builds get handled separately. - if (g->is_test_build) - return false; - - // WASM freestanding can still have an entry point but other freestanding targets do not. - if (g->zig_target->os == OsFreestanding && !target_is_wasm(g->zig_target)) - return false; - - // Declaring certain export functions means skipping the start code - if (g->have_c_main || g->have_winmain || g->have_winmain_crt_startup) - return false; - - // If there is a pub main in the root source file, that means we need start code. - if (g->have_pub_main) { - return true; - } else { - if (g->zig_target->os == OsUefi) - return false; - } - - if (g->out_type == OutTypeExe) { - // For build-exe, we might add start code even though there is no pub main, so that the - // programmer gets the "no pub main" compile error. However if linking libc and there is - // a C source file, that might have main(). - return g->c_source_files.length == 0 || g->libc_link_lib == nullptr; - } - - // For objects and libraries, and we don't have pub main, no start code. - return false; -} - static void gen_root_source(CodeGen *g) { Buf *resolved_path = get_resolved_root_src_path(g); if (resolved_path == nullptr) @@ -9323,7 +9454,7 @@ static void gen_root_source(CodeGen *g) { exit(1); } - ZigType *root_import_alias = add_source_file(g, g->root_package, resolved_path, source_code, SourceKindRoot); + ZigType *root_import_alias = add_source_file(g, g->main_pkg, resolved_path, source_code, SourceKindRoot); assert(root_import_alias == g->root_import); assert(g->root_out_name); @@ -9331,49 +9462,57 @@ static void gen_root_source(CodeGen *g) { if (!g->is_dummy_so) { // Zig has lazy top level definitions. Here we semantically analyze the panic function. - ZigType *import_with_panic; - if (g->have_pub_panic) { - import_with_panic = g->root_import; - } else { - g->panic_package = create_panic_pkg(g); - import_with_panic = add_special_code(g, g->panic_package, "panic.zig"); + Buf *import_target_path; + Buf full_path = BUF_INIT; + ZigType *std_import; + if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import, + &import_target_path, &full_path))) + { + if (err == ErrorFileNotFound) { + fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); + } else { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); + } + exit(1); } - Tld *panic_tld = find_decl(g, &get_container_scope(import_with_panic)->base, buf_create_from_str("panic")); + + Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base, + buf_create_from_str("builtin")); + assert(builtin_tld != nullptr); + resolve_top_level_decl(g, builtin_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(builtin_tld->id == TldIdVar); + TldVar *builtin_tld_var = (TldVar*)builtin_tld; + ZigValue *builtin_val = builtin_tld_var->var->const_value; + assert(builtin_val->type->id == ZigTypeIdMetaType); + ZigType *builtin_type = builtin_val->data.x_type; + + Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base, + buf_create_from_str("panic")); assert(panic_tld != nullptr); resolve_top_level_decl(g, panic_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(panic_tld->id == TldIdVar); + TldVar *panic_tld_var = (TldVar*)panic_tld; + ZigValue *panic_fn_val = panic_tld_var->var->const_value; + assert(panic_fn_val->type->id == ZigTypeIdFn); + assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction); + g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; + assert(g->panic_fn != nullptr); } - if (!g->error_during_imports) { semantic_analyze(g); } report_errors_and_maybe_exit(g); - if (want_startup_code(g)) { - g->start_import = add_special_code(g, create_start_pkg(g, g->root_package), "start.zig"); - } - if (g->zig_target->os == OsWindows && !g->have_dllmain_crt_startup && - g->out_type == OutTypeLib && g->is_dynamic) - { - g->start_import = add_special_code(g, create_start_pkg(g, g->root_package), "start_lib.zig"); - } - - if (!g->error_during_imports) { - semantic_analyze(g); - } if (g->is_test_build) { - create_test_compile_var_and_add_test_runner(g); - g->start_import = add_special_code(g, create_start_pkg(g, g->test_runner_package), "start.zig"); - + update_test_functions_builtin_decl(g); if (!g->error_during_imports) { semantic_analyze(g); } } - if (!g->is_dummy_so) { - typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn); - } - report_errors_and_maybe_exit(g); } @@ -9430,6 +9569,7 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose cache_bool(cache_hash, g->strip_debug_symbols); cache_int(cache_hash, g->build_mode); cache_bool(cache_hash, g->have_pic); + cache_bool(cache_hash, g->have_sanitize_c); cache_bool(cache_hash, want_valgrind_support(g)); cache_bool(cache_hash, g->function_sections); for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { @@ -9452,6 +9592,10 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { Buf *c_source_file = buf_create_from_str(c_file->source_path); Buf *c_source_basename = buf_alloc(); os_path_split(c_source_file, nullptr, c_source_basename); + + Stage2ProgressNode *child_prog_node = stage2_progress_start(g->sub_progress_node, buf_ptr(c_source_basename), + buf_len(c_source_basename), 0); + Buf *final_o_basename = buf_alloc(); os_path_extname(c_source_basename, final_o_basename, nullptr); buf_append_str(final_o_basename, target_o_file_ext(g->zig_target)); @@ -9568,6 +9712,8 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { g->link_objects.append(o_final_path); g->caches_to_release.append(cache_hash); + + stage2_progress_end(child_prog_node); } // returns true if we had any cache misses @@ -9583,7 +9729,10 @@ static void gen_c_objects(CodeGen *g) { exit(1); } - codegen_add_time_event(g, "Compile C Code"); + codegen_add_time_event(g, "Compile C Objects"); + const char *c_prog_name = "Compile C Objects"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, c_prog_name, strlen(c_prog_name), + g->c_source_files.length)); for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { CFile *c_file = g->c_source_files.at(c_file_i); @@ -9625,7 +9774,6 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdFnFrame: @@ -9634,7 +9782,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdVoid: case ZigTypeIdUnreachable: case ZigTypeIdBool: + g->c_want_stdbool = true; + return; case ZigTypeIdInt: + g->c_want_stdint = true; + return; case ZigTypeIdFloat: return; case ZigTypeIdOpaque: @@ -9642,7 +9794,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e return; case ZigTypeIdStruct: for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *field = &type_entry->data.structure.fields[i]; + TypeStructField *field = type_entry->data.structure.fields[i]; prepend_c_type_to_decl_list(g, gen_h, field->type_entry); } gen_h->types_to_declare.append(type_entry); @@ -9715,7 +9867,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu break; case ZigTypeIdBool: buf_init_from_str(out_buf, "bool"); - g->c_want_stdbool = true; break; case ZigTypeIdUnreachable: buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void"); @@ -9739,7 +9890,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu } break; case ZigTypeIdInt: - g->c_want_stdint = true; buf_resize(out_buf, 0); buf_appendf(out_buf, "%sint%" PRIu32 "_t", type_entry->data.integral.is_signed ? "" : "u", @@ -9812,7 +9962,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: zig_unreachable(); @@ -9851,113 +10000,7 @@ static Buf *preprocessor_mangle(Buf *src) { return result; } -static void gen_h_file(CodeGen *g) { - GenH gen_h_data = {0}; - GenH *gen_h = &gen_h_data; - - assert(!g->is_test_build); - assert(!g->disable_gen_h); - - Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); - - FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); - if (!out_h) - zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); - - Buf *export_macro = nullptr; - if (g->is_dynamic) { - export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); - buf_upcase(export_macro); - } - - Buf *extern_c_macro = preprocessor_mangle(buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name))); - buf_upcase(extern_c_macro); - - Buf h_buf = BUF_INIT; - buf_resize(&h_buf, 0); - for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { - ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - - if (fn_table_entry->export_list.length == 0) - continue; - - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - - Buf return_type_c = BUF_INIT; - get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); - - Buf *symbol_name; - if (fn_table_entry->export_list.length == 0) { - symbol_name = &fn_table_entry->symbol_name; - } else { - GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; - symbol_name = &fn_export->name; - } - - buf_appendf(&h_buf, "%s %s %s(", - buf_ptr(g->is_dynamic ? export_macro : extern_c_macro), - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - - Buf param_type_c = BUF_INIT; - if (fn_type_id->param_count > 0) { - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; - AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); - Buf *param_name = param_decl_node->data.param_decl.name; - - const char *comma_str = (param_i == 0) ? "" : ", "; - const char *restrict_str = param_info->is_noalias ? "restrict" : ""; - get_c_type(g, gen_h, param_info->type, ¶m_type_c); - - if (param_info->type->id == ZigTypeIdArray) { - // Arrays decay to pointers - buf_appendf(&h_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } else { - buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } - } - buf_appendf(&h_buf, ")"); - } else { - buf_appendf(&h_buf, "void)"); - } - - buf_appendf(&h_buf, ";\n"); - - } - - Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); - buf_upcase(ifdef_dance_name); - - fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); - fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); - - if (g->c_want_stdbool) - fprintf(out_h, "#include \n"); - if (g->c_want_stdint) - fprintf(out_h, "#include \n"); - - fprintf(out_h, "\n"); - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "#define %s extern \"C\"\n", buf_ptr(extern_c_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s\n", buf_ptr(extern_c_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - - if (g->is_dynamic) { - fprintf(out_h, "#if defined(_WIN32)\n"); - fprintf(out_h, "#define %s %s __declspec(dllimport)\n", buf_ptr(export_macro), buf_ptr(extern_c_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s %s __attribute__((visibility (\"default\")))\n", - buf_ptr(export_macro), buf_ptr(extern_c_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - } - +static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) { for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) { ZigType *type_entry = gen_h->types_to_declare.at(type_i); switch (type_entry->id) { @@ -9978,7 +10021,6 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdVector: @@ -9988,69 +10030,220 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdEnum: if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { - fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; Buf *value_buf = buf_alloc(); bigint_append_buf(value_buf, &enum_field->value, 10); - fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); + buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); if (field_i != type_entry->data.enumeration.src_field_count - 1) { - fprintf(out_h, ","); + buf_appendf(out_buf, ","); } - fprintf(out_h, "\n"); + buf_appendf(out_buf, "\n"); } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "enum %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdStruct: if (type_entry->data.structure.layout == ContainerLayoutExtern) { - fprintf(out_h, "struct %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { - TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; + TypeStructField *struct_field = type_entry->data.structure.fields[field_i]; Buf *type_name_buf = buf_alloc(); get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); if (struct_field->type_entry->id == ZigTypeIdArray) { - fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), + buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name), struct_field->type_entry->data.array.len); } else { - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); + buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); } } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "struct %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdUnion: if (type_entry->data.unionation.layout == ContainerLayoutExtern) { - fprintf(out_h, "union %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; Buf *type_name_buf = buf_alloc(); get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "union %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdOpaque: - fprintf(out_h, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); break; } } +} - fprintf(out_h, "%s", buf_ptr(&h_buf)); +static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) { + for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { + ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - fprintf(out_h, "\n#endif\n"); + if (fn_table_entry->export_list.length == 0) + continue; + + FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; + + Buf return_type_c = BUF_INIT; + get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); + + Buf *symbol_name; + if (fn_table_entry->export_list.length == 0) { + symbol_name = &fn_table_entry->symbol_name; + } else { + GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; + symbol_name = &fn_export->name; + } + + if (export_macro != nullptr) { + buf_appendf(out_buf, "%s %s %s(", + buf_ptr(export_macro), + buf_ptr(&return_type_c), + buf_ptr(symbol_name)); + } else { + buf_appendf(out_buf, "%s %s(", + buf_ptr(&return_type_c), + buf_ptr(symbol_name)); + } + + Buf param_type_c = BUF_INIT; + if (fn_type_id->param_count > 0) { + for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { + FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; + AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); + Buf *param_name = param_decl_node->data.param_decl.name; + + const char *comma_str = (param_i == 0) ? "" : ", "; + const char *restrict_str = param_info->is_noalias ? "restrict" : ""; + get_c_type(g, gen_h, param_info->type, ¶m_type_c); + + if (param_info->type->id == ZigTypeIdArray) { + // Arrays decay to pointers + buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), + restrict_str, buf_ptr(param_name)); + } else { + buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), + restrict_str, buf_ptr(param_name)); + } + } + buf_appendf(out_buf, ")"); + } else { + buf_appendf(out_buf, "void)"); + } + + buf_appendf(out_buf, ";\n"); + } +} + +static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) { + for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) { + ZigVar* var = g->global_vars.at(exp_var_i)->var; + if (var->export_list.length == 0) + continue; + + Buf var_type_c = BUF_INIT; + get_c_type(g, gen_h, var->var_type, &var_type_c); + + if (export_macro != nullptr) { + buf_appendf(h_buf, "extern %s %s %s;\n", + buf_ptr(export_macro), + buf_ptr(&var_type_c), + var->name); + } else { + buf_appendf(h_buf, "extern %s %s;\n", + buf_ptr(&var_type_c), + var->name); + } + } +} + +static void gen_h_file(CodeGen *g) { + GenH gen_h_data = {0}; + GenH *gen_h = &gen_h_data; + + assert(!g->is_test_build); + assert(!g->disable_gen_h); + + Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); + + FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); + if (!out_h) + zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); + + Buf *export_macro = nullptr; + if (g->is_dynamic) { + export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); + buf_upcase(export_macro); + } + + Buf fns_buf = BUF_INIT; + buf_resize(&fns_buf, 0); + gen_h_file_functions(g, gen_h, &fns_buf, export_macro); + + Buf vars_buf = BUF_INIT; + buf_resize(&vars_buf, 0); + gen_h_file_variables(g, gen_h, &vars_buf, export_macro); + + // Types will be populated by exported functions and variables so it has to run last. + Buf types_buf = BUF_INIT; + buf_resize(&types_buf, 0); + gen_h_file_types(g, gen_h, &types_buf); + + Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); + buf_upcase(ifdef_dance_name); + + fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); + fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); + + if (g->c_want_stdbool) + fprintf(out_h, "#include \n"); + if (g->c_want_stdint) + fprintf(out_h, "#include \n"); + + fprintf(out_h, "\n"); + + if (g->is_dynamic) { + fprintf(out_h, "#if defined(_WIN32)\n"); + fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro)); + fprintf(out_h, "#else\n"); + fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n", + buf_ptr(export_macro)); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + } + + fprintf(out_h, "%s", buf_ptr(&types_buf)); + + fprintf(out_h, "#ifdef __cplusplus\n"); + fprintf(out_h, "extern \"C\" {\n"); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + + fprintf(out_h, "%s\n", buf_ptr(&fns_buf)); + + fprintf(out_h, "#ifdef __cplusplus\n"); + fprintf(out_h, "} // extern \"C\"\n"); + fprintf(out_h, "#endif\n\n"); + + fprintf(out_h, "%s\n", buf_ptr(&vars_buf)); + + fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name)); if (fclose(out_h)) zig_panic("unable to close h file: %s", strerror(errno)); @@ -10114,7 +10307,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { CacheHash *ch = &g->cache_hash; cache_init(ch, manifest_dir); - add_cache_pkg(g, ch, g->root_package); + add_cache_pkg(g, ch, g->main_pkg); if (g->linker_script != nullptr) { cache_file(ch, buf_create_from_str(g->linker_script)); } @@ -10146,6 +10339,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_buf_opt(ch, g->test_filter); cache_buf_opt(ch, g->test_name_prefix); } + cache_bool(ch, g->link_eh_frame_hdr); cache_bool(ch, g->is_single_threaded); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->each_lib_rpath); @@ -10155,8 +10349,12 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->have_pic); cache_bool(ch, g->have_dynamic_link); cache_bool(ch, g->have_stack_probing); + cache_bool(ch, g->have_sanitize_c); cache_bool(ch, g->is_dummy_so); cache_bool(ch, g->function_sections); + cache_bool(ch, g->enable_dump_analysis); + cache_bool(ch, g->enable_doc_generation); + cache_bool(ch, g->disable_bin_generation); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); @@ -10194,7 +10392,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { } static bool need_llvm_module(CodeGen *g) { - return buf_len(&g->root_package->root_src_path) != 0; + return buf_len(&g->main_pkg->root_src_path) != 0; } static void resolve_out_paths(CodeGen *g) { @@ -10272,6 +10470,7 @@ void codegen_build_and_link(CodeGen *g) { g->have_pic = detect_pic(g); g->is_single_threaded = detect_single_threaded(g); g->have_err_ret_tracing = detect_err_ret_tracing(g); + g->have_sanitize_c = detect_sanitize_c(g); detect_libc(g); detect_dynamic_linker(g); @@ -10305,6 +10504,9 @@ void codegen_build_and_link(CodeGen *g) { init(g); codegen_add_time_event(g, "Semantic Analysis"); + const char *progress_name = "Semantic Analysis"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); gen_root_source(g); @@ -10328,23 +10530,90 @@ void codegen_build_and_link(CodeGen *g) { if (need_llvm_module(g)) { codegen_add_time_event(g, "Code Generation"); + { + const char *progress_name = "Code Generation"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } do_code_gen(g); codegen_add_time_event(g, "LLVM Emit Output"); + { + const char *progress_name = "LLVM Emit Output"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } zig_llvm_emit_output(g); if (!g->disable_gen_h && (g->out_type == OutTypeObj || g->out_type == OutTypeLib)) { codegen_add_time_event(g, "Generate .h"); + { + const char *progress_name = "Generate .h"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } gen_h_file(g); } } + if (g->enable_dump_analysis) { + const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", + buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); + FILE *f = fopen(analysis_json_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + zig_print_analysis_dump(g, f, " ", "\n"); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + } + if (g->enable_doc_generation) { + Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "docs", buf_ptr(g->output_dir)); + if ((err = os_make_path(doc_dir_path))) { + fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); + exit(1); + } + Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "index.html", + buf_ptr(g->zig_std_dir)); + Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); + Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "main.js", + buf_ptr(g->zig_std_dir)); + Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); + + if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path), + buf_ptr(index_html_dest_path), err_str(err)); + exit(1); + } + if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path), + buf_ptr(main_js_dest_path), err_str(err)); + exit(1); + } + const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path))); + FILE *f = fopen(data_js_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } + fprintf(f, "zigAnalysis="); + zig_print_analysis_dump(g, f, "", ""); + fprintf(f, ";"); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } + } // If we're outputting assembly or llvm IR we skip linking. // If we're making a library or executable we must link. // If there is more than one object, we have to link them (with -r). // Finally, if we didn't make an object from zig source, and we don't have caching enabled, // then we have an object from C source that we must copy to the output dir which we do with a -r link. - if (g->emit_file_type == EmitFileTypeBinary && (g->out_type != OutTypeObj || g->link_objects.length > 1 || + if (!g->disable_bin_generation && g->emit_file_type == EmitFileTypeBinary && + (g->out_type != OutTypeObj || g->link_objects.length > 1 || (!need_llvm_module(g) && !g->enable_cache))) { codegen_link(g); @@ -10353,6 +10622,7 @@ void codegen_build_and_link(CodeGen *g) { codegen_release_caches(g); codegen_add_time_event(g, "Done"); + codegen_switch_sub_prog_node(g, nullptr); } void codegen_release_caches(CodeGen *g) { @@ -10370,8 +10640,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c assert(g->compile_var_package != nullptr); pkg->package_table.put(buf_create_from_str("std"), g->std_package); - ZigPackage *main_pkg = g->is_test_build ? g->test_runner_package : g->root_package; - pkg->package_table.put(buf_create_from_str("root"), main_pkg); + pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); } @@ -10379,12 +10648,18 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c } CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc) + ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) { + Stage2ProgressNode *child_progress_node = stage2_progress_start( + parent_progress_node ? parent_progress_node : parent_gen->sub_progress_node, + name, strlen(name), 0); + CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_stage1_cache_path(), false); + parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_global_cache_dir(), false, child_progress_node); + child_gen->root_out_name = buf_create_from_str(name); child_gen->disable_gen_h = true; child_gen->want_stack_check = WantStackCheckDisabled; + child_gen->want_sanitize_c = WantCSanitizeDisabled; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; child_gen->verbose_ast = parent_gen->verbose_ast; child_gen->verbose_link = parent_gen->verbose_link; @@ -10411,11 +10686,17 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *override_lib_dir, - ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build) + ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) { CodeGen *g = allocate(1); + g->main_progress_node = progress_node; codegen_add_time_event(g, "Initialize"); + { + const char *progress_name = "Initialize"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } g->subsystem = TargetSubsystemAuto; g->libc = libc; @@ -10449,6 +10730,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->external_prototypes.init(8); g->string_literals_table.init(16); g->type_info_cache.init(32); + g->one_possible_values.init(32); g->is_test_build = is_test_build; g->is_single_threaded = false; buf_resize(&g->global_asm, 0); @@ -10486,15 +10768,13 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); } - g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); + g->main_pkg = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); - g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); + g->main_pkg->package_table.put(buf_create_from_str("std"), g->std_package); } else { - g->root_package = new_package(".", "", ""); + g->main_pkg = new_package(".", "", ""); } - g->root_package->package_table.put(buf_create_from_str("root"), g->root_package); - g->zig_std_special_dir = buf_alloc(); os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); @@ -10540,3 +10820,45 @@ bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async) !codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type); } } + +void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) { + if (g->sub_progress_node != nullptr) { + stage2_progress_end(g->sub_progress_node); + } + g->sub_progress_node = node; +} + +ZigValue *CodeGen::Intern::for_undefined() { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_intern_count.x_undefined += 1; +#endif + return &this->x_undefined; +} + +ZigValue *CodeGen::Intern::for_void() { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_intern_count.x_void += 1; +#endif + return &this->x_void; +} + +ZigValue *CodeGen::Intern::for_null() { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_intern_count.x_null += 1; +#endif + return &this->x_null; +} + +ZigValue *CodeGen::Intern::for_unreachable() { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_intern_count.x_unreachable += 1; +#endif + return &this->x_unreachable; +} + +ZigValue *CodeGen::Intern::for_zero_byte() { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_intern_count.zero_byte += 1; +#endif + return &this->zero_byte; +} diff --git a/src/codegen.hpp b/src/codegen.hpp index 5be15b051..b9bcf05d2 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -18,10 +18,10 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, - ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build); + ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node); CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc); + ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); @@ -46,7 +46,7 @@ void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patc void codegen_add_time_event(CodeGen *g, const char *name); void codegen_print_timing_report(CodeGen *g, FILE *f); void codegen_link(CodeGen *g); -void zig_link_add_compiler_rt(CodeGen *g); +void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node); void codegen_build_and_link(CodeGen *g); ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, @@ -54,7 +54,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c void codegen_add_assembly(CodeGen *g, Buf *path); void codegen_add_object(CodeGen *g, Buf *object_path); -void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_userland_implementation); +void codegen_translate_c(CodeGen *g, Buf *full_path); Buf *codegen_generate_builtin_source(CodeGen *g); @@ -64,4 +64,9 @@ void codegen_release_caches(CodeGen *codegen); bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); +ATTRIBUTE_NORETURN +void codegen_report_errors_and_exit(CodeGen *g); + +void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node); + #endif diff --git a/src/compiler.cpp b/src/compiler.cpp index 50be7416b..484a4ca08 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -1,34 +1,12 @@ #include "cache_hash.hpp" #include "os.hpp" +#include "compiler.hpp" #include -static Buf saved_compiler_id = BUF_INIT; -static Buf saved_app_data_dir = BUF_INIT; -static Buf saved_stage1_path = BUF_INIT; -static Buf saved_lib_dir = BUF_INIT; -static Buf saved_special_dir = BUF_INIT; -static Buf saved_std_dir = BUF_INIT; - static Buf saved_dynamic_linker_path = BUF_INIT; static bool searched_for_dyn_linker = false; -static Buf saved_libc_path = BUF_INIT; -static bool searched_for_libc = false; - -Buf *get_stage1_cache_path(void) { - if (saved_stage1_path.list.length != 0) { - return &saved_stage1_path; - } - Error err; - if ((err = os_get_app_data_dir(&saved_app_data_dir, "zig"))) { - fprintf(stderr, "Unable to get app data dir: %s\n", err_str(err)); - exit(1); - } - os_path_join(&saved_app_data_dir, buf_create_from_str("stage1"), &saved_stage1_path); - return &saved_stage1_path; -} - static void detect_dynamic_linker(Buf *lib_path) { #if defined(ZIG_OS_LINUX) for (size_t i = 0; possible_ld_names[i] != NULL; i += 1) { @@ -40,7 +18,10 @@ static void detect_dynamic_linker(Buf *lib_path) { #endif } -const Buf *get_self_libc_path(void) { +Buf *get_self_libc_path(void) { + static Buf saved_libc_path = BUF_INIT; + static bool searched_for_libc = false; + for (;;) { if (saved_libc_path.list.length != 0) { return &saved_libc_path; @@ -82,15 +63,16 @@ Buf *get_self_dynamic_linker_path(void) { } Error get_compiler_id(Buf **result) { + static Buf saved_compiler_id = BUF_INIT; + if (saved_compiler_id.list.length != 0) { *result = &saved_compiler_id; return ErrorNone; } Error err; - Buf *stage1_dir = get_stage1_cache_path(); Buf *manifest_dir = buf_alloc(); - os_path_join(stage1_dir, buf_create_from_str("exe"), manifest_dir); + os_path_join(get_global_cache_dir(), buf_create_from_str("exe"), manifest_dir); CacheHash cache_hash; CacheHash *ch = &cache_hash; @@ -190,9 +172,9 @@ static int find_zig_lib_dir(Buf *out_path) { } Buf *get_zig_lib_dir(void) { - if (saved_lib_dir.list.length != 0) { + static Buf saved_lib_dir = BUF_INIT; + if (saved_lib_dir.list.length != 0) return &saved_lib_dir; - } buf_resize(&saved_lib_dir, 0); int err; @@ -204,9 +186,9 @@ Buf *get_zig_lib_dir(void) { } Buf *get_zig_std_dir(Buf *zig_lib_dir) { - if (saved_std_dir.list.length != 0) { + static Buf saved_std_dir = BUF_INIT; + if (saved_std_dir.list.length != 0) return &saved_std_dir; - } buf_resize(&saved_std_dir, 0); os_path_join(zig_lib_dir, buf_create_from_str("std"), &saved_std_dir); @@ -215,12 +197,29 @@ Buf *get_zig_std_dir(Buf *zig_lib_dir) { } Buf *get_zig_special_dir(Buf *zig_lib_dir) { - if (saved_special_dir.list.length != 0) { + static Buf saved_special_dir = BUF_INIT; + if (saved_special_dir.list.length != 0) return &saved_special_dir; - } buf_resize(&saved_special_dir, 0); os_path_join(get_zig_std_dir(zig_lib_dir), buf_sprintf("special"), &saved_special_dir); return &saved_special_dir; } + +Buf *get_global_cache_dir(void) { + static Buf saved_global_cache_dir = BUF_INIT; + if (saved_global_cache_dir.list.length != 0) + return &saved_global_cache_dir; + buf_resize(&saved_global_cache_dir, 0); + + Buf app_data_dir = BUF_INIT; + Error err; + if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) { + fprintf(stderr, "Unable to get application data dir: %s\n", err_str(err)); + exit(1); + } + os_path_join(&app_data_dir, buf_create_from_str("stage1"), &saved_global_cache_dir); + buf_deinit(&app_data_dir); + return &saved_global_cache_dir; +} diff --git a/src/compiler.hpp b/src/compiler.hpp index 62991570f..ee9c4ab4f 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -11,7 +11,6 @@ #include "buffer.hpp" #include "error.hpp" -Buf *get_stage1_cache_path(void); Error get_compiler_id(Buf **result); Buf *get_self_dynamic_linker_path(void); Buf *get_self_libc_path(void); @@ -20,4 +19,6 @@ Buf *get_zig_lib_dir(void); Buf *get_zig_special_dir(Buf *zig_lib_dir); Buf *get_zig_std_dir(Buf *zig_lib_dir); +Buf *get_global_cache_dir(void); + #endif diff --git a/src/config.h.in b/src/config.h.in index a99aab0d7..7a0ea3536 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -13,9 +13,6 @@ #define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ #define ZIG_VERSION_STRING "@ZIG_VERSION@" -// Only used for running tests before installing. -#define ZIG_TEST_DIR "@CMAKE_SOURCE_DIR@/test" - // Used for communicating build information to self hosted build. #define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" #define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" @@ -24,4 +21,6 @@ #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" +#cmakedefine ZIG_ENABLE_MEM_PROFILE + #endif diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp new file mode 100644 index 000000000..c31438b65 --- /dev/null +++ b/src/dump_analysis.cpp @@ -0,0 +1,1364 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "dump_analysis.hpp" +#include "compiler.hpp" +#include "analyze.hpp" +#include "config.h" +#include "ir.hpp" +#include "codegen.hpp" + +enum JsonWriterState { + JsonWriterStateInvalid, + JsonWriterStateValue, + JsonWriterStateArrayStart, + JsonWriterStateArray, + JsonWriterStateObjectStart, + JsonWriterStateObject, +}; + +#define JSON_MAX_DEPTH 10 + +struct JsonWriter { + size_t state_index; + FILE *f; + const char *one_indent; + const char *nl; + JsonWriterState state[JSON_MAX_DEPTH]; +}; + +static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { + jw->state_index = 1; + jw->f = f; + jw->one_indent = one_indent; + jw->nl = nl; + jw->state[0] = JsonWriterStateInvalid; + jw->state[1] = JsonWriterStateValue; +} + +static void jw_nl_indent(JsonWriter *jw) { + assert(jw->state_index >= 1); + fprintf(jw->f, "%s", jw->nl); + for (size_t i = 0; i < jw->state_index - 1; i += 1) { + fprintf(jw->f, "%s", jw->one_indent); + } +} + +static void jw_push_state(JsonWriter *jw, JsonWriterState state) { + jw->state_index += 1; + assert(jw->state_index < JSON_MAX_DEPTH); + jw->state[jw->state_index] = state; +} + +static void jw_pop_state(JsonWriter *jw) { + assert(jw->state_index != 0); + jw->state_index -= 1; +} + +static void jw_begin_array(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "["); + jw->state[jw->state_index] = JsonWriterStateArrayStart; +} + +static void jw_begin_object(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "{"); + jw->state[jw->state_index] = JsonWriterStateObjectStart; +} + +static void jw_array_elem(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArray: + fprintf(jw->f, ","); + // fallthrough + case JsonWriterStateArrayStart: + jw->state[jw->state_index] = JsonWriterStateArray; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + return; + } + zig_unreachable(); +} + +static void jw_write_escaped_string(JsonWriter *jw, const char *s) { + fprintf(jw->f, "\""); + for (;; s += 1) { + switch (*s) { + case 0: + fprintf(jw->f, "\""); + return; + case '"': + fprintf(jw->f, "\\\""); + continue; + case '\t': + fprintf(jw->f, "\\t"); + continue; + case '\r': + fprintf(jw->f, "\\r"); + continue; + case '\n': + fprintf(jw->f, "\\n"); + continue; + case '\b': + fprintf(jw->f, "\\b"); + continue; + case '\f': + fprintf(jw->f, "\\f"); + continue; + case '\\': + fprintf(jw->f, "\\\\"); + continue; + default: + fprintf(jw->f, "%c", *s); + continue; + } + } +} + +static void jw_object_field(JsonWriter *jw, const char *name) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateArray: + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObject: + fprintf(jw->f, ","); + // fallthrough + case JsonWriterStateObjectStart: + jw->state[jw->state_index] = JsonWriterStateObject; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + jw_write_escaped_string(jw, name); + fprintf(jw->f, ": "); + return; + } + zig_unreachable(); +} + +static void jw_end_array(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArrayStart: + fprintf(jw->f, "]"); + jw_pop_state(jw); + return; + case JsonWriterStateArray: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "]"); + return; + } + zig_unreachable(); +} + + +static void jw_end_object(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + zig_unreachable(); + case JsonWriterStateValue: + zig_unreachable(); + case JsonWriterStateArray: + zig_unreachable(); + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObjectStart: + fprintf(jw->f, "}"); + jw_pop_state(jw); + return; + case JsonWriterStateObject: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "}"); + return; + } + zig_unreachable(); +} + +static void jw_null(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "null"); + jw_pop_state(jw); +} + +static void jw_bool(JsonWriter *jw, bool x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x) { + fprintf(jw->f, "true"); + } else { + fprintf(jw->f, "false"); + } + jw_pop_state(jw); +} + +static void jw_int(JsonWriter *jw, int64_t x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x > 4503599627370496 || x < -4503599627370496) { + fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); + } else { + fprintf(jw->f, "%" ZIG_PRI_i64, x); + } + jw_pop_state(jw); +} + +static void jw_bigint(JsonWriter *jw, const BigInt *x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + Buf *str = buf_alloc(); + bigint_append_buf(str, x, 10); + + if (bigint_fits_in_bits(x, 52, true)) { + fprintf(jw->f, "%s", buf_ptr(str)); + } else { + fprintf(jw->f, "\"%s\"", buf_ptr(str)); + } + jw_pop_state(jw); + + buf_destroy(str); +} + +static void jw_string(JsonWriter *jw, const char *s) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + jw_write_escaped_string(jw, s); + jw_pop_state(jw); +} + + +static void tree_print(FILE *f, ZigType *ty, size_t indent); + +static int compare_type_abi_sizes_desc(const void *a, const void *b) { + uint64_t size_a = (*(ZigType * const*)(a))->abi_size; + uint64_t size_b = (*(ZigType * const*)(b))->abi_size; + if (size_a > size_b) + return -1; + if (size_a < size_b) + return 1; + return 0; +} + +static void start_child(FILE *f, size_t indent) { + fprintf(f, "\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void start_peer(FILE *f, size_t indent) { + fprintf(f, ",\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { + ZigList children = {}; + uint64_t sum_from_fields = 0; + for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + children.append(field->type_entry); + sum_from_fields += field->type_entry->abi_size; + } + qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); + + start_peer(f, indent); + fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); + + start_peer(f, indent); + fprintf(f, "\"fields\": ["); + + for (size_t i = 0; i < children.length; i += 1) { + if (i == 0) { + start_child(f, indent + 1); + } else { + start_peer(f, indent + 1); + } + fprintf(f, "{"); + + ZigType *child_type = children.at(i); + tree_print(f, child_type, indent + 2); + + start_child(f, indent + 1); + fprintf(f, "}"); + } + + start_child(f, indent); + fprintf(f, "]"); +} + +static void tree_print(FILE *f, ZigType *ty, size_t indent) { + start_child(f, indent); + fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); + + start_peer(f, indent); + fprintf(f, "\"sizef\": \""); + zig_pretty_print_bytes(f, ty->abi_size); + fprintf(f, "\""); + + start_peer(f, indent); + fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); + + switch (ty->id) { + case ZigTypeIdFnFrame: + return tree_print_struct(f, ty->data.frame.locals_struct, indent); + case ZigTypeIdStruct: + return tree_print_struct(f, ty, indent); + default: + start_child(f, indent); + return; + } +} + +void zig_print_stack_report(CodeGen *g, FILE *f) { + if (g->largest_frame_fn == nullptr) { + fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); + return; + } + fprintf(f, "{"); + tree_print(f, g->largest_frame_fn->frame_type, 1); + + start_child(f, 0); + fprintf(f, "}\n"); +} + +struct AnalDumpCtx { + CodeGen *g; + JsonWriter jw; + + ZigList type_list; + HashMap type_map; + + ZigList pkg_list; + HashMap pkg_map; + + ZigList file_list; + HashMap file_map; + + ZigList decl_list; + HashMap decl_map; + + ZigList fn_list; + HashMap fn_map; + + ZigList node_list; + HashMap node_map; + + ZigList err_list; + HashMap err_map; +}; + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value); + +static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { + Error err; + if (value->type != ty) { + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + return; + } + if (value->special == ConstValSpecialRuntime) { + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + (void)anal_dump_get_type_id(ctx, val_ty); + return; + } + default: + return; + } + zig_unreachable(); +} + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = ctx->type_list.length; + auto existing_entry = ctx->type_map.put_unique(ty, type_id); + if (existing_entry == nullptr) { + ctx->type_list.append(ty); + } else { + type_id = existing_entry->value; + } + return type_id; +} + +static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { + assert(pkg != nullptr); + uint32_t pkg_id = ctx->pkg_list.length; + auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); + if (existing_entry == nullptr) { + ctx->pkg_list.append(pkg); + } else { + pkg_id = existing_entry->value; + } + return pkg_id; +} + +static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = ctx->file_list.length; + auto existing_entry = ctx->file_map.put_unique(file, file_id); + if (existing_entry == nullptr) { + ctx->file_list.append(file); + } else { + file_id = existing_entry->value; + } + return file_id; +} + +static uint32_t anal_dump_get_node_id(AnalDumpCtx *ctx, AstNode *node) { + uint32_t node_id = ctx->node_list.length; + auto existing_entry = ctx->node_map.put_unique(node, node_id); + if (existing_entry == nullptr) { + ctx->node_list.append(node); + } else { + node_id = existing_entry->value; + } + return node_id; +} + +static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { + uint32_t fn_id = ctx->fn_list.length; + auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); + if (existing_entry == nullptr) { + ctx->fn_list.append(fn); + + // poke the fn + (void)anal_dump_get_type_id(ctx, fn->type_entry); + (void)anal_dump_get_node_id(ctx, fn->proto_node); + } else { + fn_id = existing_entry->value; + } + return fn_id; +} + +static uint32_t anal_dump_get_err_id(AnalDumpCtx *ctx, ErrorTableEntry *err) { + uint32_t err_id = ctx->err_list.length; + auto existing_entry = ctx->err_map.put_unique(err, err_id); + if (existing_entry == nullptr) { + ctx->err_list.append(err); + } else { + err_id = existing_entry->value; + } + return err_id; +} + +static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = ctx->decl_list.length; + auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); + if (existing_entry == nullptr) { + ctx->decl_list.append(tld); + + if (tld->import != nullptr) { + (void)anal_dump_get_type_id(ctx, tld->import); + } + + // poke the types + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + (void)anal_dump_get_type_id(ctx, var->var_type); + + if (var->const_value != nullptr) { + anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + (void)anal_dump_get_type_id(ctx, fn->type_entry); + } + break; + } + default: + break; + } + + } else { + decl_id = existing_entry->value; + } + return decl_id; +} + +static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = anal_dump_get_type_id(ctx, ty); + jw_int(&ctx->jw, type_id); +} + +static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { + uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); + jw_int(&ctx->jw, pkg_id); +} + +static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = anal_dump_get_file_id(ctx, file); + jw_int(&ctx->jw, file_id); +} + +static void anal_dump_node_ref(AnalDumpCtx *ctx, AstNode *node) { + uint32_t node_id = anal_dump_get_node_id(ctx, node); + jw_int(&ctx->jw, node_id); +} + +static void anal_dump_fn_ref(AnalDumpCtx *ctx, ZigFn *fn) { + uint32_t fn_id = anal_dump_get_fn_id(ctx, fn); + jw_int(&ctx->jw, fn_id); +} + +static void anal_dump_err_ref(AnalDumpCtx *ctx, ErrorTableEntry *err) { + uint32_t err_id = anal_dump_get_err_id(ctx, err); + jw_int(&ctx->jw, err_id); +} + +static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); + jw_int(&ctx->jw, decl_id); +} + +static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { + JsonWriter *jw = &ctx->jw; + + Buf full_path_buf = BUF_INIT; + os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); + Buf *resolve_paths[] = { &full_path_buf, }; + Buf *resolved_path = buf_alloc(); + *resolved_path = os_path_resolve(resolve_paths, 1); + + auto import_entry = ctx->g->import_table.maybe_get(resolved_path); + if (!import_entry) { + return; + } + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&pkg->pkg_path)); + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, resolved_path); + + jw_object_field(jw, "main"); + anal_dump_type_ref(ctx, import_entry->value); + + jw_object_field(jw, "table"); + jw_begin_object(jw); + auto it = pkg->package_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + ZigPackage *child_pkg = entry->value; + if (child_pkg != nullptr) { + jw_object_field(jw, buf_ptr(entry->key)); + anal_dump_pkg_ref(ctx, child_pkg); + } + } + jw_end_object(jw); + + jw_end_object(jw); +} + +static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { + JsonWriter *jw = &ctx->jw; + + bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; + if (make_obj) { + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "import"); + anal_dump_type_ref(ctx, tld->import); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, tld->source_node); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(tld->name)); + } + + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + jw_object_field(jw, "kind"); + if (var->src_is_const) { + jw_string(jw, "const"); + } else { + jw_string(jw, "var"); + } + + if (var->is_thread_local) { + jw_object_field(jw, "threadlocal"); + jw_bool(jw, true); + } + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, var->var_type); + + if (var->const_value != nullptr) { + jw_object_field(jw, "value"); + anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + jw_object_field(jw, "kind"); + jw_string(jw, "const"); + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, fn->type_entry); + + jw_object_field(jw, "value"); + anal_dump_fn_ref(ctx, fn); + } + break; + } + default: + break; + } + + if (make_obj) { + jw_end_object(jw); + } +} + +static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { + JsonWriter *jw = &ctx->jw; + jw_string(jw, buf_ptr(file)); +} + +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { + Error err; + + if (value->type != ty) { + jw_null(&ctx->jw); + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + jw_string(&ctx->jw, "undefined"); + return; + } + if (value->special == ConstValSpecialRuntime) { + jw_null(&ctx->jw); + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + anal_dump_type_ref(ctx, val_ty); + return; + } + case ZigTypeIdFn: { + if (value->data.x_ptr.special == ConstPtrSpecialFunction) { + ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; + anal_dump_fn_ref(ctx, val_fn); + } else { + jw_null(&ctx->jw); + } + return; + } + default: + jw_null(&ctx->jw); + return; + } + zig_unreachable(); +} + +static void anal_dump_pointer_attrs(AnalDumpCtx *ctx, ZigType *ty) { + JsonWriter *jw = &ctx->jw; + if (ty->data.pointer.explicit_alignment != 0) { + jw_object_field(jw, "align"); + jw_int(jw, ty->data.pointer.explicit_alignment); + } + if (ty->data.pointer.is_const) { + jw_object_field(jw, "const"); + jw_bool(jw, true); + } + if (ty->data.pointer.is_volatile) { + jw_object_field(jw, "volatile"); + jw_bool(jw, true); + } + if (ty->data.pointer.allow_zero) { + jw_object_field(jw, "allowZero"); + jw_bool(jw, true); + } + if (ty->data.pointer.host_int_bytes != 0) { + jw_object_field(jw, "hostIntBytes"); + jw_int(jw, ty->data.pointer.host_int_bytes); + + jw_object_field(jw, "bitOffsetInHost"); + jw_int(jw, ty->data.pointer.bit_offset_in_host); + } + + jw_object_field(jw, "elem"); + anal_dump_type_ref(ctx, ty->data.pointer.child_type); +} + +static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { + JsonWriter *jw = &ctx->jw; + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "kind"); + jw_int(jw, type_id_index(ty)); + + switch (ty->id) { + case ZigTypeIdMetaType: + case ZigTypeIdBool: + case ZigTypeIdEnumLiteral: + break; + case ZigTypeIdStruct: { + if (ty->data.structure.special == StructSpecialSlice) { + jw_object_field(jw, "len"); + jw_int(jw, 2); + anal_dump_pointer_attrs(ctx, ty->data.structure.fields[slice_ptr_index]->type_entry); + break; + } + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.structure.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.structure.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.structure.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.structure.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + jw_array_elem(jw); + anal_dump_type_ref(ctx, ty->data.structure.fields[i]->type_entry); + } + jw_end_array(jw); + } + + if (ty->data.structure.root_struct != nullptr) { + Buf *path_buf = ty->data.structure.root_struct->path; + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, path_buf); + } + break; + } + case ZigTypeIdUnion: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.unionation.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.unionation.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.unionation.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.unionation.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + jw_array_elem(jw); + anal_dump_type_ref(ctx, ty->data.unionation.fields[i].type_entry); + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdEnum: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.enumeration.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.enumeration.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.enumeration.src_field_count; i += 1) { + jw_array_elem(jw); + jw_bigint(jw, &ty->data.enumeration.fields[i].value); + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdFloat: { + jw_object_field(jw, "bits"); + jw_int(jw, ty->data.floating.bit_count); + break; + } + case ZigTypeIdInt: { + if (ty->data.integral.is_signed) { + jw_object_field(jw, "i"); + } else { + jw_object_field(jw, "u"); + } + jw_int(jw, ty->data.integral.bit_count); + break; + } + case ZigTypeIdFn: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "generic"); + jw_bool(jw, ty->data.fn.is_generic); + + if (ty->data.fn.fn_type_id.return_type != nullptr) { + jw_object_field(jw, "ret"); + anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.return_type); + } + + if (ty->data.fn.fn_type_id.param_count != 0) { + jw_object_field(jw, "args"); + jw_begin_array(jw); + for (size_t i = 0; i < ty->data.fn.fn_type_id.param_count; i += 1) { + jw_array_elem(jw); + if (ty->data.fn.fn_type_id.param_info[i].type != nullptr) { + anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.param_info[i].type); + } else { + jw_null(jw); + } + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdOptional: { + jw_object_field(jw, "child"); + anal_dump_type_ref(ctx, ty->data.maybe.child_type); + break; + } + case ZigTypeIdPointer: { + switch (ty->data.pointer.ptr_len) { + case PtrLenSingle: + break; + case PtrLenUnknown: + jw_object_field(jw, "len"); + jw_int(jw, 1); + break; + case PtrLenC: + jw_object_field(jw, "len"); + jw_int(jw, 3); + break; + } + anal_dump_pointer_attrs(ctx, ty); + break; + } + case ZigTypeIdErrorSet: { + if (type_is_global_error_set(ty)) { + break; + } + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + if (ty->data.error_set.infer_fn != nullptr) { + jw_object_field(jw, "fn"); + anal_dump_fn_ref(ctx, ty->data.error_set.infer_fn); + } + jw_object_field(jw, "errors"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ty->data.error_set.err_count; i += 1) { + jw_array_elem(jw); + ErrorTableEntry *err = ty->data.error_set.errors[i]; + anal_dump_err_ref(ctx, err); + } + jw_end_array(jw); + break; + } + case ZigTypeIdErrorUnion: { + jw_object_field(jw, "err"); + anal_dump_type_ref(ctx, ty->data.error_union.err_set_type); + + jw_object_field(jw, "payload"); + anal_dump_type_ref(ctx, ty->data.error_union.payload_type); + + break; + } + case ZigTypeIdArray: { + jw_object_field(jw, "len"); + jw_int(jw, ty->data.array.len); + + jw_object_field(jw, "elem"); + anal_dump_type_ref(ctx, ty->data.array.child_type); + break; + } + default: + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + break; + } + jw_end_object(jw); +} + +static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, node->owner->data.structure.root_struct->path); + + jw_object_field(jw, "line"); + jw_int(jw, node->line); + + jw_object_field(jw, "col"); + jw_int(jw, node->column); + + const Buf *doc_comments_buf = nullptr; + const Buf *name_buf = nullptr; + const ZigList *field_nodes = nullptr; + bool is_var_args = false; + bool is_noalias = false; + bool is_comptime = false; + + switch (node->type) { + case NodeTypeParamDecl: + doc_comments_buf = &node->data.param_decl.doc_comments; + name_buf = node->data.param_decl.name; + is_var_args = node->data.param_decl.is_var_args; + is_noalias = node->data.param_decl.is_noalias; + is_comptime = node->data.param_decl.is_comptime; + break; + case NodeTypeFnProto: + doc_comments_buf = &node->data.fn_proto.doc_comments; + field_nodes = &node->data.fn_proto.params; + is_var_args = node->data.fn_proto.is_var_args; + break; + case NodeTypeVariableDeclaration: + doc_comments_buf = &node->data.variable_declaration.doc_comments; + break; + case NodeTypeErrorSetField: + doc_comments_buf = &node->data.err_set_field.doc_comments; + break; + case NodeTypeStructField: + doc_comments_buf = &node->data.struct_field.doc_comments; + name_buf = node->data.struct_field.name; + break; + case NodeTypeContainerDecl: + field_nodes = &node->data.container_decl.fields; + doc_comments_buf = &node->data.container_decl.doc_comments; + break; + default: + break; + } + + if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { + jw_object_field(jw, "docs"); + jw_string(jw, buf_ptr(doc_comments_buf)); + } + + if (name_buf != nullptr) { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(name_buf)); + } + + if (field_nodes != nullptr) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + for (size_t i = 0; i < field_nodes->length; i += 1) { + jw_array_elem(jw); + anal_dump_node_ref(ctx, field_nodes->at(i)); + } + jw_end_array(jw); + } + + if (is_var_args) { + jw_object_field(jw, "varArgs"); + jw_bool(jw, true); + } + + if (is_comptime) { + jw_object_field(jw, "comptime"); + jw_bool(jw, true); + } + + if (is_noalias) { + jw_object_field(jw, "noalias"); + jw_bool(jw, true); + } + + jw_end_object(jw); +} + +static void anal_dump_err(AnalDumpCtx *ctx, const ErrorTableEntry *err) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, err->decl_node); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&err->name)); + + jw_end_object(jw); +} + +static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, fn->proto_node); + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, fn->type_entry); + + jw_end_object(jw); +} + +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { + Error err; + AnalDumpCtx ctx = {}; + ctx.g = g; + JsonWriter *jw = &ctx.jw; + jw_init(jw, f, one_indent, nl); + ctx.type_map.init(16); + ctx.pkg_map.init(16); + ctx.file_map.init(16); + ctx.decl_map.init(16); + ctx.node_map.init(16); + ctx.fn_map.init(16); + ctx.err_map.init(16); + + jw_begin_object(jw); + + jw_object_field(jw, "typeKinds"); + jw_begin_array(jw); + for (size_t i = 0; i < type_id_len(); i += 1) { + jw_array_elem(jw); + jw_string(jw, type_id_name(type_id_at_index(i))); + } + jw_end_array(jw); + + jw_object_field(jw, "params"); + jw_begin_object(jw); + { + jw_object_field(jw, "zigId"); + + Buf *compiler_id; + if ((err = get_compiler_id(&compiler_id))) { + fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); + exit(1); + } + jw_string(jw, buf_ptr(compiler_id)); + + jw_object_field(jw, "zigVersion"); + jw_string(jw, ZIG_VERSION_STRING); + + jw_object_field(jw, "builds"); + jw_begin_array(jw); + jw_array_elem(jw); + jw_begin_object(jw); + jw_object_field(jw, "target"); + Buf triple_buf = BUF_INIT; + target_triple_zig(&triple_buf, g->zig_target); + jw_string(jw, buf_ptr(&triple_buf)); + jw_end_object(jw); + jw_end_array(jw); + + jw_object_field(jw, "rootName"); + jw_string(jw, buf_ptr(g->root_out_name)); + } + jw_end_object(jw); + + jw_object_field(jw, "rootPkg"); + anal_dump_pkg_ref(&ctx, g->main_pkg); + + // Poke the functions + for (size_t i = 0; i < g->fn_defs.length; i += 1) { + ZigFn *fn = g->fn_defs.at(i); + (void)anal_dump_get_fn_id(&ctx, fn); + } + + jw_object_field(jw, "calls"); + jw_begin_array(jw); + { + ZigList var_stack = {}; + + auto it = g->memoized_fn_eval_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + var_stack.resize(0); + ZigFn *fn = nullptr; + + Scope *scope = entry->key; + while (scope != nullptr) { + if (scope->id == ScopeIdVarDecl) { + ZigVar *var = reinterpret_cast(scope)->var; + var_stack.append(var); + } else if (scope->id == ScopeIdFnDef) { + fn = reinterpret_cast(scope)->fn_entry; + break; + } + scope = scope->parent; + } + ZigValue *result = entry->value; + + assert(fn != nullptr); + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "fn"); + anal_dump_fn_ref(&ctx, fn); + + jw_object_field(jw, "result"); + { + jw_begin_object(jw); + + jw_object_field(jw, "type"); + anal_dump_type_ref(&ctx, result->type); + + jw_object_field(jw, "value"); + anal_dump_value(&ctx, scope->source_node, result->type, result); + + jw_end_object(jw); + } + + if (var_stack.length != 0) { + jw_object_field(jw, "args"); + jw_begin_array(jw); + + while (var_stack.length != 0) { + ZigVar *var = var_stack.pop(); + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "type"); + anal_dump_type_ref(&ctx, var->var_type); + + jw_object_field(jw, "value"); + anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); + + jw_end_object(jw); + } + jw_end_array(jw); + } + + jw_end_object(jw); + } + + var_stack.deinit(); + } + jw_end_array(jw); + + jw_object_field(jw, "packages"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { + anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); + } + jw_end_array(jw); + + jw_object_field(jw, "types"); + jw_begin_array(jw); + + for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { + ZigType *ty = ctx.type_list.at(i); + anal_dump_type(&ctx, ty); + } + jw_end_array(jw); + + jw_object_field(jw, "decls"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { + Tld *decl = ctx.decl_list.at(i); + anal_dump_decl(&ctx, decl); + } + jw_end_array(jw); + + jw_object_field(jw, "fns"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { + ZigFn *fn = ctx.fn_list.at(i); + jw_array_elem(jw); + anal_dump_fn(&ctx, fn); + } + jw_end_array(jw); + + jw_object_field(jw, "errors"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { + const ErrorTableEntry *err = ctx.err_list.at(i); + jw_array_elem(jw); + anal_dump_err(&ctx, err); + } + jw_end_array(jw); + + jw_object_field(jw, "astNodes"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.node_list.length; i += 1) { + const AstNode *node = ctx.node_list.at(i); + jw_array_elem(jw); + anal_dump_node(&ctx, node); + } + jw_end_array(jw); + + jw_object_field(jw, "files"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { + Buf *file = ctx.file_list.at(i); + jw_array_elem(jw); + anal_dump_file(&ctx, file); + } + jw_end_array(jw); + + jw_end_object(jw); +} diff --git a/src/stack_report.hpp b/src/dump_analysis.hpp similarity index 61% rename from src/stack_report.hpp rename to src/dump_analysis.hpp index 8a644466f..6d1c644ea 100644 --- a/src/stack_report.hpp +++ b/src/dump_analysis.hpp @@ -5,12 +5,13 @@ * See http://opensource.org/licenses/MIT */ -#ifndef ZIG_STACK_REPORT_HPP -#define ZIG_STACK_REPORT_HPP +#ifndef ZIG_DUMP_ANALYSIS_HPP +#define ZIG_DUMP_ANALYSIS_HPP #include "all_types.hpp" #include void zig_print_stack_report(CodeGen *g, FILE *f); +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl); #endif diff --git a/src/errmsg.cpp b/src/errmsg.cpp index edbfd858a..9425b110c 100644 --- a/src/errmsg.cpp +++ b/src/errmsg.cpp @@ -16,51 +16,70 @@ enum ErrType { }; static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) { - const char *path = buf_ptr(err->path); - size_t line = err->line_start + 1; - size_t col = err->column_start + 1; - const char *text = buf_ptr(err->msg); - bool is_tty = os_stderr_tty(); - if (color == ErrColorOn || (color == ErrColorAuto && is_tty)) { - if (err_type == ErrTypeError) { - os_stderr_set_color(TermColorBold); - fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); - os_stderr_set_color(TermColorRed); - fprintf(stderr, "error:"); - os_stderr_set_color(TermColorBold); - fprintf(stderr, " %s", text); - os_stderr_set_color(TermColorReset); - fprintf(stderr, "\n"); - } else if (err_type == ErrTypeNote) { - os_stderr_set_color(TermColorBold); - fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); - os_stderr_set_color(TermColorCyan); - fprintf(stderr, "note:"); - os_stderr_set_color(TermColorBold); - fprintf(stderr, " %s", text); - os_stderr_set_color(TermColorReset); - fprintf(stderr, "\n"); - } else { - zig_unreachable(); + bool use_colors = color == ErrColorOn || (color == ErrColorAuto && is_tty); + + // Show the error location, if available + if (err->path != nullptr) { + const char *path = buf_ptr(err->path); + Slice pathslice{path, strlen(path)}; + + // Cache cwd + static Buf *cwdbuf{nullptr}; + static Slice cwd; + + if (cwdbuf == nullptr) { + cwdbuf = buf_alloc(); + Error err = os_get_cwd(cwdbuf); + if (err != ErrorNone) + zig_panic("get cwd failed"); + buf_append_char(cwdbuf, ZIG_OS_SEP_CHAR); + cwd.ptr = buf_ptr(cwdbuf); + cwd.len = strlen(cwd.ptr); } + const size_t line = err->line_start + 1; + const size_t col = err->column_start + 1; + if (use_colors) os_stderr_set_color(TermColorBold); + + // Strip cwd from path + if (memStartsWith(pathslice, cwd)) + fprintf(stderr, ".%c%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", ZIG_OS_SEP_CHAR, path+cwd.len, line, col); + else + fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); + } + + // Write out the error type + switch (err_type) { + case ErrTypeError: + if (use_colors) os_stderr_set_color(TermColorRed); + fprintf(stderr, "error: "); + break; + case ErrTypeNote: + if (use_colors) os_stderr_set_color(TermColorCyan); + fprintf(stderr, "note: "); + break; + default: + zig_unreachable(); + } + + // Write out the error message + if (use_colors) os_stderr_set_color(TermColorBold); + fputs(buf_ptr(err->msg), stderr); + if (use_colors) os_stderr_set_color(TermColorReset); + fputc('\n', stderr); + + if (buf_len(&err->line_buf) != 0){ + // Show the referenced line fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); for (size_t i = 0; i < err->column_start; i += 1) { fprintf(stderr, " "); } - os_stderr_set_color(TermColorGreen); + // Draw the caret + if (use_colors) os_stderr_set_color(TermColorGreen); fprintf(stderr, "^"); - os_stderr_set_color(TermColorReset); + if (use_colors) os_stderr_set_color(TermColorReset); fprintf(stderr, "\n"); - } else { - if (err_type == ErrTypeError) { - fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": error: %s\n", path, line, col, text); - } else if (err_type == ErrTypeNote) { - fprintf(stderr, " %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": note: %s\n", path, line, col, text); - } else { - zig_unreachable(); - } } for (size_t i = 0; i < err->notes.length; i += 1) { @@ -86,6 +105,12 @@ ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size err_msg->column_start = column; err_msg->msg = msg; + if (source == nullptr) { + // Must initialize the buffer anyway + buf_init_from_str(&err_msg->line_buf, ""); + return err_msg; + } + size_t line_start_offset = offset; for (;;) { if (line_start_offset == 0) { diff --git a/src/error.cpp b/src/error.cpp index 86df76ed4..9fc0383b1 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -57,6 +57,7 @@ const char *err_str(Error err) { case ErrorNoCCompilerInstalled: return "no C compiler installed"; case ErrorNotLazy: return "not lazy"; case ErrorIsAsync: return "is async"; + case ErrorImportOutsidePkgPath: return "import of file outside package path"; } return "(invalid error)"; } diff --git a/src/glibc.cpp b/src/glibc.cpp index 50ccf20ef..08faf6fb6 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -169,11 +169,11 @@ Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbo } Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose) + Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node) { Error err; - Buf *cache_dir = get_stage1_cache_path(); + Buf *cache_dir = get_global_cache_dir(); CacheHash *cache_hash = allocate(1); Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); cache_init(cache_hash, manifest_dir); @@ -332,8 +332,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con return err; } - CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str(lib->name)); + CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node); codegen_set_lib_version(child_gen, lib->sover, 0, 0); child_gen->is_dynamic = true; child_gen->is_dummy_so = true; diff --git a/src/glibc.hpp b/src/glibc.hpp index 50796197d..42c209937 100644 --- a/src/glibc.hpp +++ b/src/glibc.hpp @@ -41,7 +41,7 @@ struct ZigGLibCAbi { Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose); Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose); + Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node); // returns ErrorUnknownABI when glibc is not the native libc Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver); diff --git a/src/hash_map.hpp b/src/hash_map.hpp index 51ec352ed..e819ea1c1 100644 --- a/src/hash_map.hpp +++ b/src/hash_map.hpp @@ -23,10 +23,10 @@ public: } struct Entry { - bool used; - int distance_from_start_index; K key; V value; + bool used; + int distance_from_start_index; }; void clear() { @@ -187,10 +187,10 @@ private: if (distance_from_start_index > _max_distance_from_start_index) _max_distance_from_start_index = distance_from_start_index; *entry = { - true, - distance_from_start_index, key, value, + true, + distance_from_start_index, }; key = tmp.key; value = tmp.value; @@ -208,10 +208,10 @@ private: if (distance_from_start_index > _max_distance_from_start_index) _max_distance_from_start_index = distance_from_start_index; *entry = { - true, - distance_from_start_index, key, value, + true, + distance_from_start_index, }; return; } diff --git a/src/install_files.h b/src/install_files.h index 5e89c07b5..a76ff42a9 100644 --- a/src/install_files.h +++ b/src/install_files.h @@ -148,6 +148,7 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/env/clearenv.c", "musl/src/env/getenv.c", "musl/src/env/putenv.c", +"musl/src/env/secure_getenv.c", "musl/src/env/setenv.c", "musl/src/env/unsetenv.c", "musl/src/errno/__errno_location.c", @@ -311,6 +312,7 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/linux/chroot.c", "musl/src/linux/clock_adjtime.c", "musl/src/linux/clone.c", +"musl/src/linux/copy_file_range.c", "musl/src/linux/epoll.c", "musl/src/linux/eventfd.c", "musl/src/linux/fallocate.c", @@ -1098,8 +1100,10 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/process/fork.c", "musl/src/process/i386/vfork.s", "musl/src/process/posix_spawn.c", +"musl/src/process/posix_spawn_file_actions_addchdir.c", "musl/src/process/posix_spawn_file_actions_addclose.c", "musl/src/process/posix_spawn_file_actions_adddup2.c", +"musl/src/process/posix_spawn_file_actions_addfchdir.c", "musl/src/process/posix_spawn_file_actions_addopen.c", "musl/src/process/posix_spawn_file_actions_destroy.c", "musl/src/process/posix_spawn_file_actions_init.c", @@ -1251,7 +1255,9 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/signal/sigtimedwait.c", "musl/src/signal/sigwait.c", "musl/src/signal/sigwaitinfo.c", +"musl/src/signal/x32/getitimer.c", "musl/src/signal/x32/restore.s", +"musl/src/signal/x32/setitimer.c", "musl/src/signal/x32/sigsetjmp.s", "musl/src/signal/x86_64/restore.s", "musl/src/signal/x86_64/sigsetjmp.s", @@ -1707,7 +1713,6 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/thread/x32/__unmapself.s", "musl/src/thread/x32/clone.s", "musl/src/thread/x32/syscall_cp.s", -"musl/src/thread/x32/syscall_cp_fixup.c", "musl/src/thread/x86_64/__set_thread_area.s", "musl/src/thread/x86_64/__unmapself.s", "musl/src/thread/x86_64/clone.s", @@ -1791,6 +1796,7 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/unistd/lseek.c", "musl/src/unistd/mips/pipe.s", "musl/src/unistd/mips64/pipe.s", +"musl/src/unistd/mipsn32/lseek.c", "musl/src/unistd/mipsn32/pipe.s", "musl/src/unistd/nice.c", "musl/src/unistd/pause.c", @@ -1835,5 +1841,6 @@ static const char *ZIG_MUSL_SRC_FILES[] = { "musl/src/unistd/usleep.c", "musl/src/unistd/write.c", "musl/src/unistd/writev.c", +"musl/src/unistd/x32/lseek.c", }; #endif diff --git a/src/ir.cpp b/src/ir.cpp index 14adf6fbd..d871aa27a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13,13 +13,12 @@ #include "os.hpp" #include "range_set.hpp" #include "softfloat.hpp" -#include "translate_c.hpp" #include "util.hpp" #include struct IrExecContext { - ZigList mem_slot_list; + ZigList mem_slot_list; }; struct IrBuilder { @@ -41,6 +40,11 @@ struct IrAnalyze { ZigList src_implicit_return_type_list; ZigList resume_stack; IrBasicBlock *const_predecessor_bb; + size_t ref_count; + size_t break_debug_id; // for debugging purposes + + // For the purpose of using in a debugger + void dump(); }; enum ConstCastResultId { @@ -66,6 +70,12 @@ enum ConstCastResultId { ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, ConstCastResultIdBadAllowsZero, + ConstCastResultIdArrayChild, + ConstCastResultIdSentinelArrays, + ConstCastResultIdPtrLens, + ConstCastResultIdCV, + ConstCastResultIdPtrSentinel, + ConstCastResultIdIntShorten, }; struct ConstCastOnly; @@ -87,7 +97,12 @@ struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; struct ConstCastTypeMismatch; +struct ConstCastArrayMismatch; struct ConstCastBadAllowsZero; +struct ConstCastBadNullTermArrays; +struct ConstCastBadCV; +struct ConstCastPtrSentinel; +struct ConstCastIntShorten; struct ConstCastOnly { ConstCastResultId id; @@ -99,11 +114,16 @@ struct ConstCastOnly { ConstCastErrUnionPayloadMismatch *error_union_payload; ConstCastErrUnionErrSetMismatch *error_union_error_set; ConstCastTypeMismatch *type_mismatch; + ConstCastArrayMismatch *array_mismatch; ConstCastOnly *return_type; ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; ConstCastBadAllowsZero *bad_allows_zero; + ConstCastBadNullTermArrays *sentinel_arrays; + ConstCastBadCV *bad_cv; + ConstCastPtrSentinel *bad_ptr_sentinel; + ConstCastIntShorten *int_shorten; } data; }; @@ -130,6 +150,12 @@ struct ConstCastSliceMismatch { ZigType *actual_child; }; +struct ConstCastArrayMismatch { + ConstCastOnly child; + ZigType *wanted_child; + ZigType *actual_child; +}; + struct ConstCastErrUnionErrSetMismatch { ConstCastOnly child; ZigType *wanted_err_set; @@ -151,11 +177,41 @@ struct ConstCastBadAllowsZero { ZigType *actual_type; }; +struct ConstCastBadNullTermArrays { + ConstCastOnly child; + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastBadCV { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastPtrSentinel { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastIntShorten { + ZigType *wanted_type; + ZigType *actual_type; +}; + +// for debugging purposes +struct DbgIrBreakPoint { + const char *src_file; + uint32_t line; +}; +DbgIrBreakPoint dbg_ir_breakpoints_buf[20]; +size_t dbg_ir_breakpoints_count = 0; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, ResultLoc *result_loc); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type); +static IrInstruction *ir_implicit_cast2(IrAnalyze *ira, IrInstruction *value_source_instr, + IrInstruction *value, ZigType *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr, ResultLoc *result_loc); static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); @@ -168,21 +224,21 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); -static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val); -static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); +static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val); +static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val); static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ConstExprValue *out_val, ConstExprValue *ptr_val); + ZigValue *out_val, ZigValue *ptr_val); static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on); -static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); -static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *ptr_type); static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *dest_type); static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime); + ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, + bool non_null_comptime, bool allow_discard); static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime, bool allow_discard); @@ -198,20 +254,419 @@ static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNo IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node, LVal lval, ResultLoc *parent_result_loc); static void ir_reset_result(ResultLoc *result_loc); +static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, + Scope *scope, AstNode *source_node, Buf *out_bare_name); +static ResultLocCast *ir_build_cast_result_loc(IrBuilder *irb, IrInstruction *dest_type, + ResultLoc *parent_result_loc); +static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr, + TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing); +static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); +static ResultLoc *no_result_loc(void); +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value); -static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { +static void destroy_instruction(IrInstruction *inst) { +#ifdef ZIG_ENABLE_MEM_PROFILE + const char *name = ir_instruction_type_str(inst->id); +#else + const char *name = nullptr; +#endif + switch (inst->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdReturn: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdConst: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBinOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMergeErrSets: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclVarSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallSrcArgs: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallExtra: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCondBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPhi: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdContainerInitList: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdContainerInitFields: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnreachable: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdElemPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVarPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdReturnPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdLoadPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdLoadPtrGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdStorePtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorStoreElem: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdStructFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetCold: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetRuntimeSafety: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetFloatMode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArrayType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAnyFrameType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAsmSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAsmGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSizeOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestNonNull: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOptionalUnwrapPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPopCount: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdClz: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCtz: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBswap: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitReverse: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchVar: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchElseVar: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchTarget: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionTag: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdImport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdRef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdRefGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCompileErr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCompileLog: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCImport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCInclude: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCDefine: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCUndef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEmbedFile: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCmpxchgSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCmpxchgGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFence: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTruncate: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrSetCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFromBytes: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdToBytes: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToFloat: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBoolToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdShuffleVector: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSplatSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSplatGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBoolNot: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemset: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemcpy: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberCount: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBreakpoint: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdReturnAddress: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameAddress: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameHandle: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameSizeSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameSizeGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAlignOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOverflowOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestErrSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestErrGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnwrapErrCode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnwrapErrPayload: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOptionalWrap: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrWrapCode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrWrapPayload: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFnProto: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestComptime: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrCastSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrCastGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitCastSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitCastGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdWidenOrShorten: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToEnum: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToErr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckSwitchProngs: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckStatementIsVoid: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTagName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclRef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPanic: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFieldParentPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdByteOffsetOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitOffsetOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeInfo: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdHasField: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeId: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetEvalBranchQuota: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAlignCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdImplicitCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResolveResult: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResetResult: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOpaqueType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetAlignStack: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArgType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTagType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdExport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrorReturnTrace: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrorUnion: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicRmw: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSaveErrRetAddr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAddImplicitReturnType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMulAdd: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicLoad: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicStore: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEnumToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckRuntimeScope: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclVarGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArrayToVector: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorToArray: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrOfArrayToSlice: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAssertZero: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAssertNonNull: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResizeSlice: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdHasDecl: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUndeclaredIdent: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAllocaSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAllocaGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEndExpr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionInitNamedField: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSuspendBegin: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSuspendFinish: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResume: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAwaitSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAwaitGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSpillBegin: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSpillEnd: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorExtractElem: + return destroy(reinterpret_cast(inst), name); + } + zig_unreachable(); +} + +static void ira_ref(IrAnalyze *ira) { + ira->ref_count += 1; +} +static void ira_deref(IrAnalyze *ira) { + if (ira->ref_count > 1) { + ira->ref_count -= 1; + return; + } + assert(ira->ref_count != 0); + + for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { + IrBasicBlock *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i]; + for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) { + IrInstruction *pass1_inst = pass1_bb->instruction_list.items[inst_i]; + destroy_instruction(pass1_inst); + } + destroy(pass1_bb, "IrBasicBlock"); + } + ira->old_irb.exec->basic_block_list.deinit(); + ira->old_irb.exec->tld_list.deinit(); + // cannot destroy here because of var->owner_exec + //destroy(ira->old_irb.exec, "IrExecutablePass1"); + ira->src_implicit_return_type_list.deinit(); + ira->resume_stack.deinit(); + ira->exec_context.mem_slot_list.deinit(); + destroy(ira, "IrAnalyze"); +} + +static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); - ConstExprValue *result; + ZigValue *result; + + InferredStructField *isf = const_val->type->data.pointer.inferred_struct_field; + if (isf != nullptr) { + TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); + assert(field != nullptr); + if (field->is_comptime) { + assert(field->init_val != nullptr); + return field->init_val; + } + assert(const_val->data.x_ptr.special == ConstPtrSpecialRef); + ZigValue *struct_val = const_val->data.x_ptr.data.ref.pointee; + return struct_val->data.x_struct.fields[field->src_index]; + } switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) { case OnePossibleValueInvalid: - zig_unreachable(); + return nullptr; case OnePossibleValueYes: - result = create_const_vals(1); - result->type = const_val->type->data.pointer.child_type; - result->special = ConstValSpecialStatic; - return result; + return get_the_one_possible_value(g, const_val->type->data.pointer.child_type); case OnePossibleValueNo: break; } @@ -223,15 +678,19 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c result = const_val->data.x_ptr.data.ref.pointee; break; case ConstPtrSpecialBaseArray: { - ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; - expand_undef_array(g, array_val); - result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; + ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) { + result = array_val->type->data.array.sentinel; + } else { + expand_undef_array(g, array_val); + result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; + } break; } case ConstPtrSpecialBaseStruct: { - ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; + ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; expand_undef_struct(g, struct_val); - result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; + result = struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; break; } case ConstPtrSpecialBaseErrorUnionCode: @@ -257,36 +716,35 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c return result; } -static bool is_opt_err_set(ZigType *ty) { - return ty->id == ZigTypeIdErrorSet || - (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +static bool is_tuple(ZigType *type) { + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple; } static bool is_slice(ZigType *type) { - return type->id == ZigTypeIdStruct && type->data.structure.is_slice; + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; } static bool slice_is_const(ZigType *type) { assert(is_slice(type)); - return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; + return type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; } -// This function returns true when you can change the type of a ConstExprValue and the +// This function returns true when you can change the type of a ZigValue and the // value remains meaningful. -static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { - if (a == b) +static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { + if (expected == actual) return true; - if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) + if (get_codegen_ptr_type(expected) != nullptr && get_codegen_ptr_type(actual) != nullptr) return true; - if (is_opt_err_set(a) && is_opt_err_set(b)) + if (is_opt_err_set(expected) && is_opt_err_set(actual)) return true; - if (a->id != b->id) + if (expected->id != actual->id) return false; - switch (a->id) { + switch (expected->id) { case ZigTypeIdInvalid: case ZigTypeIdUnreachable: zig_unreachable(); @@ -303,23 +761,26 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { case ZigTypeIdErrorSet: case ZigTypeIdOpaque: case ZigTypeIdAnyFrame: + case ZigTypeIdFn: return true; case ZigTypeIdFloat: - return a->data.floating.bit_count == b->data.floating.bit_count; + return expected->data.floating.bit_count == actual->data.floating.bit_count; case ZigTypeIdInt: - return a->data.integral.is_signed == b->data.integral.is_signed; + return expected->data.integral.is_signed == actual->data.integral.is_signed; case ZigTypeIdStruct: - return is_slice(a) && is_slice(b); - case ZigTypeIdArray: + return is_slice(expected) && is_slice(actual); case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdEnum: case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdArgTuple: case ZigTypeIdVector: case ZigTypeIdFnFrame: return false; + case ZigTypeIdArray: + return expected->data.array.len == actual->data.array.len && + expected->data.array.child_type == actual->data.array.child_type && + (expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr && + const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel))); } zig_unreachable(); } @@ -366,16 +827,16 @@ static Buf *exec_c_import_buf(IrExecutable *exec) { return exec->c_import_buf; } -static bool value_is_comptime(ConstExprValue *const_val) { +static bool value_is_comptime(ZigValue *const_val) { return const_val->special != ConstValSpecialRuntime; } static bool instr_is_comptime(IrInstruction *instruction) { - return value_is_comptime(&instruction->value); + return value_is_comptime(instruction->value); } static bool instr_is_unreachable(IrInstruction *instruction) { - return instruction->value.type && instruction->value.type->id == ZigTypeIdUnreachable; + return instruction->value->type && instruction->value->type->id == ZigTypeIdUnreachable; } static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) { @@ -399,7 +860,7 @@ static void ir_ref_var(ZigVar *var) { } ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { - ConstExprValue *result = ir_eval_const_value(ira->codegen, scope, node, ira->codegen->builtin_types.entry_type, + ZigValue *result = ir_eval_const_value(ira->codegen, scope, node, ira->codegen->builtin_types.entry_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, node, nullptr, ira->new_irb.exec, nullptr, UndefBad); @@ -411,7 +872,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { } static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { - IrBasicBlock *result = allocate(1); + IrBasicBlock *result = allocate(1, "IrBasicBlock"); result->scope = scope; result->name_hint = name_hint; result->debug_id = exec_next_debug_id(irb->exec); @@ -469,6 +930,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrSets *) { + return IrInstructionIdMergeErrSets; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -485,6 +950,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorStoreElem *) { + return IrInstructionIdVectorStoreElem; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } @@ -513,6 +982,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCallSrc *) { return IrInstructionIdCallSrc; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCallSrcArgs *) { + return IrInstructionIdCallSrcArgs; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCallExtra *) { + return IrInstructionIdCallExtra; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCallGen *) { return IrInstructionIdCallGen; } @@ -573,12 +1050,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) { return IrInstructionIdSliceType; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) { - return IrInstructionIdGlobalAsm; +static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmSrc *) { + return IrInstructionIdAsmSrc; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) { - return IrInstructionIdAsm; +static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmGen *) { + return IrInstructionIdAsmGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) { @@ -993,6 +1470,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { return IrInstructionIdAtomicLoad; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicStore *) { + return IrInstructionIdAtomicStore; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) { return IrInstructionIdSaveErrRetAddr; } @@ -1077,15 +1558,40 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) { return IrInstructionIdSpillEnd; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorExtractElem *) { + return IrInstructionIdVectorExtractElem; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = allocate(1); + const char *name = nullptr; +#ifdef ZIG_ENABLE_MEM_PROFILE + T *dummy = nullptr; + name = ir_instruction_type_str(ir_instruction_id(dummy)); +#endif + T *special_instruction = allocate(1, name); + special_instruction->base.id = ir_instruction_id(special_instruction); + special_instruction->base.scope = scope; + special_instruction->base.source_node = source_node; + special_instruction->base.debug_id = exec_next_debug_id(irb->exec); + special_instruction->base.owner_bb = irb->current_basic_block; + special_instruction->base.value = allocate(1, "ZigValue"); + return special_instruction; +} + +template +static T *ir_create_instruction_noval(IrBuilder *irb, Scope *scope, AstNode *source_node) { + const char *name = nullptr; +#ifdef ZIG_ENABLE_MEM_PROFILE + T *dummy = nullptr; + name = ir_instruction_type_str(ir_instruction_id(dummy)); +#endif + T *special_instruction = allocate(1, name); special_instruction->base.id = ir_instruction_id(special_instruction); special_instruction->base.scope = scope; special_instruction->base.source_node = source_node; special_instruction->base.debug_id = exec_next_debug_id(irb->exec); special_instruction->base.owner_bb = irb->current_basic_block; - special_instruction->base.value.global_refs = allocate(1); return special_instruction; } @@ -1113,8 +1619,8 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime) { IrInstructionCondBr *cond_br_instruction = ir_build_instruction(irb, scope, source_node); - cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; - cond_br_instruction->base.value.special = ConstValSpecialStatic; + cond_br_instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; + cond_br_instruction->base.value->special = ConstValSpecialStatic; cond_br_instruction->condition = condition; cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; @@ -1132,8 +1638,8 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou IrInstruction *operand) { IrInstructionReturn *return_instruction = ir_build_instruction(irb, scope, source_node); - return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; - return_instruction->base.value.special = ConstValSpecialStatic; + return_instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; + return_instruction->base.value->special = ConstValSpecialStatic; return_instruction->operand = operand; if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block); @@ -1142,55 +1648,55 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { - IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; - const_instruction->base.value.special = ConstValSpecialStatic; + IrInstructionConst *const_instruction = ir_create_instruction_noval(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->base.value = irb->codegen->intern.for_void(); return &const_instruction->base; } static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) { - IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.special = ConstValSpecialUndef; - const_instruction->base.value.type = irb->codegen->builtin_types.entry_undef; + IrInstructionConst *const_instruction = ir_create_instruction_noval(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->base.value = irb->codegen->intern.for_undefined(); return &const_instruction->base; } static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int; - const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); + const_instruction->base.value->type = irb->codegen->builtin_types.entry_num_lit_int; + const_instruction->base.value->special = ConstValSpecialStatic; + bigint_init_unsigned(&const_instruction->base.value->data.x_bigint, value); return &const_instruction->base; } static IrInstruction *ir_build_const_bigint(IrBuilder *irb, Scope *scope, AstNode *source_node, BigInt *bigint) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int; - const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_bigint(&const_instruction->base.value.data.x_bigint, bigint); + const_instruction->base.value->type = irb->codegen->builtin_types.entry_num_lit_int; + const_instruction->base.value->special = ConstValSpecialStatic; + bigint_init_bigint(&const_instruction->base.value->data.x_bigint, bigint); return &const_instruction->base; } static IrInstruction *ir_build_const_bigfloat(IrBuilder *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float; - const_instruction->base.value.special = ConstValSpecialStatic; - bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat); + const_instruction->base.value->type = irb->codegen->builtin_types.entry_num_lit_float; + const_instruction->base.value->special = ConstValSpecialStatic; + bigfloat_init_bigfloat(&const_instruction->base.value->data.x_bigfloat, bigfloat); return &const_instruction->base; } static IrInstruction *ir_build_const_null(IrBuilder *irb, Scope *scope, AstNode *source_node) { - IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_null; - const_instruction->base.value.special = ConstValSpecialStatic; + IrInstructionConst *const_instruction = ir_create_instruction_noval(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->base.value = irb->codegen->intern.for_null(); return &const_instruction->base; } static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize; - const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); + const_instruction->base.value->type = irb->codegen->builtin_types.entry_usize; + const_instruction->base.value->special = ConstValSpecialStatic; + bigint_init_unsigned(&const_instruction->base.value->data.x_bigint, value); return &const_instruction->base; } @@ -1198,9 +1704,9 @@ static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode ZigType *type_entry) { IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_type = type_entry; + const_instruction->base.value->type = irb->codegen->builtin_types.entry_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_type = type_entry; return &const_instruction->base; } @@ -1214,35 +1720,35 @@ static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry) { IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = fn_entry->type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_ptr.data.fn.fn_entry = fn_entry; - const_instruction->base.value.data.x_ptr.mut = ConstPtrMutComptimeConst; - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialFunction; + const_instruction->base.value->type = fn_entry->type_entry; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_ptr.data.fn.fn_entry = fn_entry; + const_instruction->base.value->data.x_ptr.mut = ConstPtrMutComptimeConst; + const_instruction->base.value->data.x_ptr.special = ConstPtrSpecialFunction; return &const_instruction->base; } static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *import) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_type = import; + const_instruction->base.value->type = irb->codegen->builtin_types.entry_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_type = import; return &const_instruction->base; } static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_bool; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_bool = value; + const_instruction->base.value->type = irb->codegen->builtin_types.entry_bool; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_bool = value; return &const_instruction->base; } static IrInstruction *ir_build_const_enum_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_enum_literal; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_enum_literal = name; + const_instruction->base.value->type = irb->codegen->builtin_types.entry_enum_literal; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_enum_literal = name; return &const_instruction->base; } @@ -1250,31 +1756,26 @@ static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstN ZigFn *fn_entry, IrInstruction *first_arg) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = get_bound_fn_type(irb->codegen, fn_entry); - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_bound_fn.fn = fn_entry; - const_instruction->base.value.data.x_bound_fn.first_arg = first_arg; + const_instruction->base.value->type = get_bound_fn_type(irb->codegen, fn_entry); + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_bound_fn.fn = fn_entry; + const_instruction->base.value->data.x_bound_fn.first_arg = first_arg; return &const_instruction->base; } static IrInstruction *ir_create_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - init_const_str_lit(irb->codegen, &const_instruction->base.value, str); + init_const_str_lit(irb->codegen, const_instruction->base.value, str); return &const_instruction->base; } + static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstruction *instruction = ir_create_const_str_lit(irb, scope, source_node, str); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } -static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { - IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - init_const_c_str_lit(irb->codegen, &const_instruction->base.value, str); - return &const_instruction->base; -} - static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { @@ -1290,6 +1791,38 @@ static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *sou return &bin_op_instruction->base; } +static IrInstruction *ir_build_bin_op_gen(IrAnalyze *ira, IrInstruction *source_instr, ZigType *res_type, + IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) +{ + IrInstructionBinOp *bin_op_instruction = ir_build_instruction(&ira->new_irb, + source_instr->scope, source_instr->source_node); + bin_op_instruction->base.value->type = res_type; + bin_op_instruction->op_id = op_id; + bin_op_instruction->op1 = op1; + bin_op_instruction->op2 = op2; + bin_op_instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(op1, ira->new_irb.current_basic_block); + ir_ref_instruction(op2, ira->new_irb.current_basic_block); + + return &bin_op_instruction->base; +} + + +static IrInstruction *ir_build_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *op1, IrInstruction *op2, Buf *type_name) +{ + IrInstructionMergeErrSets *merge_err_sets_instruction = ir_build_instruction(irb, scope, source_node); + merge_err_sets_instruction->op1 = op1; + merge_err_sets_instruction->op2 = op2; + merge_err_sets_instruction->type_name = type_name; + + ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(op2, irb->current_basic_block); + + return &merge_err_sets_instruction->base; +} + static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, ScopeFnDef *crossed_fndef_scope) { @@ -1309,24 +1842,23 @@ static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *so static IrInstruction *ir_build_return_ptr(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstructionReturnPtr *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ty; + instruction->base.value->type = ty; return &instruction->base; } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len, - IrInstruction *init_array_type) + AstNode *init_array_type_source_node) { IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; instruction->ptr_len = ptr_len; - instruction->init_array_type = init_array_type; + instruction->init_array_type_source_node = init_array_type_source_node; ir_ref_instruction(array_ptr, irb->current_basic_block); ir_ref_instruction(elem_index, irb->current_basic_block); - if (init_array_type != nullptr) ir_ref_instruction(init_array_type, irb->current_basic_block); return &instruction->base; } @@ -1399,30 +1931,61 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_call_extra(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *options, IrInstruction *fn_ref, IrInstruction *args, ResultLoc *result_loc) +{ + IrInstructionCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->options = options; + call_instruction->fn_ref = fn_ref; + call_instruction->args = args; + call_instruction->result_loc = result_loc; + + ir_ref_instruction(options, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + ir_ref_instruction(args, irb->current_basic_block); + + return &call_instruction->base; +} + +static IrInstruction *ir_build_call_src_args(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *options, IrInstruction *fn_ref, IrInstruction **args_ptr, size_t args_len, + ResultLoc *result_loc) +{ + IrInstructionCallSrcArgs *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->options = options; + call_instruction->fn_ref = fn_ref; + call_instruction->args_ptr = args_ptr; + call_instruction->args_len = args_len; + call_instruction->result_loc = result_loc; + + ir_ref_instruction(options, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + for (size_t i = 0; i < args_len; i += 1) + ir_ref_instruction(args_ptr[i], irb->current_basic_block); + + return &call_instruction->base; +} + static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, CallModifier modifier, bool is_async_call_builtin, + IrInstruction *ret_ptr, CallModifier modifier, bool is_async_call_builtin, IrInstruction *new_stack, ResultLoc *result_loc) { IrInstructionCallSrc *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; - call_instruction->is_comptime = is_comptime; - call_instruction->fn_inline = fn_inline; call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->modifier = modifier; call_instruction->is_async_call_builtin = is_async_call_builtin; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; + call_instruction->ret_ptr = ret_ptr; if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); - if (modifier == CallModifierAsync && new_stack != nullptr) { - // in this case the arg at the end is the return pointer - ir_ref_instruction(args[arg_count], irb->current_basic_block); - } + if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; @@ -1430,15 +1993,14 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - FnInline fn_inline, CallModifier modifier, IrInstruction *new_stack, bool is_async_call_builtin, + CallModifier modifier, IrInstruction *new_stack, bool is_async_call_builtin, IrInstruction *result_loc, ZigType *return_type) { IrInstructionCallGen *call_instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - call_instruction->base.value.type = return_type; + call_instruction->base.value->type = return_type; call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; - call_instruction->fn_inline = fn_inline; call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->modifier = modifier; @@ -1480,8 +2042,8 @@ static IrInstruction *ir_create_br(IrBuilder *irb, Scope *scope, AstNode *source IrBasicBlock *dest_block, IrInstruction *is_comptime) { IrInstructionBr *br_instruction = ir_create_instruction(irb, scope, source_node); - br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; - br_instruction->base.value.special = ConstValSpecialStatic; + br_instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; + br_instruction->base.value->special = ConstValSpecialStatic; br_instruction->dest_block = dest_block; br_instruction->is_comptime = is_comptime; @@ -1501,9 +2063,11 @@ static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, - IrInstruction *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) + IrInstruction *sentinel, IrInstruction *align_value, + uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); + ptr_type_of_instruction->sentinel = sentinel; ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; @@ -1513,6 +2077,7 @@ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *s ptr_type_of_instruction->host_int_bytes = host_int_bytes; ptr_type_of_instruction->is_allow_zero = is_allow_zero; + if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); @@ -1540,17 +2105,16 @@ static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *sour } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t item_count, IrInstruction **elem_result_loc_list, - IrInstruction *result_loc) + size_t item_count, IrInstruction **elem_result_loc_list, IrInstruction *result_loc, + AstNode *init_array_type_source_node) { IrInstructionContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); - container_init_list_instruction->container_type = container_type; container_init_list_instruction->item_count = item_count; container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; container_init_list_instruction->result_loc = result_loc; + container_init_list_instruction->init_array_type_source_node = init_array_type_source_node; - ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); } @@ -1560,17 +2124,14 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields, - IrInstruction *result_loc) + size_t field_count, IrInstructionContainerInitFieldsField *fields, IrInstruction *result_loc) { IrInstructionContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); - container_init_fields_instruction->container_type = container_type; container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; container_init_fields_instruction->result_loc = result_loc; - ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < field_count; i += 1) { ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); } @@ -1582,8 +2143,8 @@ static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scop static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = ir_build_instruction(irb, scope, source_node); - unreachable_instruction->base.value.special = ConstValSpecialStatic; - unreachable_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; + unreachable_instruction->base.value->special = ConstValSpecialStatic; + unreachable_instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; return &unreachable_instruction->base; } @@ -1591,8 +2152,8 @@ static IrInstructionStorePtr *ir_build_store_ptr(IrBuilder *irb, Scope *scope, A IrInstruction *ptr, IrInstruction *value) { IrInstructionStorePtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.special = ConstValSpecialStatic; - instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->base.value->special = ConstValSpecialStatic; + instruction->base.value->type = irb->codegen->builtin_types.entry_void; instruction->ptr = ptr; instruction->value = value; @@ -1602,12 +2163,29 @@ static IrInstructionStorePtr *ir_build_store_ptr(IrBuilder *irb, Scope *scope, A return instruction; } +static IrInstruction *ir_build_vector_store_elem(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *vector_ptr, IrInstruction *index, IrInstruction *value) +{ + IrInstructionVectorStoreElem *inst = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_void; + inst->vector_ptr = vector_ptr; + inst->index = index; + inst->value = value; + + ir_ref_instruction(vector_ptr, ira->new_irb.current_basic_block); + ir_ref_instruction(index, ira->new_irb.current_basic_block); + ir_ref_instruction(value, ira->new_irb.current_basic_block); + + return &inst->base; +} + static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *align_value, IrInstruction *ptr) { IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction(irb, scope, source_node); - decl_var_instruction->base.value.special = ConstValSpecialStatic; - decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; + decl_var_instruction->base.value->special = ConstValSpecialStatic; + decl_var_instruction->base.value->type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->align_value = align_value; decl_var_instruction->ptr = ptr; @@ -1623,8 +2201,8 @@ static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *sourc { IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - decl_var_instruction->base.value.special = ConstValSpecialStatic; - decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void; + decl_var_instruction->base.value->special = ConstValSpecialStatic; + decl_var_instruction->base.value->type = ira->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->var_ptr = var_ptr; @@ -1638,30 +2216,28 @@ static IrInstruction *ir_build_resize_slice(IrAnalyze *ira, IrInstruction *sourc { IrInstructionResizeSlice *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ty; + instruction->base.value->type = ty; instruction->operand = operand; instruction->result_loc = result_loc; ir_ref_instruction(operand, ira->new_irb.current_basic_block); - ir_ref_instruction(result_loc, ira->new_irb.current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block); return &instruction->base; } static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *name, IrInstruction *target, IrInstruction *linkage) + IrInstruction *target, IrInstruction *options) { IrInstructionExport *export_instruction = ir_build_instruction( irb, scope, source_node); - export_instruction->base.value.special = ConstValSpecialStatic; - export_instruction->base.value.type = irb->codegen->builtin_types.entry_void; - export_instruction->name = name; + export_instruction->base.value->special = ConstValSpecialStatic; + export_instruction->base.value->type = irb->codegen->builtin_types.entry_void; export_instruction->target = target; - export_instruction->linkage = linkage; + export_instruction->options = options; - ir_ref_instruction(name, irb->current_basic_block); ir_ref_instruction(target, irb->current_basic_block); - if (linkage) ir_ref_instruction(linkage, irb->current_basic_block); + ir_ref_instruction(options, irb->current_basic_block); return &export_instruction->base; } @@ -1716,13 +2292,15 @@ static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstN } static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size, - IrInstruction *child_type) + IrInstruction *sentinel, IrInstruction *child_type) { IrInstructionArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; + instruction->sentinel = sentinel; instruction->child_type = child_type; ir_ref_instruction(size, irb->current_basic_block); + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); return &instruction->base; @@ -1738,34 +2316,59 @@ static IrInstruction *ir_build_anyframe_type(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } + static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, bool is_allow_zero) + IrInstruction *child_type, bool is_const, bool is_volatile, + IrInstruction *sentinel, IrInstruction *align_value, bool is_allow_zero) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->is_volatile = is_volatile; instruction->child_type = child_type; + instruction->sentinel = sentinel; instruction->align_value = align_value; instruction->is_allow_zero = is_allow_zero; + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(child_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); return &instruction->base; } -static IrInstruction *ir_build_global_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *asm_code) { - IrInstructionGlobalAsm *instruction = ir_build_instruction(irb, scope, source_node); - instruction->asm_code = asm_code; +static IrInstruction *ir_build_asm_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *asm_template, IrInstruction **input_list, IrInstruction **output_types, + ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global) +{ + IrInstructionAsmSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->asm_template = asm_template; + instruction->input_list = input_list; + instruction->output_types = output_types; + instruction->output_vars = output_vars; + instruction->return_count = return_count; + instruction->has_side_effects = has_side_effects; + instruction->is_global = is_global; + + assert(source_node->type == NodeTypeAsmExpr); + for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { + IrInstruction *output_type = output_types[i]; + if (output_type) ir_ref_instruction(output_type, irb->current_basic_block); + } + + for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { + IrInstruction *input_value = input_list[i]; + ir_ref_instruction(input_value, irb->current_basic_block); + } + return &instruction->base; } -static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_asm_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, Buf *asm_template, AsmToken *token_list, size_t token_list_len, IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects) { - IrInstructionAsm *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionAsmGen *instruction = ir_build_instruction(&ira->new_irb, scope, source_node); instruction->asm_template = asm_template; instruction->token_list = token_list; instruction->token_list_len = token_list_len; @@ -1778,12 +2381,12 @@ static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source assert(source_node->type == NodeTypeAsmExpr); for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { IrInstruction *output_type = output_types[i]; - if (output_type) ir_ref_instruction(output_type, irb->current_basic_block); + if (output_type) ir_ref_instruction(output_type, ira->new_irb.current_basic_block); } for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { IrInstruction *input_value = input_list[i]; - ir_ref_instruction(input_value, irb->current_basic_block); + ir_ref_instruction(input_value, ira->new_irb.current_basic_block); } return &instruction->base; @@ -1825,7 +2428,7 @@ static IrInstruction *ir_build_optional_wrap(IrAnalyze *ira, IrInstruction *sour { IrInstructionOptionalWrap *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_ty; + instruction->base.value->type = result_ty; instruction->operand = operand; instruction->result_loc = result_loc; @@ -1840,7 +2443,7 @@ static IrInstruction *ir_build_err_wrap_payload(IrAnalyze *ira, IrInstruction *s { IrInstructionErrWrapPayload *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; @@ -1855,7 +2458,7 @@ static IrInstruction *ir_build_err_wrap_code(IrAnalyze *ira, IrInstruction *sour { IrInstructionErrWrapCode *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; @@ -1925,8 +2528,8 @@ static IrInstructionSwitchBr *ir_build_switch_br(IrBuilder *irb, Scope *scope, A IrInstruction *switch_prongs_void) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; - instruction->base.value.special = ConstValSpecialStatic; + instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; + instruction->base.value->special = ConstValSpecialStatic; instruction->target_value = target_value; instruction->else_block = else_block; instruction->case_count = case_count; @@ -2022,7 +2625,7 @@ static IrInstruction *ir_build_ref_gen(IrAnalyze *ira, IrInstruction *source_ins { IrInstructionRefGen *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; @@ -2137,7 +2740,7 @@ static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source { IrInstructionCmpxchgGen *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; @@ -2361,18 +2964,21 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, ResultLoc *result_loc) + IrInstruction *ptr, IrInstruction *start, IrInstruction *end, IrInstruction *sentinel, + bool safety_check_on, ResultLoc *result_loc) { IrInstructionSliceSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; + instruction->sentinel = sentinel; instruction->safety_check_on = safety_check_on; instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(start, irb->current_basic_block); if (end) ir_ref_instruction(end, irb->current_basic_block); + if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); return &instruction->base; } @@ -2382,7 +2988,7 @@ static IrInstruction *ir_build_splat_gen(IrAnalyze *ira, IrInstruction *source_i { IrInstructionSplatGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->scalar = scalar; ir_ref_instruction(scalar, ira->new_irb.current_basic_block); @@ -2395,7 +3001,7 @@ static IrInstruction *ir_build_slice_gen(IrAnalyze *ira, IrInstruction *source_i { IrInstructionSliceGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = slice_type; + instruction->base.value->type = slice_type; instruction->ptr = ptr; instruction->start = start; instruction->end = end; @@ -2517,9 +3123,7 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode //TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, // lround, llround, lrint, llrint // So far this is only non-complicated type functions. -const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { - const bool b = llvm_name; - +const char *float_op_to_name(BuiltinFnId op) { switch (op) { case BuiltinFnIdSqrt: return "sqrt"; @@ -2531,8 +3135,8 @@ const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { return "exp"; case BuiltinFnIdExp2: return "exp2"; - case BuiltinFnIdLn: - return b ? "log" : "ln"; + case BuiltinFnIdLog: + return "log"; case BuiltinFnIdLog10: return "log10"; case BuiltinFnIdLog2: @@ -2546,7 +3150,7 @@ const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { case BuiltinFnIdTrunc: return "trunc"; case BuiltinFnIdNearbyInt: - return b ? "nearbyint" : "nearbyInt"; + return "nearbyint"; case BuiltinFnIdRound: return "round"; default: @@ -2554,14 +3158,14 @@ const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { } } -static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) { +static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *operand, + BuiltinFnId fn_id) +{ IrInstructionFloatOp *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op1 = op1; - instruction->op = op; + instruction->operand = operand; + instruction->fn_id = fn_id; - if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(operand, irb->current_basic_block); return &instruction->base; } @@ -2609,7 +3213,7 @@ static IrInstruction *ir_build_test_err_gen(IrAnalyze *ira, IrInstruction *sourc { IrInstructionTestErrGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ira->codegen->builtin_types.entry_bool; + instruction->base.value->type = ira->codegen->builtin_types.entry_bool; instruction->err_union = err_union; ir_ref_instruction(err_union, ira->new_irb.current_basic_block); @@ -2642,12 +3246,13 @@ static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, - bool is_var_args) + IrInstruction **param_types, IrInstruction *align_value, IrInstruction *callconv_value, + IrInstruction *return_type, bool is_var_args) { IrInstructionFnProto *instruction = ir_build_instruction(irb, scope, source_node); instruction->param_types = param_types; instruction->align_value = align_value; + instruction->callconv_value = callconv_value; instruction->return_type = return_type; instruction->is_var_args = is_var_args; @@ -2658,6 +3263,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); } if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); + if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block); ir_ref_instruction(return_type, irb->current_basic_block); return &instruction->base; @@ -2692,7 +3298,7 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc { IrInstructionPtrCastGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ptr_type; + instruction->base.value->type = ptr_type; instruction->ptr = ptr; instruction->safety_check_on = safety_check_on; @@ -2706,7 +3312,7 @@ static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *sourc { IrInstructionLoadPtrGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ty; + instruction->base.value->type = ty; instruction->ptr = ptr; instruction->result_loc = result_loc; @@ -2716,6 +3322,18 @@ static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *sourc return &instruction->base; } +static IrInstruction *ir_build_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *operand, ResultLocCast *result_loc_cast) +{ + IrInstructionImplicitCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand = operand; + instruction->result_loc_cast = result_loc_cast; + + ir_ref_instruction(operand, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *operand, ResultLocBitCast *result_loc_bit_cast) { @@ -2733,7 +3351,7 @@ static IrInstruction *ir_build_bit_cast_gen(IrAnalyze *ira, IrInstruction *sourc { IrInstructionBitCastGen *instruction = ir_build_instruction( &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ty; + instruction->base.value->type = ty; instruction->operand = operand; ir_ref_instruction(operand, ira->new_irb.current_basic_block); @@ -2885,8 +3503,8 @@ static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *s static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) { IrInstructionPanic *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.special = ConstValSpecialStatic; - instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; + instruction->base.value->special = ConstValSpecialStatic; + instruction->base.value->type = irb->codegen->builtin_types.entry_unreachable; instruction->msg = msg; ir_ref_instruction(msg, irb->current_basic_block); @@ -3013,20 +3631,6 @@ static IrInstruction *ir_build_align_cast(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *dest_type, IrInstruction *target, ResultLoc *result_loc) -{ - IrInstructionImplicitCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - instruction->result_loc = result_loc; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_resolve_result(IrBuilder *irb, Scope *scope, AstNode *source_node, ResultLoc *result_loc, IrInstruction *ty) { @@ -3034,7 +3638,7 @@ static IrInstruction *ir_build_resolve_result(IrBuilder *irb, Scope *scope, AstN instruction->result_loc = result_loc; instruction->ty = ty; - ir_ref_instruction(ty, irb->current_basic_block); + if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); return &instruction->base; } @@ -3066,11 +3670,12 @@ static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, Ast } static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *fn_type, IrInstruction *arg_index) + IrInstruction *fn_type, IrInstruction *arg_index, bool allow_var) { IrInstructionArgType *instruction = ir_build_instruction(irb, scope, source_node); instruction->fn_type = fn_type; instruction->arg_index = arg_index; + instruction->allow_var = allow_var; ir_ref_instruction(fn_type, irb->current_basic_block); ir_ref_instruction(arg_index, irb->current_basic_block); @@ -3137,6 +3742,25 @@ static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_atomic_store(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *value, + IrInstruction *ordering, AtomicOrder resolved_ordering) +{ + IrInstructionAtomicStore *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->value = value; + instruction->ordering = ordering; + instruction->resolved_ordering = resolved_ordering; + + if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(value, irb->current_basic_block); + if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionSaveErrRetAddr *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; @@ -3210,7 +3834,7 @@ static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *so { IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->vector = vector; instruction->result_loc = result_loc; @@ -3225,7 +3849,7 @@ static IrInstruction *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInstructi { IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->operand = operand; instruction->result_loc = result_loc; @@ -3240,7 +3864,7 @@ static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *so { IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->array = array; ir_ref_instruction(array, ira->new_irb.current_basic_block); @@ -3253,7 +3877,7 @@ static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source { IrInstructionAssertZero *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ira->codegen->builtin_types.entry_void; + instruction->base.value->type = ira->codegen->builtin_types.entry_void; instruction->target = target; ir_ref_instruction(target, ira->new_irb.current_basic_block); @@ -3266,7 +3890,7 @@ static IrInstruction *ir_build_assert_non_null(IrAnalyze *ira, IrInstruction *so { IrInstructionAssertNonNull *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = ira->codegen->builtin_types.entry_void; + instruction->base.value->type = ira->codegen->builtin_types.entry_void; instruction->target = target; ir_ref_instruction(target, ira->new_irb.current_basic_block); @@ -3315,7 +3939,7 @@ static IrInstruction *ir_build_end_expr(IrBuilder *irb, Scope *scope, AstNode *s static IrInstructionSuspendBegin *ir_build_suspend_begin(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionSuspendBegin *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->base.value->type = irb->codegen->builtin_types.entry_void; return instruction; } @@ -3324,7 +3948,7 @@ static IrInstruction *ir_build_suspend_finish(IrBuilder *irb, Scope *scope, AstN IrInstructionSuspendBegin *begin) { IrInstructionSuspendFinish *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->base.value->type = irb->codegen->builtin_types.entry_void; instruction->begin = begin; ir_ref_instruction(&begin->base, irb->current_basic_block); @@ -3349,7 +3973,7 @@ static IrInstructionAwaitGen *ir_build_await_gen(IrAnalyze *ira, IrInstruction * { IrInstructionAwaitGen *instruction = ir_build_instruction(&ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value.type = result_type; + instruction->base.value->type = result_type; instruction->frame = frame; instruction->result_loc = result_loc; @@ -3361,7 +3985,7 @@ static IrInstructionAwaitGen *ir_build_await_gen(IrAnalyze *ira, IrInstruction * static IrInstruction *ir_build_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *frame) { IrInstructionResume *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->base.value->type = irb->codegen->builtin_types.entry_void; instruction->frame = frame; ir_ref_instruction(frame, irb->current_basic_block); @@ -3373,8 +3997,8 @@ static IrInstructionSpillBegin *ir_build_spill_begin(IrBuilder *irb, Scope *scop IrInstruction *operand, SpillId spill_id) { IrInstructionSpillBegin *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.value.special = ConstValSpecialStatic; - instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->base.value->special = ConstValSpecialStatic; + instruction->base.value->type = irb->codegen->builtin_types.entry_void; instruction->operand = operand; instruction->spill_id = spill_id; @@ -3394,6 +4018,21 @@ static IrInstruction *ir_build_spill_end(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } +static IrInstruction *ir_build_vector_extract_elem(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *vector, IrInstruction *index) +{ + IrInstructionVectorExtractElem *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = vector->value->type->data.vector.elem_type; + instruction->vector = vector; + instruction->index = index; + + ir_ref_instruction(vector, ira->new_irb.current_basic_block); + ir_ref_instruction(index, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3455,8 +4094,8 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o Scope *defer_expr_scope = defer_node->data.defer.expr_scope; IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && - defer_expr_value->value.type->id == ZigTypeIdUnreachable) + if (defer_expr_value->value->type != nullptr && + defer_expr_value->value->type->id == ZigTypeIdUnreachable) { is_noreturn = true; } else { @@ -3549,7 +4188,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { - ResultLocReturn *result_loc_ret = allocate(1); + ResultLocReturn *result_loc_ret = allocate(1, "ResultLocReturn"); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); @@ -3644,7 +4283,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val, SpillIdRetErrCode); - ResultLocReturn *result_loc_ret = allocate(1); + ResultLocReturn *result_loc_ret = allocate(1, "ResultLocReturn"); result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); @@ -3672,7 +4311,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime, bool skip_name_check) { - ZigVar *variable_entry = allocate(1); + ZigVar *variable_entry = allocate(1, "ZigVar"); variable_entry->parent_scope = parent_scope; variable_entry->shadowable = is_shadowable; variable_entry->mem_slot_index = SIZE_MAX; @@ -3747,7 +4386,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n } static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { - ResultLocPeer *result = allocate(1); + ResultLocPeer *result = allocate(1, "ResultLocPeer"); result->base.id = ResultLocIdPeer; result->base.source_instruction = peer_parent->base.source_instruction; result->parent = peer_parent; @@ -3786,9 +4425,10 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); - scope_block->peer_parent = allocate(1); + scope_block->peer_parent = allocate(1, "ResultLocPeerParent"); scope_block->peer_parent->base.id = ResultLocIdPeerParent; scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; + scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; scope_block->peer_parent->end_bb = scope_block->end_block; scope_block->peer_parent->is_comptime = scope_block->is_comptime; scope_block->peer_parent->parent = result_loc; @@ -3894,12 +4534,26 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); } +static IrInstruction *ir_gen_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *node) { + IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + + if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + // TODO only pass type_name when the || operator is the top level AST node in the var decl expr + Buf bare_name = BUF_INIT; + Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name); + + return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name); +} + static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ResultLocInstruction *result_loc_inst = allocate(1); + ResultLocInstruction *result_loc_inst = allocate(1, "ResultLocInstruction"); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = lvalue; ir_ref_instruction(lvalue, irb->current_basic_block); @@ -3913,6 +4567,19 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_const_void(irb, scope, node); } +static IrInstruction *ir_gen_assign_merge_err_sets(IrBuilder *irb, Scope *scope, AstNode *node) { + IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); + if (lvalue == irb->codegen->invalid_instruction) + return lvalue; + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (op2 == irb->codegen->invalid_instruction) + return op2; + IrInstruction *result = ir_build_merge_err_sets(irb, scope, node, op1, op2, nullptr); + ir_build_store_ptr(irb, scope, node, lvalue, result); + return ir_build_const_void(irb, scope, node); +} + static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) @@ -3958,10 +4625,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node ir_set_cursor_at_end_and_append_block(irb, true_block); - IrInstruction **incoming_values = allocate(2); + IrInstruction **incoming_values = allocate(2, "IrInstruction *"); incoming_values[0] = val1; incoming_values[1] = val2; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; @@ -4003,7 +4670,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; @@ -4016,6 +4683,7 @@ static ResultLocPeerParent *ir_build_result_peers(IrBuilder *irb, IrInstruction ResultLocPeerParent *peer_parent = allocate(1); peer_parent->base.id = ResultLocIdPeerParent; peer_parent->base.source_instruction = cond_br_inst; + peer_parent->base.allow_write_through_const = parent->allow_write_through_const; peer_parent->end_bb = end_block; peer_parent->is_comptime = is_comptime; peer_parent->parent = parent; @@ -4093,7 +4761,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = null_result; incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_null_block; incoming_blocks[1] = after_ok_block; IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); @@ -4153,7 +4821,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, case BinOpTypeAssignBitOr: return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc); case BinOpTypeAssignMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets), lval, result_loc); + return ir_lval_wrap(irb, scope, ir_gen_assign_merge_err_sets(irb, scope, node), lval, result_loc); case BinOpTypeBoolOr: return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc); case BinOpTypeBoolAnd: @@ -4201,7 +4869,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, case BinOpTypeArrayMult: return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc); case BinOpTypeMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets), lval, result_loc); + return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc); case BinOpTypeUnwrapOptional: return ir_gen_orelse(irb, scope, node, lval, result_loc); case BinOpTypeErrorUnion: @@ -4251,7 +4919,7 @@ static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base); tld_var->base.resolution = TldResolutionInvalid; tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false, - &g->invalid_instruction->value, &tld_var->base, g->builtin_types.entry_invalid); + g->invalid_instruction->value, &tld_var->base, g->builtin_types.entry_invalid); scope_decls->decl_table.put(var_name, &tld_var->base); } @@ -4264,10 +4932,10 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, if (buf_eql_str(variable_name, "_")) { if (lval == LValPtr) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); - const_instruction->base.value.type = get_pointer_to_type(irb->codegen, + const_instruction->base.value->type = get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_void, false); - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_ptr.special = ConstPtrSpecialDiscard; return &const_instruction->base; } else { add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to")); @@ -4457,10 +5125,7 @@ static IrInstruction *ir_gen_async_call(IrBuilder *irb, Scope *scope, AstNode *a return fn_ref; size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset; - - // last "arg" is return pointer - IrInstruction **args = allocate(arg_count + 1); - + IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset); IrInstruction *arg = ir_gen_node(irb, arg_node, scope); @@ -4469,15 +5134,50 @@ static IrInstruction *ir_gen_async_call(IrBuilder *irb, Scope *scope, AstNode *a args[i] = arg; } - args[arg_count] = ret_ptr; - CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone; bool is_async_call_builtin = true; - IrInstruction *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, modifier, is_async_call_builtin, bytes, result_loc); + IrInstruction *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, + ret_ptr, modifier, is_async_call_builtin, bytes, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } +static IrInstruction *ir_gen_fn_call_with_args(IrBuilder *irb, Scope *scope, AstNode *source_node, + AstNode *fn_ref_node, CallModifier modifier, IrInstruction *options, + AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc) +{ + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_instruction) + return fn_ref; + + IrInstruction *fn_type = ir_build_typeof(irb, scope, source_node, fn_ref); + + IrInstruction **args = allocate(args_len); + for (size_t i = 0; i < args_len; i += 1) { + AstNode *arg_node = args_ptr[i]; + + IrInstruction *arg_index = ir_build_const_usize(irb, scope, arg_node, i); + IrInstruction *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true); + ResultLoc *no_result = no_result_loc(); + ir_build_reset_result(irb, scope, source_node, no_result); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result); + + IrInstruction *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base); + if (arg == irb->codegen->invalid_instruction) + return arg; + + args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast); + } + + IrInstruction *fn_call; + if (options != nullptr) { + fn_call = ir_build_call_src_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc); + } else { + fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr, + modifier, false, nullptr, result_loc); + } + return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); +} + static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { @@ -4795,7 +5495,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdCos: case BuiltinFnIdExp: case BuiltinFnIdExp2: - case BuiltinFnIdLn: + case BuiltinFnIdLog: case BuiltinFnIdLog2: case BuiltinFnIdLog10: case BuiltinFnIdFabs: @@ -4810,13 +5510,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_instruction) - return arg1_value; - - IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id); - return ir_lval_wrap(irb, scope, ir_sqrt, lval, result_loc); + IrInstruction *inst = ir_build_float_op(irb, scope, node, arg0_value, builtin_fn->id); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); } case BuiltinFnIdTruncate: { @@ -5282,6 +5977,24 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast); return ir_lval_wrap(irb, scope, bitcast, lval, result_loc); } + case BuiltinFnIdAs: + { + AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); + IrInstruction *dest_type = ir_gen_node(irb, dest_type_node, scope); + if (dest_type == irb->codegen->invalid_instruction) + return dest_type; + + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc); + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, + &result_loc_cast->base); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } case BuiltinFnIdIntToPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5378,34 +6091,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); } - case BuiltinFnIdInlineCall: - case BuiltinFnIdNoInlineCall: - { - if (node->data.fn_call_expr.params.length == 0) { - add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); - return irb->codegen->invalid_instruction; - } - - AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_instruction) - return fn_ref; - - size_t arg_count = node->data.fn_call_expr.params.length - 1; - - IrInstruction **args = allocate(arg_count); - for (size_t i = 0; i < arg_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1); - args[i] = ir_gen_node(irb, arg_node, scope); - if (args[i] == irb->codegen->invalid_instruction) - return args[i]; - } - FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - - IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - fn_inline, CallModifierNone, false, nullptr, result_loc); - return ir_lval_wrap(irb, scope, call, lval, result_loc); - } case BuiltinFnIdNewStackCall: { if (node->data.fn_call_expr.params.length < 2) { @@ -5435,10 +6120,52 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return args[i]; } - IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, CallModifierNone, false, new_stack, result_loc); + IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, + nullptr, CallModifierNone, false, new_stack, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } + case BuiltinFnIdCall: { + // Cast the options parameter to the options type + ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions"); + IrInstruction *options_type_inst = ir_build_const_type(irb, scope, node, options_type); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); + + AstNode *options_node = node->data.fn_call_expr.params.at(0); + IrInstruction *options_inner = ir_gen_node_extra(irb, options_node, scope, + LValNone, &result_loc_cast->base); + if (options_inner == irb->codegen->invalid_instruction) + return options_inner; + IrInstruction *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast); + + AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); + AstNode *args_node = node->data.fn_call_expr.params.at(2); + if (args_node->type == NodeTypeContainerInitExpr) { + if (args_node->data.container_init_expr.kind == ContainerInitKindArray || + args_node->data.container_init_expr.entries.length == 0) + { + return ir_gen_fn_call_with_args(irb, scope, node, + fn_ref_node, CallModifierNone, options, + args_node->data.container_init_expr.entries.items, + args_node->data.container_init_expr.entries.length, + lval, result_loc); + } else { + exec_add_error_node(irb->codegen, irb->exec, args_node, + buf_sprintf("TODO: @call with anon struct literal")); + return irb->codegen->invalid_instruction; + } + } else { + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_instruction) + return fn_ref; + + IrInstruction *args = ir_gen_node(irb, args_node, scope); + if (args == irb->codegen->invalid_instruction) + return args; + + IrInstruction *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc); + return ir_lval_wrap(irb, scope, call, lval, result_loc); + } + } case BuiltinFnIdAsyncCall: return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc); case BuiltinFnIdTypeId: @@ -5538,27 +6265,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value, false); return ir_lval_wrap(irb, scope, arg_type, lval, result_loc); } case BuiltinFnIdExport: { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_instruction) - return arg0_value; + // Cast the options parameter to the options type + ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions"); + IrInstruction *options_type_inst = ir_build_const_type(irb, scope, node, options_type); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_instruction) - return arg1_value; + AstNode *target_node = node->data.fn_call_expr.params.at(0); + IrInstruction *target_value = ir_gen_node(irb, target_node, scope); + if (target_value == irb->codegen->invalid_instruction) + return target_value; - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_instruction) - return arg2_value; + AstNode *options_node = node->data.fn_call_expr.params.at(1); + IrInstruction *options_value = ir_gen_node_extra(irb, options_node, + scope, LValNone, &result_loc_cast->base); + if (options_value == irb->codegen->invalid_instruction) + return options_value; - IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *casted_options_value = ir_build_implicit_cast( + irb, scope, options_node, options_value, result_loc_cast); + + IrInstruction *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value); return ir_lval_wrap(irb, scope, ir_export, lval, result_loc); } case BuiltinFnIdErrorReturnTrace: @@ -5621,6 +6352,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo AtomicOrderMonotonic); return ir_lval_wrap(irb, scope, inst, lval, result_loc); } + case BuiltinFnIdAtomicStore: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_instruction) + return arg2_value; + + AstNode *arg3_node = node->data.fn_call_expr.params.at(3); + IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); + if (arg3_value == irb->codegen->invalid_instruction) + return arg3_value; + + IrInstruction *inst = ir_build_atomic_store(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, + // this value does not mean anything since we passed non-null values for other arg + AtomicOrderMonotonic); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); + } case BuiltinFnIdIntToEnum: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5729,22 +6487,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_instruction) - return fn_ref; - - size_t arg_count = node->data.fn_call_expr.params.length; - IrInstruction **args = allocate(arg_count); - for (size_t i = 0; i < arg_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); - if (args[i] == irb->codegen->invalid_instruction) - return args[i]; - } - - IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, node->data.fn_call_expr.modifier, false, nullptr, result_loc); - return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); + return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, node->data.fn_call_expr.modifier, + nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -5804,7 +6548,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -5828,6 +6572,7 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) { + if (inst == irb->codegen->invalid_instruction) return inst; ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc); return inst; } @@ -5862,9 +6607,9 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) { case TokenIdStar: case TokenIdStarStar: return PtrLenSingle; - case TokenIdBracketStarBracket: + case TokenIdLBracket: return PtrLenUnknown; - case TokenIdBracketStarCBracket: + case TokenIdSymbol: return PtrLenC; default: zig_unreachable(); @@ -5873,13 +6618,25 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) { static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); + PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); + bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; + AstNode *sentinel_expr = node->data.pointer_type.sentinel; AstNode *expr_node = node->data.pointer_type.op_expr; AstNode *align_expr = node->data.pointer_type.align_expr; + IrInstruction *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } + IrInstruction *align_value; if (align_expr != nullptr) { align_value = ir_gen_node(irb, align_expr, scope); @@ -5924,7 +6681,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, - ptr_len, align_value, bit_offset_start, host_int_bytes, is_allow_zero); + ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero); } static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -6017,28 +6774,55 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; - IrInstruction *container_type = nullptr; - IrInstruction *elem_type = nullptr; - if (container_init_expr->type->type == NodeTypeInferredArrayType) { - elem_type = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.child_type, scope); - if (elem_type == irb->codegen->invalid_instruction) - return elem_type; - } else { - container_type = ir_gen_node(irb, container_init_expr->type, scope); - if (container_type == irb->codegen->invalid_instruction) - return container_type; - } - - switch (kind) { - case ContainerInitKindStruct: { - if (elem_type != nullptr) { + ResultLocCast *result_loc_cast = nullptr; + ResultLoc *child_result_loc; + AstNode *init_array_type_source_node; + if (container_init_expr->type != nullptr) { + IrInstruction *container_type; + if (container_init_expr->type->type == NodeTypeInferredArrayType) { + if (kind == ContainerInitKindStruct) { add_node_error(irb->codegen, container_init_expr->type, buf_sprintf("initializing array with struct syntax")); return irb->codegen->invalid_instruction; } + IrInstruction *sentinel; + if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) { + sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } - IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc, - container_type); + IrInstruction *elem_type = ir_gen_node(irb, + container_init_expr->type->data.inferred_array_type.child_type, scope); + if (elem_type == irb->codegen->invalid_instruction) + return elem_type; + size_t item_count = container_init_expr->entries.length; + IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); + container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type); + } else { + container_type = ir_gen_node(irb, container_init_expr->type, scope); + if (container_type == irb->codegen->invalid_instruction) + return container_type; + } + + result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc); + child_result_loc = &result_loc_cast->base; + init_array_type_source_node = container_type->source_node; + } else { + child_result_loc = parent_result_loc; + if (parent_result_loc->source_instruction != nullptr) { + init_array_type_source_node = parent_result_loc->source_instruction->source_node; + } else { + init_array_type_source_node = node; + } + } + + switch (kind) { + case ContainerInitKindStruct: { + IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, + nullptr); size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); @@ -6066,29 +6850,27 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A fields[i].source_node = entry_node; fields[i].result_loc = field_ptr; } - IrInstruction *init_fields = ir_build_container_init_fields(irb, scope, node, container_type, - field_count, fields, container_ptr); + IrInstruction *result = ir_build_container_init_fields(irb, scope, node, field_count, + fields, container_ptr); - return ir_lval_wrap(irb, scope, init_fields, lval, parent_result_loc); + if (result_loc_cast != nullptr) { + result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); + } + return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); } case ContainerInitKindArray: { size_t item_count = container_init_expr->entries.length; - if (container_type == nullptr) { - IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); - container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type); - } - - IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc, - container_type); + IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, + nullptr); IrInstruction **result_locs = allocate(item_count); for (size_t i = 0; i < item_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstruction *elem_index = ir_build_const_usize(irb, scope, expr_node, i); - IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, elem_index, - false, PtrLenSingle, container_type); + IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, + elem_index, false, PtrLenSingle, init_array_type_source_node); ResultLocInstruction *result_loc_inst = allocate(1); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = elem_ptr; @@ -6103,9 +6885,12 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A result_locs[i] = elem_ptr; } - IrInstruction *init_list = ir_build_container_init_list(irb, scope, node, container_type, - item_count, result_locs, container_ptr); - return ir_lval_wrap(irb, scope, init_list, lval, parent_result_loc); + IrInstruction *result = ir_build_container_init_list(irb, scope, node, item_count, + result_locs, container_ptr, init_array_type_source_node); + if (result_loc_cast != nullptr) { + result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); + } + return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); } } zig_unreachable(); @@ -6115,6 +6900,7 @@ static ResultLocVar *ir_build_var_result_loc(IrBuilder *irb, IrInstruction *allo ResultLocVar *result_loc_var = allocate(1); result_loc_var->base.id = ResultLocIdVar; result_loc_var->base.source_instruction = alloca; + result_loc_var->base.allow_write_through_const = true; result_loc_var->var = var; ir_build_reset_result(irb, alloca->scope, alloca->source_node, &result_loc_var->base); @@ -6122,6 +6908,21 @@ static ResultLocVar *ir_build_var_result_loc(IrBuilder *irb, IrInstruction *allo return result_loc_var; } +static ResultLocCast *ir_build_cast_result_loc(IrBuilder *irb, IrInstruction *dest_type, + ResultLoc *parent_result_loc) +{ + ResultLocCast *result_loc_cast = allocate(1); + result_loc_cast->base.id = ResultLocIdCast; + result_loc_cast->base.source_instruction = dest_type; + result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const; + ir_ref_instruction(dest_type, irb->current_basic_block); + result_loc_cast->parent = parent_result_loc; + + ir_build_reset_result(irb, dest_type->scope, dest_type->source_node, &result_loc_cast->base); + + return result_loc_cast; +} + static void build_decl_var_and_init(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *init, const char *name_hint, IrInstruction *is_comptime) { @@ -6190,7 +6991,15 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod // Create a result location for the initialization expression. ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var); - ResultLoc *init_result_loc = (type_instruction == nullptr) ? &result_loc_var->base : nullptr; + ResultLoc *init_result_loc; + ResultLocCast *result_loc_cast; + if (type_instruction != nullptr) { + result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base); + init_result_loc = &result_loc_cast->base; + } else { + result_loc_cast = nullptr; + init_result_loc = &result_loc_var->base; + } Scope *init_scope = is_comptime_scalar ? create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope; @@ -6206,9 +7015,9 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - if (type_instruction != nullptr) { - IrInstruction *implicit_cast = ir_build_implicit_cast(irb, scope, node, type_instruction, init_value, - &result_loc_var->base); + if (result_loc_cast != nullptr) { + IrInstruction *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->source_node, + init_value, result_loc_cast); ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base); } @@ -6647,6 +7456,8 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo // it's actually in break statements, handled similarly to return statements. // That is why we set those values in loop_scope above and not in this ir_gen_node call. IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base); + if (body_result == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; if (!instr_is_unreachable(body_result)) { ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result)); @@ -6706,11 +7517,7 @@ static IrInstruction *ir_gen_enum_literal(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeStringLiteral); - if (node->data.string_literal.c) { - return ir_build_const_c_str_lit(irb, scope, node, node->data.string_literal.buf); - } else { - return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); - } + return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); } static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -6721,9 +7528,20 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n bool is_const = node->data.array_type.is_const; bool is_volatile = node->data.array_type.is_volatile; bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr; + AstNode *sentinel_expr = node->data.array_type.sentinel; AstNode *align_expr = node->data.array_type.align_expr; Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); + + IrInstruction *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope); + if (sentinel == irb->codegen->invalid_instruction) + return sentinel; + } else { + sentinel = nullptr; + } + if (size_node) { if (is_const) { add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); @@ -6750,7 +7568,7 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n if (child_type == irb->codegen->invalid_instruction) return child_type; - return ir_build_array_type(irb, scope, node, size_value, child_type); + return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type); } else { IrInstruction *align_value; if (align_expr != nullptr) { @@ -6765,7 +7583,8 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n if (child_type == irb->codegen->invalid_instruction) return child_type; - return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value, is_allow_zero); + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel, + align_value, is_allow_zero); } } @@ -6790,7 +7609,7 @@ static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, Ast return ir_build_const_undefined(irb, scope, node); } -static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_template, +static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template, ZigList *tok_list) { // TODO Connect the errors in this function back up to the actual source location @@ -6838,7 +7657,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t cur_tok->end = i; state = StateStart; } else { - add_node_error(irb->codegen, source_node, + add_node_error(ira->codegen, source_node, buf_create_from_str("expected a '%' or '['")); return ErrorSemanticAnalyzeFail; } @@ -6861,7 +7680,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t { // do nothing } else { - add_node_error(irb->codegen, source_node, + add_node_error(ira->codegen, source_node, buf_sprintf("invalid substitution character: '%c'", c)); return ErrorSemanticAnalyzeFail; } @@ -6874,7 +7693,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t break; case StatePercent: case StateVar: - add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template")); + add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template")); return ErrorSemanticAnalyzeFail; case StateTemplate: cur_tok->end = buf_len(asm_template); @@ -6903,14 +7722,16 @@ static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_ } static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - Error err; assert(node->type == NodeTypeAsmExpr); AstNodeAsmExpr *asm_expr = &node->data.asm_expr; + + IrInstruction *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope); + if (asm_template == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + bool is_volatile = asm_expr->volatile_token != nullptr; bool in_fn_scope = (scope_fn_entry(scope) != nullptr); - Buf *template_buf = &asm_expr->asm_template->data.str_lit.str; - if (!in_fn_scope) { if (is_volatile) { add_token_error(irb->codegen, node->owner, asm_expr->volatile_token, @@ -6926,12 +7747,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return irb->codegen->invalid_instruction; } - return ir_build_global_asm(irb, scope, node, template_buf); - } - - ZigList tok_list = {}; - if ((err = parse_asm_template(irb, node, template_buf, &tok_list))) { - return irb->codegen->invalid_instruction; + return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr, + nullptr, 0, is_volatile, true); } IrInstruction **input_list = allocate(asm_expr->input_list.length); @@ -6989,24 +7806,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod input_list[i] = input_value; } - for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { - AsmToken asm_token = tok_list.at(token_i); - if (asm_token.id == AsmTokenIdVar) { - size_t index = find_asm_index(irb->codegen, node, &asm_token, template_buf); - if (index == SIZE_MAX) { - const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; - uint32_t len = asm_token.end - asm_token.start - 2; - - add_node_error(irb->codegen, node, - buf_sprintf("could not find '%.*s' in the inputs or outputs", - len, ptr)); - return irb->codegen->invalid_instruction; - } - } - } - - return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length, - input_list, output_types, output_vars, return_count, is_volatile); + return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types, + output_vars, return_count, is_volatile, false); } static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, @@ -7086,7 +7887,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -7183,7 +7984,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -7278,6 +8079,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ResultLocPeerParent *peer_parent = allocate(1); peer_parent->base.id = ResultLocIdPeerParent; + peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; peer_parent->end_bb = end_block; peer_parent->is_comptime = is_comptime; peer_parent->parent = result_loc; @@ -7682,6 +8484,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, AstNode *array_node = slice_expr->array_ref_expr; AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; + AstNode *sentinel_node = slice_expr->sentinel; IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr); if (ptr_value == irb->codegen->invalid_instruction) @@ -7700,7 +8503,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, end_value = nullptr; } - IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); + IrInstruction *sentinel_value; + if (sentinel_node) { + sentinel_value = ir_gen_node(irb, sentinel_node, scope); + if (sentinel_value == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } else { + sentinel_value = nullptr; + } + + IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, + sentinel_value, true, result_loc); return ir_lval_wrap(irb, scope, slice, lval, result_loc); } @@ -7779,7 +8592,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = err_result; incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_err_block; incoming_blocks[1] = after_ok_block; IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); @@ -7803,14 +8616,14 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, Scope *scope, AstNode *source_node, Buf *out_bare_name) { - if (exec->name) { + if (exec != nullptr && exec->name) { ZigType *import = get_scope_import(scope); - Buf *namespace_name = buf_create_from_buf(&import->name); - if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR); + Buf *namespace_name = buf_alloc(); + append_namespace_qualification(codegen, namespace_name, import); buf_append_buf(namespace_name, exec->name); buf_init_from_buf(out_bare_name, exec->name); return namespace_name; - } else if (exec->name_fn != nullptr) { + } else if (exec != nullptr && exec->name_fn != nullptr) { Buf *name = buf_alloc(); buf_append_buf(name, &exec->name_fn->symbol_name); buf_appendf(name, "("); @@ -7820,8 +8633,8 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char return name; } else { ZigType *import = get_scope_import(scope); - Buf *namespace_name = buf_create_from_buf(&import->name); - if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR); + Buf *namespace_name = buf_alloc(); + append_namespace_qualification(codegen, namespace_name, import); buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name, source_node->line + 1, source_node->column + 1); buf_init_from_buf(out_bare_name, namespace_name); @@ -7859,7 +8672,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, } // errors should be populated with set1's values -static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2) { +static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2, + Buf *type_name) +{ assert(set1->id == ZigTypeIdErrorSet); assert(set2->id == ZigTypeIdErrorSet); @@ -7867,8 +8682,12 @@ static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigTyp err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits; err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align; err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size; - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{"); + if (type_name == nullptr) { + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + } else { + buf_init_from_buf(&err_set_type->name, type_name); + } for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); @@ -7885,9 +8704,14 @@ static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigTyp err_set_type->data.error_set.err_count = count; err_set_type->data.error_set.errors = allocate(count); + bool need_comma = false; for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; - buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + if (type_name == nullptr) { + const char *comma = need_comma ? "," : ""; + need_comma = true; + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); + } err_set_type->data.error_set.errors[i] = error_entry; } @@ -7896,15 +8720,20 @@ static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigTyp ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; if (errors[error_entry->value] == nullptr) { errors[error_entry->value] = error_entry; - buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + if (type_name == nullptr) { + const char *comma = need_comma ? "," : ""; + need_comma = true; + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); + } err_set_type->data.error_set.errors[index] = error_entry; index += 1; } } assert(index == count); - assert(count != 0); - buf_appendf(&err_set_type->name, "}"); + if (type_name == nullptr) { + buf_appendf(&err_set_type->name, "}"); + } return err_set_type; @@ -7927,6 +8756,17 @@ static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstN return err_set_type; } +static AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) { + if (err_set_field_node->type == NodeTypeSymbol) { + return err_set_field_node; + } else if (err_set_field_node->type == NodeTypeErrorSetField) { + assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol); + return err_set_field_node->data.err_set_field.field_name; + } else { + return err_set_field_node; + } +} + static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeErrorSetDecl); @@ -7942,14 +8782,15 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; err_set_type->data.error_set.errors = allocate(err_count); - ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count); + size_t errors_count = irb->codegen->errors_by_index.length + err_count; + ErrorTableEntry **errors = allocate(errors_count, "ErrorTableEntry *"); for (uint32_t i = 0; i < err_count; i += 1) { - AstNode *symbol_node = node->data.err_set_decl.decls.at(i); - assert(symbol_node->type == NodeTypeSymbol); + AstNode *field_node = node->data.err_set_decl.decls.at(i); + AstNode *symbol_node = ast_field_to_symbol_node(field_node); Buf *err_name = symbol_node->data.symbol_expr.symbol; ErrorTableEntry *err = allocate(1); - err->decl_node = symbol_node; + err->decl_node = field_node; buf_init_from_buf(&err->name, err_name); auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); @@ -7965,13 +8806,15 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A ErrorTableEntry *prev_err = errors[err->value]; if (prev_err != nullptr) { - ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); - add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here")); + ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node), + buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); + add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node), + buf_sprintf("other error here")); return irb->codegen->invalid_instruction; } errors[err->value] = err; } - free(errors); + deallocate(errors, errors_count, "ErrorTableEntry *"); return ir_build_const_type(irb, parent_scope, node, err_set_type); } @@ -8006,6 +8849,13 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo return irb->codegen->invalid_instruction; } + IrInstruction *callconv_value = nullptr; + if (node->data.fn_proto.callconv_expr != nullptr) { + callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope); + if (callconv_value == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } + IrInstruction *return_type; if (node->data.fn_proto.return_var_token == nullptr) { if (node->data.fn_proto.return_type == nullptr) { @@ -8022,7 +8872,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo //return_type = nullptr; } - return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); + return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -8116,6 +8966,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: + case NodeTypeErrorSetField: case NodeTypeFnDef: case NodeTypeTestDecl: zig_unreachable(); @@ -8244,6 +9095,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop add_node_error(irb->codegen, node, buf_sprintf("inferred array size invalid here")); return irb->codegen->invalid_instruction; + case NodeTypeVarFieldType: + return ir_lval_wrap(irb, scope, + ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc); } zig_unreachable(); } @@ -8276,7 +9130,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc if (irb->exec->first_err_trace_msg == nullptr) { irb->exec->first_err_trace_msg = irb->codegen->trace_err; } - src_assert(irb->exec->first_err_trace_msg != nullptr, node); } return result; } @@ -8315,7 +9168,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr); - assert(result); + + if (result == irb->codegen->invalid_instruction) + return false; + if (irb->exec->first_err_trace_msg != nullptr) { codegen->trace_err = irb->exec->first_err_trace_msg; return false; @@ -8333,7 +9189,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { assert(fn_entry); - IrExecutable *ir_executable = &fn_entry->ir_executable; + IrExecutable *ir_executable = fn_entry->ir_executable; AstNode *body_node = fn_entry->body_node; assert(fn_entry->child_scope); @@ -8380,30 +9236,41 @@ static void ir_assert(bool ok, IrInstruction *source_instruction) { // This function takes a comptime ptr and makes the child const value conform to the type // described by the pointer. static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ConstExprValue *ptr_val) + ZigValue *ptr_val) { Error err; assert(ptr_val->type->id == ZigTypeIdPointer); assert(ptr_val->special == ConstValSpecialStatic); - ConstExprValue tmp = {}; + ZigValue tmp = {}; tmp.special = ConstValSpecialStatic; tmp.type = ptr_val->type->data.pointer.child_type; if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val))) return err; - ConstExprValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val); - copy_const_val(child_val, &tmp, false); + ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val); + copy_const_val(child_val, &tmp); return ErrorNone; } -ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, +ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, AstNode *source_node) { Error err; - ConstExprValue *val = const_ptr_pointee_unchecked(codegen, const_val); - assert(val != nullptr); + ZigValue *val = const_ptr_pointee_unchecked(codegen, const_val); + if (val == nullptr) return nullptr; assert(const_val->type->id == ZigTypeIdPointer); ZigType *expected_type = const_val->type->data.pointer.child_type; - if (!types_have_same_zig_comptime_repr(val->type, expected_type)) { + if (expected_type == codegen->builtin_types.entry_var) { + return val; + } + switch (type_has_one_possible_value(codegen, expected_type)) { + case OnePossibleValueInvalid: + return nullptr; + case OnePossibleValueNo: + break; + case OnePossibleValueYes: + return get_the_one_possible_value(codegen, expected_type); + } + if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->type)) { if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) return nullptr; return const_ptr_pointee_unchecked(codegen, const_val); @@ -8411,19 +9278,19 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal return val; } -static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ZigValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); if (instruction->id == IrInstructionIdReturn) { IrInstructionReturn *ret_inst = (IrInstructionReturn *)instruction; IrInstruction *operand = ret_inst->operand; - if (operand->value.special == ConstValSpecialRuntime) { + if (operand->value->special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, operand->source_node, buf_sprintf("unable to evaluate constant expression")); - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; } - return &operand->value; + return operand->value; } else if (ir_has_side_effects(instruction)) { if (instr_is_comptime(instruction)) { switch (instruction->id) { @@ -8435,12 +9302,12 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec } } if (get_scope_typeof(instruction->scope) != nullptr) { - // doesn't count, it's inside a @typeOf() + // doesn't count, it's inside a @TypeOf() continue; } exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; } } zig_unreachable(); @@ -8454,14 +9321,14 @@ static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *so return true; } -static bool const_val_fits_in_num_lit(ConstExprValue *const_val, ZigType *num_lit_type) { +static bool const_val_fits_in_num_lit(ZigValue *const_val, ZigType *num_lit_type) { return ((num_lit_type->id == ZigTypeIdComptimeFloat && (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) || (num_lit_type->id == ZigTypeIdComptimeInt && (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt))); } -static bool float_has_fraction(ConstExprValue *const_val) { +static bool float_has_fraction(ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { @@ -8489,7 +9356,7 @@ static bool float_has_fraction(ConstExprValue *const_val) { } } -static void float_append_buf(Buf *buf, ConstExprValue *const_val) { +static void float_append_buf(Buf *buf, ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { @@ -8527,7 +9394,7 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { } } -static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { +static void float_init_bigint(BigInt *bigint, ZigValue *const_val) { if (const_val->type->id == ZigTypeIdComptimeFloat) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == ZigTypeIdFloat) { @@ -8574,7 +9441,7 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { } } -static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { +static void float_init_bigfloat(ZigValue *dest_val, BigFloat *bigfloat) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == ZigTypeIdFloat) { @@ -8601,7 +9468,7 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { } } -static void float_init_f16(ConstExprValue *dest_val, float16_t x) { +static void float_init_f16(ZigValue *dest_val, float16_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_16(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { @@ -8626,7 +9493,7 @@ static void float_init_f16(ConstExprValue *dest_val, float16_t x) { } } -static void float_init_f32(ConstExprValue *dest_val, float x) { +static void float_init_f32(ZigValue *dest_val, float x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { @@ -8655,7 +9522,7 @@ static void float_init_f32(ConstExprValue *dest_val, float x) { } } -static void float_init_f64(ConstExprValue *dest_val, double x) { +static void float_init_f64(ZigValue *dest_val, double x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { @@ -8684,7 +9551,7 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { } } -static void float_init_f128(ConstExprValue *dest_val, float128_t x) { +static void float_init_f128(ZigValue *dest_val, float128_t x) { if (dest_val->type->id == ZigTypeIdComptimeFloat) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == ZigTypeIdFloat) { @@ -8717,7 +9584,7 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { } } -static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) { +static void float_init_float(ZigValue *dest_val, ZigValue *src_val) { if (src_val->type->id == ZigTypeIdComptimeFloat) { float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == ZigTypeIdFloat) { @@ -8742,7 +9609,7 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) } } -static bool float_is_nan(ConstExprValue *op) { +static bool float_is_nan(ZigValue *op) { if (op->type->id == ZigTypeIdComptimeFloat) { return bigfloat_is_nan(&op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { @@ -8763,53 +9630,60 @@ static bool float_is_nan(ConstExprValue *op) { } } -static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { - assert(op1->type == op2->type); - if (op1->type->id == ZigTypeIdComptimeFloat) { - return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { - return CmpLT; - } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { - return CmpGT; - } else { - return CmpEQ; - } - case 32: - if (op1->data.x_f32 > op2->data.x_f32) { - return CmpGT; - } else if (op1->data.x_f32 < op2->data.x_f32) { - return CmpLT; - } else { - return CmpEQ; - } - case 64: - if (op1->data.x_f64 > op2->data.x_f64) { - return CmpGT; - } else if (op1->data.x_f64 < op2->data.x_f64) { - return CmpLT; - } else { - return CmpEQ; - } - case 128: - if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { - return CmpLT; - } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { - return CmpEQ; - } else { - return CmpGT; - } - default: - zig_unreachable(); +static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { + if (op1->type == op2->type) { + if (op1->type->id == ZigTypeIdComptimeFloat) { + return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { + return CmpLT; + } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } + case 32: + if (op1->data.x_f32 > op2->data.x_f32) { + return CmpGT; + } else if (op1->data.x_f32 < op2->data.x_f32) { + return CmpLT; + } else { + return CmpEQ; + } + case 64: + if (op1->data.x_f64 > op2->data.x_f64) { + return CmpGT; + } else if (op1->data.x_f64 < op2->data.x_f64) { + return CmpLT; + } else { + return CmpEQ; + } + case 128: + if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { + return CmpLT; + } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { + return CmpEQ; + } else { + return CmpGT; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); } - } else { - zig_unreachable(); } + BigFloat op1_big; + BigFloat op2_big; + float_init_bigfloat(op1, &op1_big); + float_init_bigfloat(op2, &op2_big); + return bigfloat_cmp(&op1_big, &op2_big); } -static Cmp float_cmp_zero(ConstExprValue *op) { +// This function cannot handle NaN +static Cmp float_cmp_zero(ZigValue *op) { if (op->type->id == ZigTypeIdComptimeFloat) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == ZigTypeIdFloat) { @@ -8859,7 +9733,7 @@ static Cmp float_cmp_zero(ConstExprValue *op) { } } -static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_add(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -8886,7 +9760,7 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } -static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_sub(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -8913,7 +9787,7 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } -static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_mul(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -8940,7 +9814,7 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } -static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_div(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -8967,7 +9841,7 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } -static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_div_trunc(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -8996,7 +9870,7 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE } } -static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_div_floor(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -9025,7 +9899,7 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE } } -static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -9070,7 +9944,7 @@ static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* f128M_sub(a, c, c); } -static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { +static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; if (op1->type->id == ZigTypeIdComptimeFloat) { @@ -9097,7 +9971,7 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } -static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { +static void float_negate(ZigValue *out_val, ZigValue *op) { out_val->type = op->type; if (op->type->id == ZigTypeIdComptimeFloat) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); @@ -9128,7 +10002,7 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { } } -void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { +void float_write_ieee597(ZigValue *op, uint8_t *buf, bool is_big_endian) { if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: @@ -9151,7 +10025,7 @@ void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { } } -void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { +void float_read_ieee597(ZigValue *val, uint8_t *buf, bool is_big_endian) { if (val->type->id == ZigTypeIdFloat) { switch (val->type->data.floating.bit_count) { case 16: @@ -9181,7 +10055,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc return false; } - ConstExprValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); + ZigValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); if (const_val == nullptr) return false; @@ -9447,7 +10321,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc } ir_add_error(ira, instruction, - buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", + buf_sprintf("%s value %s cannot be coerced to type '%s'", num_lit_str, buf_ptr(val_buf), buf_ptr(&other_type->name))); @@ -9470,6 +10344,14 @@ static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) { } } +static ErrorTableEntry *better_documented_error(ErrorTableEntry *preferred, ErrorTableEntry *other) { + if (preferred->decl_node->type == NodeTypeErrorSetField) + return preferred; + if (other->decl_node->type == NodeTypeErrorSetField) + return other; + return preferred; +} + static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2, AstNode *source_node) { @@ -9488,7 +10370,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp if (type_is_global_error_set(set2)) { return set1; } - ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); + size_t errors_count = ira->codegen->errors_by_index.length; + ErrorTableEntry **errors = allocate(errors_count, "ErrorTableEntry *"); populate_error_set_table(errors, set1); ZigList intersection_list = {}; @@ -9496,15 +10379,20 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); + bool need_comma = false; for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; ErrorTableEntry *existing_entry = errors[error_entry->value]; if (existing_entry != nullptr) { - intersection_list.append(existing_entry); - buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name)); + // prefer the one with docs + const char *comma = need_comma ? "," : ""; + need_comma = true; + ErrorTableEntry *existing_entry_with_docs = better_documented_error(existing_entry, error_entry); + intersection_list.append(existing_entry_with_docs); + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); } } - free(errors); + deallocate(errors, errors_count, "ErrorTableEntry *"); err_set_type->data.error_set.err_count = intersection_list.length; err_set_type->data.error_set.errors = intersection_list.items; @@ -9537,6 +10425,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted // alignment can be decreased // bit offset attributes must match exactly // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one + // sentinel-terminated pointers can coerce into PtrLenUnknown ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); ZigType *actual_ptr_type = get_src_ptr_type(actual_type); bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); @@ -9548,6 +10437,35 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted bool actual_opt_or_ptr = actual_ptr_type != nullptr && (actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional); if (wanted_opt_or_ptr && actual_opt_or_ptr) { + bool ok_null_term_ptrs = + wanted_ptr_type->data.pointer.sentinel == nullptr || + (actual_ptr_type->data.pointer.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, + actual_ptr_type->data.pointer.sentinel)); + if (!ok_null_term_ptrs) { + result.id = ConstCastResultIdPtrSentinel; + result.data.bad_ptr_sentinel = allocate_nonzero(1); + result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; + result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; + return result; + } + bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; + if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr)) { + result.id = ConstCastResultIdPtrLens; + return result; + } + + bool ok_cv_qualifiers = + (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile); + if (!ok_cv_qualifiers) { + result.id = ConstCastResultIdCV; + result.data.bad_cv = allocate_nonzero(1); + result.data.bad_cv->wanted_type = wanted_ptr_type; + result.data.bad_cv->actual_type = actual_ptr_type; + return result; + } + ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id == ConstCastResultIdInvalid) @@ -9586,11 +10504,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted result.id = ConstCastResultIdInvalid; return result; } - bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; - if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) && - type_has_bits(wanted_type) == type_has_bits(actual_type) && - (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && + if (type_has_bits(wanted_type) == type_has_bits(actual_type) && actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type)) @@ -9599,10 +10513,40 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted } } + // arrays + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdArray && + wanted_type->data.array.len == actual_type->data.array.len) + { + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.array.child_type, + actual_type->data.array.child_type, source_node, wanted_is_mutable); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdArrayChild; + result.data.array_mismatch = allocate_nonzero(1); + result.data.array_mismatch->child = child; + result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type; + result.data.array_mismatch->actual_child = actual_type->data.array.child_type; + return result; + } + bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) || + (actual_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel)); + if (!ok_null_terminated) { + result.id = ConstCastResultIdSentinelArrays; + result.data.sentinel_arrays = allocate_nonzero(1); + result.data.sentinel_arrays->child = child; + result.data.sentinel_arrays->wanted_type = wanted_type; + result.data.sentinel_arrays->actual_type = actual_type; + return result; + } + return result; + } + // slice const if (is_slice(wanted_type) && is_slice(actual_type)) { - ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index]->type_entry; + ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index]->type_entry; if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; @@ -9611,6 +10555,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted result.id = ConstCastResultIdInvalid; return result; } + bool ok_sentinels = + wanted_ptr_type->data.pointer.sentinel == nullptr || + (actual_ptr_type->data.pointer.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, + actual_ptr_type->data.pointer.sentinel)); + if (!ok_sentinels) { + result.id = ConstCastResultIdPtrSentinel; + result.data.bad_ptr_sentinel = allocate_nonzero(1); + result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; + result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; + return result; + } if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && @@ -9683,7 +10639,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted ZigType *container_set = wanted_type; // if the container set is inferred, then this will always work. - if (container_set->data.error_set.infer_fn != nullptr) { + if (container_set->data.error_set.infer_fn != nullptr && container_set->data.error_set.incomplete) { return result; } // if the container set is the global one, it will always work. @@ -9701,7 +10657,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted return result; } - ErrorTableEntry **errors = allocate(g->errors_by_index.length); + size_t errors_count = g->errors_by_index.length; + ErrorTableEntry **errors = allocate(errors_count, "ErrorTableEntry *"); for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); @@ -9718,7 +10675,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted result.data.error_set_mismatch->missing_errors.append(contained_error_entry); } } - free(errors); + deallocate(errors, errors_count, "ErrorTableEntry *"); return result; } @@ -9795,6 +10752,14 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted return result; } + if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) { + result.id = ConstCastResultIdIntShorten; + result.data.int_shorten = allocate_nonzero(1); + result.data.int_shorten->wanted_type = wanted_type; + result.data.int_shorten->actual_type = actual_type; + return result; + } + result.id = ConstCastResultIdType; result.data.type_mismatch = allocate_nonzero(1); result.data.type_mismatch->wanted_type = wanted_type; @@ -9817,13 +10782,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT size_t i = 0; for (;;) { prev_inst = instructions[i]; - if (type_is_invalid(prev_inst->value.type)) { + if (type_is_invalid(prev_inst->value->type)) { return ira->codegen->builtin_types.entry_invalid; } - if (prev_inst->value.type->id == ZigTypeIdUnreachable) { + if (prev_inst->value->type->id == ZigTypeIdUnreachable) { i += 1; if (i == instruction_count) { - return prev_inst->value.type; + return prev_inst->value->type; } continue; } @@ -9832,14 +10797,14 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT ErrorTableEntry **errors = nullptr; size_t errors_count = 0; ZigType *err_set_type = nullptr; - if (prev_inst->value.type->id == ZigTypeIdErrorSet) { - if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) { + if (prev_inst->value->type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value->type, prev_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(prev_inst->value.type)) { + if (type_is_global_error_set(prev_inst->value->type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { - err_set_type = prev_inst->value.type; + err_set_type = prev_inst->value->type; update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { @@ -9850,12 +10815,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } } - bool any_are_null = (prev_inst->value.type->id == ZigTypeIdNull); + bool any_are_null = (prev_inst->value->type->id == ZigTypeIdNull); bool convert_to_const_slice = false; for (; i < instruction_count; i += 1) { IrInstruction *cur_inst = instructions[i]; - ZigType *cur_type = cur_inst->value.type; - ZigType *prev_type = prev_inst->value.type; + ZigType *cur_type = cur_inst->value->type; + ZigType *prev_type = prev_inst->value->type; if (type_is_invalid(cur_type)) { return cur_type; @@ -9880,10 +10845,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && + cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(cur_type)) { + if (!allow_infer && type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; @@ -9937,7 +10904,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } // neither of them are supersets. so we invent a new error set type that is a union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type); + err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type, nullptr); assert(errors != nullptr); continue; } else if (cur_type->id == ZigTypeIdErrorUnion) { @@ -9946,10 +10913,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT continue; } ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { + bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(cur_err_set_type)) { + if (!allow_infer && type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; @@ -9988,7 +10957,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } // not a subset. invent new error set type, union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type); + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type, nullptr); prev_inst = cur_inst; assert(errors != nullptr); continue; @@ -9999,13 +10968,12 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } if (cur_type->id == ZigTypeIdErrorSet) { - if (prev_type->id == ZigTypeIdArray) { - convert_to_const_slice = true; - } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && + cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(cur_type)) { + if (!allow_infer && type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } @@ -10016,11 +10984,26 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT update_errors_helper(ira->codegen, &errors, &errors_count); if (err_set_type == nullptr) { + bool allow_infer = false; if (prev_type->id == ZigTypeIdErrorUnion) { err_set_type = prev_type->data.error_union.err_set_type; + allow_infer = err_set_type->data.error_set.infer_fn != nullptr && + err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; } else { err_set_type = cur_type; } + + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!allow_infer && type_is_global_error_set(err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); @@ -10044,7 +11027,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT continue; } // not a subset. invent new error set type, union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type); + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type, nullptr); assert(errors != nullptr); continue; } @@ -10068,15 +11051,22 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_err_set_type == cur_err_set_type) continue; - if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) { + bool allow_infer_prev = prev_err_set_type->data.error_set.infer_fn != nullptr && + prev_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + bool allow_infer_cur = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + + if (!allow_infer_prev && !resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { + if (!allow_infer_cur && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) { + if ((!allow_infer_prev && type_is_global_error_set(prev_err_set_type)) || + (!allow_infer_cur && type_is_global_error_set(cur_err_set_type))) + { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } @@ -10130,7 +11120,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT continue; } - err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type); + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type, nullptr); continue; } } @@ -10147,20 +11137,20 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) { - TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value.data.x_enum_literal); + TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value->data.x_enum_literal); if (field != nullptr) { continue; } } if (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) { - TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value.data.x_enum_literal); + TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value->data.x_enum_literal); if (field != nullptr) { continue; } } if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) { - TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value.data.x_enum_literal); + TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value->data.x_enum_literal); if (field != nullptr) { prev_inst = cur_inst; continue; @@ -10168,7 +11158,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } if (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) { - TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value.data.x_enum_literal); + TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value->data.x_enum_literal); if (field != nullptr) { prev_inst = cur_inst; continue; @@ -10245,10 +11235,14 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT { if (err_set_type != nullptr) { ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { + bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) { + if ((!allow_infer && type_is_global_error_set(cur_err_set_type)) || + type_is_global_error_set(err_set_type)) + { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; @@ -10256,7 +11250,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT update_errors_helper(ira->codegen, &errors, &errors_count); - err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type); + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type, nullptr); } prev_inst = cur_inst; continue; @@ -10324,45 +11318,93 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } } - if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray && - cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) + // *[N]T to []T + // *[N]T to E![]T + if (cur_type->id == ZigTypeIdPointer && + cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) || + is_slice(prev_type))) { - convert_to_const_slice = true; - prev_inst = cur_inst; - continue; + ZigType *array_type = cur_type->data.pointer.child_type; + ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ? + prev_type->data.error_union.payload_type : prev_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !cur_type->data.pointer.is_const) && + types_match_const_cast_only(ira, + slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + convert_to_const_slice = false; + continue; + } } - if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray && - cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) + // *[N]T to []T + // *[N]T to E![]T + if (prev_type->id == ZigTypeIdPointer && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + prev_type->data.pointer.ptr_len == PtrLenSingle && + ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || + is_slice(cur_type))) { + ZigType *array_type = prev_type->data.pointer.child_type; + ZigType *slice_type = (cur_type->id == ZigTypeIdErrorUnion) ? + cur_type->data.error_union.payload_type : cur_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !prev_type->data.pointer.is_const) && + types_match_const_cast_only(ira, + slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; + convert_to_const_slice = false; + continue; + } + } + + // *[N]T and *[M]T + if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + (cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const || + prev_type->data.pointer.child_type->data.array.len == 0) && + ( + prev_type->data.pointer.child_type->data.array.sentinel == nullptr || + (cur_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel, + cur_type->data.pointer.child_type->data.array.sentinel)) + ) && + types_match_const_cast_only(ira, + cur_type->data.pointer.child_type->data.array.child_type, + prev_type->data.pointer.child_type->data.array.child_type, + source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; convert_to_const_slice = true; continue; } - - if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) && - (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || - cur_type->data.array.len == 0) && + if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + (prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const || + cur_type->data.pointer.child_type->data.array.len == 0) && + ( + cur_type->data.pointer.child_type->data.array.sentinel == nullptr || + (prev_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel, + prev_type->data.pointer.child_type->data.array.sentinel)) + ) && types_match_const_cast_only(ira, - prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + prev_type->data.pointer.child_type->data.array.child_type, + cur_type->data.pointer.child_type->data.array.child_type, + source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) { - convert_to_const_slice = false; - continue; - } - - if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) && - (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || - prev_type->data.array.len == 0) && - types_match_const_cast_only(ira, - cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - prev_inst = cur_inst; - convert_to_const_slice = false; + convert_to_const_slice = true; continue; } @@ -10401,22 +11443,29 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT free(errors); if (convert_to_const_slice) { - assert(prev_inst->value.type->id == ZigTypeIdArray); - ZigType *ptr_type = get_pointer_to_type_extra( - ira->codegen, prev_inst->value.type->data.array.child_type, - true, false, PtrLenUnknown, - 0, 0, 0, false); - ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); - if (err_set_type != nullptr) { - return get_error_union_type(ira->codegen, err_set_type, slice_type); + if (prev_inst->value->type->id == ZigTypeIdPointer) { + ZigType *array_type = prev_inst->value->type->data.pointer.child_type; + src_assert(array_type->id == ZigTypeIdArray, source_node); + ZigType *ptr_type = get_pointer_to_type_extra2( + ira->codegen, array_type->data.array.child_type, + prev_inst->value->type->data.pointer.is_const, false, + PtrLenUnknown, + 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel); + ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); + if (err_set_type != nullptr) { + return get_error_union_type(ira->codegen, err_set_type, slice_type); + } else { + return slice_type; + } } else { - return slice_type; + zig_unreachable(); } } else if (err_set_type != nullptr) { - if (prev_inst->value.type->id == ZigTypeIdErrorSet) { + if (prev_inst->value->type->id == ZigTypeIdErrorSet) { return err_set_type; - } else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = prev_inst->value.type->data.error_union.payload_type; + } else if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { + ZigType *payload_type = prev_inst->value->type->data.error_union.payload_type; if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; return get_error_union_type(ira->codegen, err_set_type, payload_type); @@ -10426,61 +11475,39 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT return ira->codegen->builtin_types.entry_invalid; return get_error_union_type(ira->codegen, err_set_type, payload_type); } else { - if (prev_inst->value.type->id == ZigTypeIdComptimeInt || - prev_inst->value.type->id == ZigTypeIdComptimeFloat) + if (prev_inst->value->type->id == ZigTypeIdComptimeInt || + prev_inst->value->type->id == ZigTypeIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == ZigTypeIdNull) { + } else if (prev_inst->value->type->id == ZigTypeIdNull) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; } else { - if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown))) + if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value->type); } } - } else if (any_are_null && prev_inst->value.type->id != ZigTypeIdNull) { - if (prev_inst->value.type->id == ZigTypeIdComptimeInt || - prev_inst->value.type->id == ZigTypeIdComptimeFloat) - { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make maybe out of number literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == ZigTypeIdOptional) { - return prev_inst->value.type; + } else if (any_are_null && prev_inst->value->type->id != ZigTypeIdNull) { + if (prev_inst->value->type->id == ZigTypeIdOptional) { + return prev_inst->value->type; } else { - if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown))) + if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; - return get_optional_type(ira->codegen, prev_inst->value.type); + return get_optional_type(ira->codegen, prev_inst->value->type); } } else { - return prev_inst->value.type; - } -} - -static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { - ConstGlobalRefs *global_refs = dest->global_refs; - memcpy(dest, src, sizeof(ConstExprValue)); - if (!same_global_refs) { - dest->global_refs = global_refs; - if (src->special != ConstValSpecialStatic) - return; - if (dest->type->id == ZigTypeIdStruct) { - dest->data.x_struct.fields = create_const_vals(dest->type->data.structure.src_field_count); - for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) { - copy_const_val(&dest->data.x_struct.fields[i], &src->data.x_struct.fields[i], false); - } - } + return prev_inst->value->type; } } static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr, CastOp cast_op, - ConstExprValue *other_val, ZigType *other_type, - ConstExprValue *const_val, ZigType *new_type) + ZigValue *other_val, ZigType *other_type, + ZigValue *const_val, ZigType *new_type) { const_val->special = other_val->special; @@ -10491,13 +11518,11 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ case CastOpErrSet: case CastOpBitCast: zig_panic("TODO"); - case CastOpNoop: - { - bool same_global_refs = other_val->special == ConstValSpecialStatic; - copy_const_val(const_val, other_val, same_global_refs); - const_val->type = new_type; - break; - } + case CastOpNoop: { + copy_const_val(const_val, other_val); + const_val->type = new_type; + break; + } case CastOpNumLitToConcrete: if (other_val->type->id == ZigTypeIdComptimeFloat) { assert(new_type->id == ZigTypeIdFloat); @@ -10583,25 +11608,39 @@ static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, Z IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, old_instruction->scope, old_instruction->source_node); IrInstruction *new_instruction = &const_instruction->base; - new_instruction->value.type = ty; - new_instruction->value.special = ConstValSpecialStatic; + new_instruction->value->type = ty; + new_instruction->value->special = ConstValSpecialStatic; return new_instruction; } +static IrInstruction *ir_const_noval(IrAnalyze *ira, IrInstruction *old_instruction) { + IrInstructionConst *const_instruction = ir_create_instruction_noval(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + return &const_instruction->base; +} + +// This function initializes the new IrInstruction with the provided ZigValue, +// rather than creating a new one. +static IrInstruction *ir_const_move(IrAnalyze *ira, IrInstruction *old_instruction, ZigValue *val) { + IrInstruction *result = ir_const_noval(ira, old_instruction); + result->value = val; + return result; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op) { if (instr_is_comptime(value) || !type_has_bits(wanted_type)) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, - &result->value, wanted_type)) + if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, value->value, value->value->type, + result->value, wanted_type)) { return ira->codegen->invalid_instruction; } return result; } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } } @@ -10609,67 +11648,65 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { - assert(value->value.type->id == ZigTypeIdPointer); + assert(value->value->type->id == ZigTypeIdPointer); Error err; - if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, + if ((err = type_resolve(ira->codegen, value->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value->type)); if (instr_is_comptime(value)) { - ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node); + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, value->value, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; - result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; - result->value.data.x_ptr.data.base_array.array_val = pointee; - result->value.data.x_ptr.data.base_array.elem_index = 0; - result->value.data.x_ptr.data.base_array.is_cstr = false; + result->value->data.x_ptr.special = ConstPtrSpecialBaseArray; + result->value->data.x_ptr.mut = value->value->data.x_ptr.mut; + result->value->data.x_ptr.data.base_array.array_val = pointee; + result->value->data.x_ptr.data.base_array.elem_index = 0; return result; } } IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpBitCast); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *value, ZigType *wanted_type, ResultLoc *result_loc) + IrInstruction *array_ptr, ZigType *wanted_type, ResultLoc *result_loc) { Error err; - if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, + if ((err = type_resolve(ira->codegen, array_ptr->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } - wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); + wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, array_ptr->value->type)); - if (instr_is_comptime(value)) { - ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node); + if (instr_is_comptime(array_ptr)) { + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr->value, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - assert(value->value.type->id == ZigTypeIdPointer); - ZigType *array_type = value->value.type->data.pointer.child_type; + assert(array_ptr->value->type->id == ZigTypeIdPointer); + ZigType *array_type = array_ptr->value->type->data.pointer.child_type; assert(is_slice(wanted_type)); - bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; + bool is_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); - result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = - value->value.data.x_ptr.mut; - result->value.type = wanted_type; + init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, is_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr->value->data.x_ptr.mut; + result->value->type = wanted_type; return result; } } @@ -10677,10 +11714,10 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc if (result_loc == nullptr) result_loc = no_result_loc(); IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } - return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, value, result_loc_inst); + return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, array_ptr, result_loc_inst); } static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) { @@ -10723,7 +11760,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction IrSuspendPosition *suspend_pos) { if (ira->codegen->verbose_ir) { - fprintf(stderr, "suspend %s_%zu %s_%zu #%zu (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint, + fprintf(stderr, "suspend %s_%zu %s_%zu #%" PRIu32 " (%zu,%zu)\n", + ira->old_irb.current_basic_block->name_hint, ira->old_irb.current_basic_block->debug_id, ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, @@ -10761,7 +11799,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) { ira->instruction_index = pos.instruction_index; assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); if (ira->codegen->verbose_ir) { - fprintf(stderr, "%s_%zu #%zu\n", ira->old_irb.current_basic_block->name_hint, + fprintf(stderr, "%s_%zu #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint, ira->old_irb.current_basic_block->debug_id, ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id); } @@ -10881,53 +11919,55 @@ static IrInstruction *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruc } static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) { - if (instruction->value.type->id == ZigTypeIdUnreachable) + if (instruction->value->type->id == ZigTypeIdUnreachable) ir_finish_bb(ira); return instruction; } static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); - result->value.data.x_type = ty; + result->value->data.x_type = ty; return result; } static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instruction, bool value) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool); - result->value.data.x_bool = value; + result->value->data.x_bool = value; return result; } static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ty); - result->value.special = ConstValSpecialUndef; + result->value->special = ConstValSpecialUndef; return result; } static IrInstruction *ir_const_unreachable(IrAnalyze *ira, IrInstruction *source_instruction) { - IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_unreachable); - result->value.special = ConstValSpecialStatic; + IrInstruction *result = ir_const_noval(ira, source_instruction); + result->value = ira->codegen->intern.for_unreachable(); return result; } static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) { - return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void); + IrInstruction *result = ir_const_noval(ira, source_instruction); + result->value = ira->codegen->intern.for_void(); + return result; } static IrInstruction *ir_const_unsigned(IrAnalyze *ira, IrInstruction *source_instruction, uint64_t value) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int); - bigint_init_unsigned(&result->value.data.x_bigint, value); + bigint_init_unsigned(&result->value->data.x_bigint, value); return result; } static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction, - ConstExprValue *pointee, ZigType *pointee_type, + ZigValue *pointee, ZigType *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false); IrInstruction *const_instr = ir_const(ira, instruction, ptr_type); - ConstExprValue *const_val = &const_instr->value; + ZigValue *const_val = const_instr->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.mut = ptr_mut; const_val->data.x_ptr.data.ref.pointee = pointee; @@ -10935,7 +11975,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio } static Error ir_resolve_const_val(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, - ConstExprValue *val, UndefAllowed undef_allowed) + ZigValue *val, UndefAllowed undef_allowed) { Error err; for (;;) { @@ -10968,17 +12008,17 @@ static Error ir_resolve_const_val(CodeGen *codegen, IrExecutable *exec, AstNode } } -static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { +static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { Error err; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, value->source_node, - &value->value, undef_allowed))) + value->value, undef_allowed))) { return nullptr; } - return &value->value; + return value->value; } -ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef_allowed) @@ -10986,9 +12026,9 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod Error err; if (expected_type != nullptr && type_is_invalid(expected_type)) - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; - IrExecutable *ir_executable = allocate(1); + IrExecutable *ir_executable = allocate(1, "IrExecutablePass1"); ir_executable->source_node = source_node; ir_executable->parent_exec = parent_exec; ir_executable->name = exec_name; @@ -10996,11 +12036,13 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod ir_executable->fn_entry = fn_entry; ir_executable->c_import_buf = c_import_buf; ir_executable->begin_scope = scope; - ir_gen(codegen, node, scope, ir_executable); + + if (!ir_gen(codegen, node, scope, ir_executable)) + return codegen->invalid_instruction->value; if (ir_executable->first_err_trace_msg != nullptr) { codegen->trace_err = ir_executable->first_err_trace_msg; - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; } if (codegen->verbose_ir) { @@ -11010,7 +12052,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod ir_print(codegen, stderr, ir_executable, 2, IrPassSrc); fprintf(stderr, "}\n"); } - IrExecutable *analyzed_executable = allocate(1); + IrExecutable *analyzed_executable = allocate(1, "IrExecutablePass2"); analyzed_executable->source_node = source_node; analyzed_executable->parent_exec = parent_exec; analyzed_executable->source_exec = ir_executable; @@ -11023,7 +12065,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node); if (type_is_invalid(result_type)) { - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; } if (codegen->verbose_ir) { @@ -11032,27 +12074,27 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod fprintf(stderr, "}\n"); } - ConstExprValue *result = ir_exec_const_result(codegen, analyzed_executable); + ZigValue *result = ir_exec_const_result(codegen, analyzed_executable); if (type_is_invalid(result->type)) - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; if ((err = ir_resolve_const_val(codegen, analyzed_executable, node, result, undef_allowed))) - return &codegen->invalid_instruction->value; + return codegen->invalid_instruction->value; return result; } static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstruction *err_value) { - if (type_is_invalid(err_value->value.type)) + if (type_is_invalid(err_value->value->type)) return nullptr; - if (err_value->value.type->id != ZigTypeIdErrorSet) { + if (err_value->value->type->id != ZigTypeIdErrorSet) { ir_add_error(ira, err_value, - buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value.type->name))); + buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value->type->name))); return nullptr; } - ConstExprValue *const_val = ir_resolve_const(ira, err_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, err_value, UndefBad); if (!const_val) return nullptr; @@ -11061,7 +12103,7 @@ static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstruction *err_valu } static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, - ConstExprValue *val) + ZigValue *val) { Error err; if ((err = ir_resolve_const_val(codegen, exec, source_node, val, UndefBad))) @@ -11071,28 +12113,28 @@ static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutable *exec, AstN return val->data.x_type; } -static ConstExprValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstruction *type_value) { - if (type_is_invalid(type_value->value.type)) +static ZigValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstruction *type_value) { + if (type_is_invalid(type_value->value->type)) return nullptr; - if (type_value->value.type->id != ZigTypeIdMetaType) { + if (type_value->value->type->id != ZigTypeIdMetaType) { ir_add_error(ira, type_value, - buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name))); + buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value->type->name))); return nullptr; } Error err; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, type_value->source_node, - &type_value->value, LazyOk))) + type_value->value, LazyOk))) { return nullptr; } - return &type_value->value; + return type_value->value; } static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { - ConstExprValue *val = ir_resolve_type_lazy(ira, type_value); + ZigValue *val = ir_resolve_type_lazy(ira, type_value); if (val == nullptr) return ira->codegen->builtin_types.entry_invalid; @@ -11141,18 +12183,18 @@ static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstruction *type_value) { } static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) { - if (type_is_invalid(type_value->value.type)) + if (type_is_invalid(type_value->value->type)) return ira->codegen->builtin_types.entry_invalid; - if (type_value->value.type->id != ZigTypeIdMetaType) { + if (type_value->value->type->id != ZigTypeIdMetaType) { ErrorMsg *msg = ir_add_error(ira, type_value, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name))); + buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value->type->name))); add_error_note(ira->codegen, msg, op_source->source_node, buf_sprintf("`||` merges error sets; `or` performs boolean OR")); return ira->codegen->builtin_types.entry_invalid; } - ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, type_value, UndefBad); if (!const_val) return ira->codegen->builtin_types.entry_invalid; @@ -11172,20 +12214,23 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; - if (type_is_invalid(fn_value->value.type)) + if (type_is_invalid(fn_value->value->type)) return nullptr; - if (fn_value->value.type->id != ZigTypeIdFn) { + if (fn_value->value->type->id != ZigTypeIdFn) { ir_add_error_node(ira, fn_value->source_node, - buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value.type->name))); + buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value->type->name))); return nullptr; } - ConstExprValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); if (!const_val) return nullptr; - assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); + // May be a ConstPtrSpecialHardCodedAddr + if (const_val->data.x_ptr.special != ConstPtrSpecialFunction) + return nullptr; + return const_val->data.x_ptr.data.fn.fn_entry; } @@ -11197,22 +12242,22 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so if (instr_is_comptime(value)) { ZigType *payload_type = wanted_type->data.maybe.child_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); - if (type_is_invalid(casted_payload->value.type)) + if (type_is_invalid(casted_payload->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk); + ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.special = ConstValSpecialStatic; - if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { - copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); + const_instruction->base.value->special = ConstValSpecialStatic; + if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) { + copy_const_val(const_instruction->base.value, val); } else { - const_instruction->base.value.data.x_optional = val; + const_instruction->base.value->data.x_optional = val; } - const_instruction->base.value.type = wanted_type; + const_instruction->base.value->type = wanted_type; return &const_instruction->base; } @@ -11222,12 +12267,12 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so IrInstruction *result_loc_inst = nullptr; if (result_loc != nullptr) { result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } } IrInstruction *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst); - result->value.data.rh_maybe = RuntimeHintOptionalNonNull; + result->value->data.rh_maybe = RuntimeHintOptionalNonNull; return result; } @@ -11240,24 +12285,24 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction ZigType *err_set_type = wanted_type->data.error_union.err_set_type; if (instr_is_comptime(value)) { IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); - if (type_is_invalid(casted_payload->value.type)) + if (type_is_invalid(casted_payload->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); + ZigValue *val = ir_resolve_const(ira, casted_payload, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *err_set_val = create_const_vals(1); + ZigValue *err_set_val = create_const_vals(1); err_set_val->type = err_set_type; err_set_val->special = ConstValSpecialStatic; err_set_val->data.x_err_set = nullptr; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.error_set = err_set_val; - const_instruction->base.value.data.x_err_union.payload = val; + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_union.error_set = err_set_val; + const_instruction->base.value->data.x_err_union.payload = val; return &const_instruction->base; } @@ -11265,7 +12310,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (handle_is_ptr(wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } } else { @@ -11273,18 +12318,18 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction } IrInstruction *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst); - result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; + result->value->data.rh_error_union = RuntimeHintErrorUnionNonError; return result; } static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { - assert(value->value.type->id == ZigTypeIdErrorSet); + assert(value->value->type->id == ZigTypeIdErrorSet); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; @@ -11309,14 +12354,14 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_set = val->data.x_err_set; + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_set = val->data.x_err_set; return &const_instruction->base; } IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11324,7 +12369,7 @@ static IrInstruction *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInstruc IrInstruction *frame_ptr, ZigType *wanted_type) { if (instr_is_comptime(frame_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad); if (ptr_val == nullptr) return ira->codegen->invalid_instruction; @@ -11336,7 +12381,7 @@ static IrInstruction *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInstruc IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, frame_ptr, CastOpBitCast); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11349,7 +12394,7 @@ static IrInstruction *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpBitCast); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11362,21 +12407,21 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); if (instr_is_comptime(casted_value)) { - ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *err_set_val = create_const_vals(1); + ZigValue *err_set_val = create_const_vals(1); err_set_val->special = ConstValSpecialStatic; err_set_val->type = wanted_type->data.error_union.err_set_type; err_set_val->data.x_err_set = val->data.x_err_set; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.error_set = err_set_val; - const_instruction->base.value.data.x_err_union.payload = nullptr; + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_union.error_set = err_set_val; + const_instruction->base.value->data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -11384,7 +12429,7 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (handle_is_ptr(wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } } else { @@ -11393,7 +12438,7 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst); - result->value.data.rh_error_union = RuntimeHintErrorUnionError; + result->value->data.rh_error_union = RuntimeHintErrorUnionError; return result; } @@ -11401,17 +12446,17 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so assert(wanted_type->id == ZigTypeIdOptional); assert(instr_is_comptime(value)); - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); assert(val != nullptr); IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialStatic; + result->value->special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - result->value.data.x_ptr.special = ConstPtrSpecialNull; + result->value->data.x_ptr.special = ConstPtrSpecialNull; } else if (is_opt_err_set(wanted_type)) { - result->value.data.x_err_set = nullptr; + result->value->data.x_err_set = nullptr; } else { - result->value.data.x_optional = nullptr; + result->value->data.x_optional = nullptr; } return result; } @@ -11423,12 +12468,12 @@ static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction assert(wanted_type->data.pointer.ptr_len == PtrLenC); assert(instr_is_comptime(value)); - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); assert(val != nullptr); IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.data.x_ptr.special = ConstPtrSpecialNull; - result->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + result->value->data.x_ptr.special = ConstPtrSpecialNull; + result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; return result; } @@ -11437,85 +12482,36 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi { Error err; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown))) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, LazyOk); + ZigValue *val = ir_resolve_const(ira, value, LazyOk); if (!val) return ira->codegen->invalid_instruction; - return ir_get_const_ptr(ira, source_instruction, val, value->value.type, + return ir_get_const_ptr(ira, source_instruction, val, value->value->type, ConstPtrMutComptimeConst, is_const, is_volatile, 0); } - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value->type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); if ((err = type_resolve(ira->codegen, ptr_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; IrInstruction *result_loc; - if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { - result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value.type, nullptr, true, + if (type_has_bits(ptr_type) && !handle_is_ptr(value->value->type)) { + result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value->type, nullptr, true, false, true); } else { result_loc = nullptr; } IrInstruction *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc); - new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; + new_instruction->value->data.rh_ptr = RuntimeHintPtrStack; return new_instruction; } -static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *array_arg, ZigType *wanted_type, ResultLoc *result_loc) -{ - assert(is_slice(wanted_type)); - // In this function we honor the const-ness of wanted_type, because - // we may be casting [0]T to []const T which is perfectly valid. - - IrInstruction *array_ptr = nullptr; - IrInstruction *array; - if (array_arg->value.type->id == ZigTypeIdPointer) { - array = ir_get_deref(ira, source_instr, array_arg, nullptr); - array_ptr = array_arg; - } else { - array = array_arg; - } - ZigType *array_type = array->value.type; - assert(array_type->id == ZigTypeIdArray); - - if (instr_is_comptime(array) || array_type->data.array.len == 0) { - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); - result->value.type = wanted_type; - return result; - } - - IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); - init_const_usize(ira->codegen, &start->value, 0); - - IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); - init_const_usize(ira->codegen, &end->value, array_type->data.array.len); - - if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); - - if (result_loc == nullptr) result_loc = no_result_loc(); - IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, - true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { - return result_loc_inst; - } - IrInstruction *result = ir_build_slice_gen(ira, source_instr, wanted_type, array_ptr, start, end, false, result_loc_inst); - result->value.data.rh_slice.id = RuntimeHintSliceIdLen; - result->value.data.rh_slice.len = array_type->data.array.len; - - return result; -} - static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) { assert(union_type->id == ZigTypeIdUnion); @@ -11540,19 +12536,19 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour IrInstruction *enum_target; ZigType *enum_type; - if (target->value.type->id == ZigTypeIdUnion) { - enum_type = ir_resolve_union_tag_type(ira, target, target->value.type); + if (target->value->type->id == ZigTypeIdUnion) { + enum_type = ir_resolve_union_tag_type(ira, target, target->value->type); if (type_is_invalid(enum_type)) return ira->codegen->invalid_instruction; enum_target = ir_implicit_cast(ira, target, enum_type); - if (type_is_invalid(enum_target->value.type)) + if (type_is_invalid(enum_target->value->type)) return ira->codegen->invalid_instruction; - } else if (target->value.type->id == ZigTypeIdEnum) { + } else if (target->value->type->id == ZigTypeIdEnum) { enum_target = target; - enum_type = target->value.type; + enum_type = target->value->type; } else { ir_add_error(ira, target, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } @@ -11567,41 +12563,41 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour enum_type->data.enumeration.src_field_count == 1) { IrInstruction *result = ir_const(ira, source_instr, tag_type); - init_const_bigint(&result->value, tag_type, + init_const_bigint(result->value, tag_type, &enum_type->data.enumeration.fields[0].value); return result; } if (instr_is_comptime(enum_target)) { - ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad); + ZigValue *val = ir_resolve_const(ira, enum_target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, source_instr, tag_type); - init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag); + init_const_bigint(result->value, tag_type, &val->data.x_enum_tag); return result; } IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, source_instr->source_node, enum_target); - result->value.type = tag_type; + result->value->type = tag_type; return result; } static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - assert(target->value.type->id == ZigTypeIdUnion); + assert(target->value->type->id == ZigTypeIdUnion); assert(wanted_type->id == ZigTypeIdEnum); - assert(wanted_type == target->value.type->data.unionation.tag_type); + assert(wanted_type == target->value->type->data.unionation.tag_type); if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialStatic; - result->value.type = wanted_type; - bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_union.tag); return result; } @@ -11610,16 +12606,16 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou wanted_type->data.enumeration.src_field_count == 1) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialStatic; - result->value.type = wanted_type; - TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; - bigint_init_bigint(&result->value.data.x_enum_tag, &enum_field->value); + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + TypeEnumField *enum_field = target->value->type->data.unionation.fields[0].enum_field; + bigint_init_bigint(&result->value->data.x_enum_tag, &enum_field->value); return result; } IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, target); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11627,7 +12623,7 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc IrInstruction *target, ZigType *wanted_type) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialUndef; + result->value->special = ConstValSpecialUndef; return result; } @@ -11641,11 +12637,11 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so return ira->codegen->invalid_instruction; IrInstruction *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type); - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); @@ -11676,12 +12672,12 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so } IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialStatic; - result->value.type = wanted_type; - bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag); - result->value.data.x_union.payload = create_const_vals(1); - result->value.data.x_union.payload->special = ConstValSpecialStatic; - result->value.data.x_union.payload->type = field_type; + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + bigint_init_bigint(&result->value->data.x_union.tag, &val->data.x_enum_tag); + result->value->data.x_union.payload = create_const_vals(1); + result->value->data.x_union.payload->special = ConstValSpecialStatic; + result->value->data.x_union.payload->type = field_type; return result; } @@ -11689,7 +12685,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so // and in fact it's a noop cast because the union value is just the enum value if (wanted_type->data.unionation.gen_field_count == 0) { IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11701,9 +12697,10 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); if (field_type == nullptr) return ira->codegen->invalid_instruction; - if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) + bool has_bits; + if ((err = type_has_bits2(ira->codegen, field_type, &has_bits))) return ira->codegen->invalid_instruction; - if (type_has_bits(field_type)) { + if (has_bits) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); add_error_note(ira->codegen, msg, field_node, buf_sprintf("field '%s' has type '%s'", @@ -11720,7 +12717,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (wanted_type->id == ZigTypeIdInt) { @@ -11734,16 +12731,16 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction { ir_add_error(ira, source_instr, buf_sprintf("cast from '%s' to '%s' truncates bits", - buf_ptr(&target->value.type->name), buf_ptr(&wanted_type->name))); + buf_ptr(&target->value->type->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } } IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.type = wanted_type; + result->value->type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { - bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); + bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); } else { - float_init_float(&result->value, val); + float_init_float(result->value, val); } return result; } @@ -11753,16 +12750,16 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction // the target is zero. if (!type_has_bits(wanted_type)) { assert(wanted_type->id == ZigTypeIdInt); - assert(type_has_bits(target->value.type)); + assert(type_has_bits(target->value->type)); ir_build_assert_zero(ira, source_instr, target); IrInstruction *result = ir_const_unsigned(ira, source_instr, 0); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, source_instr->source_node, target); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11772,7 +12769,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour Error err; assert(wanted_type->id == ZigTypeIdEnum); - ZigType *actual_type = target->value.type; + ZigType *actual_type = target->value->type; if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; @@ -11788,12 +12785,12 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt); if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); - if (field == nullptr) { + if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -11805,28 +12802,28 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour } IrInstruction *result = ir_const(ira, source_instr, wanted_type); - bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); + bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_bigint); return result; } IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope, source_instr->source_node, nullptr, target); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { - float_init_float(&result->value, val); + float_init_float(result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { - bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); + bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); } else { zig_unreachable(); } @@ -11836,12 +12833,12 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - assert(target->value.type->id == ZigTypeIdInt); - assert(!target->value.type->data.integral.is_signed); + assert(target->value->type->id == ZigTypeIdInt); + assert(!target->value->type->data.integral.is_signed); assert(wanted_type->id == ZigTypeIdErrorSet); if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; @@ -11864,7 +12861,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc } size_t index = bigint_as_usize(&val->data.x_bigint); - result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); + result->value->data.x_err_set = ira->codegen->errors_by_index.at(index); return result; } else { ErrorTableEntry *err = nullptr; @@ -11887,13 +12884,13 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } - result->value.data.x_err_set = err; + result->value->data.x_err_set = err; return result; } } IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11902,10 +12899,10 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc { assert(wanted_type->id == ZigTypeIdInt); - ZigType *err_type = target->value.type; + ZigType *err_type = target->value->type; if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; @@ -11919,11 +12916,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } else { zig_unreachable(); } - result->value.type = wanted_type; + result->value->type = wanted_type; uint64_t err_value = err ? err->value : 0; - bigint_init_unsigned(&result->value.data.x_bigint, err_value); + bigint_init_unsigned(&result->value->data.x_bigint, err_value); - if (!bigint_fits_in_bits(&result->value.data.x_bigint, + if (!bigint_fits_in_bits(&result->value->data.x_bigint, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error_node(ira, source_instr->source_node, @@ -11949,12 +12946,12 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } if (err_set_type->data.error_set.err_count == 0) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - bigint_init_unsigned(&result->value.data.x_bigint, 0); + bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; - bigint_init_unsigned(&result->value.data.x_bigint, err->value); + bigint_init_unsigned(&result->value->data.x_bigint, err->value); return result; } } @@ -11968,7 +12965,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -11977,25 +12974,25 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou { assert(wanted_type->id == ZigTypeIdPointer); Error err; - if ((err = type_resolve(ira->codegen, target->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + if ((err = type_resolve(ira->codegen, target->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; - assert((wanted_type->data.pointer.is_const && target->value.type->data.pointer.is_const) || !target->value.type->data.pointer.is_const); - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type)); + assert((wanted_type->data.pointer.is_const && target->value->type->data.pointer.is_const) || !target->value->type->data.pointer.is_const); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value->type)); ZigType *array_type = wanted_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); assert(array_type->data.array.len == 1); if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; assert(val->type->id == ZigTypeIdPointer); - ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - ConstExprValue *array_val = create_const_vals(1); + ZigValue *array_val = create_const_vals(1); array_val->special = ConstValSpecialStatic; array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; @@ -12005,11 +13002,11 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialRef; - const_instruction->base.value.data.x_ptr.data.ref.pointee = array_val; - const_instruction->base.value.data.x_ptr.mut = val->data.x_ptr.mut; + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_ptr.special = ConstPtrSpecialRef; + const_instruction->base.value->data.x_ptr.data.ref.pointee = array_val; + const_instruction->base.value->data.x_ptr.mut = val->data.x_ptr.mut; return &const_instruction->base; } } @@ -12017,7 +13014,7 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou // pointer to array and pointer to single item are represented the same way at runtime IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpBitCast); - result->value.type = wanted_type; + result->value->type = wanted_type; return result; } @@ -12049,7 +13046,7 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa ZigList *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; for (size_t i = 0; i < missing_errors->length; i += 1) { ErrorTableEntry *error_entry = missing_errors->at(i); - add_error_note(ira->codegen, parent_msg, error_entry->decl_node, + add_error_note(ira->codegen, parent_msg, ast_field_to_symbol_node(error_entry->decl_node), buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); } break; @@ -12125,6 +13122,55 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa } break; } + case ConstCastResultIdPtrLens: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer length mismatch")); + break; + } + case ConstCastResultIdPtrSentinel: { + ZigType *actual_type = cast_result->data.bad_ptr_sentinel->actual_type; + ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type; + { + Buf *txt_msg = buf_sprintf("destination pointer requires a terminating '"); + render_const_value(ira->codegen, txt_msg, wanted_type->data.pointer.sentinel); + buf_appendf(txt_msg, "' sentinel"); + if (actual_type->data.pointer.sentinel != nullptr) { + buf_appendf(txt_msg, ", but source pointer has a terminating '"); + render_const_value(ira->codegen, txt_msg, actual_type->data.pointer.sentinel); + buf_appendf(txt_msg, "' sentinel"); + } + add_error_note(ira->codegen, parent_msg, source_node, txt_msg); + } + break; + } + case ConstCastResultIdSentinelArrays: { + ZigType *actual_type = cast_result->data.sentinel_arrays->actual_type; + ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type; + Buf *txt_msg = buf_sprintf("destination array requires a terminating '"); + render_const_value(ira->codegen, txt_msg, wanted_type->data.array.sentinel); + buf_appendf(txt_msg, "' sentinel"); + if (actual_type->data.array.sentinel != nullptr) { + buf_appendf(txt_msg, ", but source array has a terminating '"); + render_const_value(ira->codegen, txt_msg, actual_type->data.array.sentinel); + buf_appendf(txt_msg, "' sentinel"); + } + add_error_note(ira->codegen, parent_msg, source_node, txt_msg); + break; + } + case ConstCastResultIdCV: { + ZigType *wanted_type = cast_result->data.bad_cv->wanted_type; + ZigType *actual_type = cast_result->data.bad_cv->actual_type; + bool ok_const = !actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const; + bool ok_volatile = !actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile; + if (!ok_const) { + add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards const qualifier")); + } else if (!ok_volatile) { + add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards volatile qualifier")); + } else { + zig_unreachable(); + } + break; + } case ConstCastResultIdFnIsGeneric: add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("only one of the functions is generic")); @@ -12133,6 +13179,17 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("calling convention mismatch")); break; + case ConstCastResultIdIntShorten: { + ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; + ZigType *actual_type = cast_result->data.int_shorten->actual_type; + const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; + const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned"; + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", + wanted_signed, wanted_type->data.integral.bit_count, + actual_signed, actual_type->data.integral.bit_count)); + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnVarArgs: // TODO case ConstCastResultIdFnReturnType: // TODO @@ -12141,6 +13198,7 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa case ConstCastResultIdFnArgNoAlias: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO + case ConstCastResultIdArrayChild: // TODO break; } } @@ -12149,10 +13207,10 @@ static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction * IrInstruction *array, ZigType *vector_type) { if (instr_is_comptime(array)) { - // arrays and vectors have the same ConstExprValue representation + // arrays and vectors have the same ZigValue representation IrInstruction *result = ir_const(ira, source_instr, vector_type); - copy_const_val(&result->value, &array->value, false); - result->value.type = vector_type; + copy_const_val(result->value, array->value); + result->value->type = vector_type; return result; } return ir_build_array_to_vector(ira, source_instr, array, vector_type); @@ -12162,10 +13220,10 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction * IrInstruction *vector, ZigType *array_type, ResultLoc *result_loc) { if (instr_is_comptime(vector)) { - // arrays and vectors have the same ConstExprValue representation + // arrays and vectors have the same ZigValue representation IrInstruction *result = ir_const(ira, source_instr, array_type); - copy_const_val(&result->value, &vector->value, false); - result->value.type = array_type; + copy_const_val(result->value, vector->value); + result->value->type = array_type; return result; } if (result_loc == nullptr) { @@ -12173,7 +13231,7 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction * } IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst); @@ -12186,23 +13244,23 @@ static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *sou if (instr_is_comptime(integer)) { unsigned_integer = integer; } else { - assert(integer->value.type->id == ZigTypeIdInt); + assert(integer->value->type->id == ZigTypeIdInt); - if (integer->value.type->data.integral.bit_count > + if (integer->value->type->data.integral.bit_count > ira->codegen->builtin_types.entry_usize->data.integral.bit_count) { ir_add_error(ira, source_instr, buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'", - buf_ptr(&integer->value.type->name), + buf_ptr(&integer->value->type->name), buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } - if (integer->value.type->data.integral.is_signed) { + if (integer->value->type->data.integral.is_signed) { ZigType *unsigned_int_type = get_int_type(ira->codegen, false, - integer->value.type->data.integral.bit_count); + integer->value->type->data.integral.bit_count); unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); - if (type_is_invalid(unsigned_integer->value.type)) + if (type_is_invalid(unsigned_integer->value->type)) return ira->codegen->invalid_instruction; } else { unsigned_integer = integer; @@ -12232,31 +13290,117 @@ static IrInstruction *ir_analyze_enum_literal(IrAnalyze *ira, IrInstruction *sou if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - TypeEnumField *field = find_enum_type_field(enum_type, value->value.data.x_enum_literal); + TypeEnumField *field = find_enum_type_field(enum_type, value->value->data.x_enum_literal); if (field == nullptr) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'", - buf_ptr(&enum_type->name), buf_ptr(value->value.data.x_enum_literal))); + buf_ptr(&enum_type->name), buf_ptr(value->value->data.x_enum_literal))); add_error_note(ira->codegen, msg, enum_type->data.enumeration.decl_node, buf_sprintf("'%s' declared here", buf_ptr(&enum_type->name))); return ira->codegen->invalid_instruction; } IrInstruction *result = ir_const(ira, source_instr, enum_type); - bigint_init_bigint(&result->value.data.x_enum_tag, &field->value); + bigint_init_bigint(&result->value->data.x_enum_tag, &field->value); return result; } -static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, - ZigType *wanted_type, IrInstruction *value, ResultLoc *result_loc) +static IrInstruction *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, ZigType *wanted_type) +{ + ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon list literal to array")); + return ira->codegen->invalid_instruction; +} + +static IrInstruction *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, ZigType *wanted_type) +{ + ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct")); + return ira->codegen->invalid_instruction; +} + +static IrInstruction *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, ZigType *wanted_type) +{ + ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to union")); + return ira->codegen->invalid_instruction; +} + +// Add a compile error and return ErrorSemanticAnalyzeFail if the pointer alignment does not work, +// otherwise return ErrorNone. Does not emit any instructions. +// Assumes that the pointer types have element types with the same ABI alignment. Avoids resolving the +// pointer types' alignments if both of the pointer types are ABI aligned. +static Error ir_cast_ptr_align(IrAnalyze *ira, IrInstruction *source_instr, ZigType *dest_ptr_type, + ZigType *src_ptr_type, AstNode *src_source_node) { Error err; - ZigType *actual_type = value->value.type; + + ir_assert(dest_ptr_type->id == ZigTypeIdPointer, source_instr); + ir_assert(src_ptr_type->id == ZigTypeIdPointer, source_instr); + + if (dest_ptr_type->data.pointer.explicit_alignment == 0 && + src_ptr_type->data.pointer.explicit_alignment == 0) + { + return ErrorNone; + } + + if ((err = type_resolve(ira->codegen, dest_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ErrorSemanticAnalyzeFail; + + if ((err = type_resolve(ira->codegen, src_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ErrorSemanticAnalyzeFail; + + uint32_t wanted_align = get_ptr_align(ira->codegen, dest_ptr_type); + uint32_t actual_align = get_ptr_align(ira->codegen, src_ptr_type); + if (wanted_align > actual_align) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, src_source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_ptr_type->name), actual_align)); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_ptr_type->name), wanted_align)); + return ErrorSemanticAnalyzeFail; + } + + return ErrorNone; +} + +static IrInstruction *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *struct_operand, TypeStructField *field) +{ + IrInstruction *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); + if (type_is_invalid(struct_ptr->value->type)) + return ira->codegen->invalid_instruction; + IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, struct_ptr, + struct_operand->value->type, false); + if (type_is_invalid(field_ptr->value->type)) + return ira->codegen->invalid_instruction; + return ir_get_deref(ira, source_instr, field_ptr, nullptr); +} + +static IrInstruction *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *optional_operand, bool safety_check_on) +{ + IrInstruction *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false); + IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr, + safety_check_on, false); + return ir_get_deref(ira, source_instr, payload_ptr, nullptr); +} + +static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *wanted_type, IrInstruction *value) +{ + Error err; + ZigType *actual_type = value->value->type; AstNode *source_node = source_instr->source_node; if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { return ira->codegen->invalid_instruction; } + // This means the wanted type is anything. + if (wanted_type == ira->codegen->builtin_types.entry_var) { + return value; + } + // perfect match or non-const to const ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false); @@ -12267,13 +13411,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } if (const_cast_result.id == ConstCastResultIdFnCC) { - ir_assert(value->value.type->id == ZigTypeIdFn, source_instr); + ir_assert(value->value->type->id == ZigTypeIdFn, source_instr); // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync && actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { - ir_assert(value->value.data.x_ptr.special == ConstPtrSpecialFunction, source_instr); - ZigFn *fn = value->value.data.x_ptr.data.fn.fn_entry; + ir_assert(value->value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); + ZigFn *fn = value->value->data.x_ptr.data.fn.fn_entry; if (fn->inferred_async_node == nullptr) { fn->inferred_async_node = source_instr->source_node; } @@ -12288,12 +13432,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); } else { return ira->codegen->invalid_instruction; } @@ -12315,9 +13459,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst { IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_child_type); - if (type_is_invalid(cast1->value.type)) + if (type_is_invalid(cast1->value->type)) return ira->codegen->invalid_instruction; - return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, result_loc); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, nullptr); } } } @@ -12327,12 +13471,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { - return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); } else { return ira->codegen->invalid_instruction; } @@ -12350,12 +13494,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { - IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value, nullptr); - if (type_is_invalid(cast1->value.type)) + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); + if (type_is_invalid(cast1->value->type)) return ira->codegen->invalid_instruction; - IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc); - if (type_is_invalid(cast2->value.type)) + IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) return ira->codegen->invalid_instruction; return cast2; @@ -12370,29 +13514,29 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { - if (value->value.special == ConstValSpecialUndef) { + if (value->value->special == ConstValSpecialUndef) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); - result->value.special = ConstValSpecialUndef; + result->value->special = ConstValSpecialUndef; return result; } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { - copy_const_val(&result->value, &value->value, false); - result->value.type = wanted_type; + copy_const_val(result->value, value->value); + result->value->type = wanted_type; } else { - float_init_bigint(&result->value.data.x_bigint, &value->value); + float_init_bigint(&result->value->data.x_bigint, value->value); } return result; } else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { BigFloat bf; - bigfloat_init_bigint(&bf, &value->value.data.x_bigint); - float_init_bigfloat(&result->value, &bf); + bigfloat_init_bigint(&bf, &value->value->data.x_bigint); + float_init_bigfloat(result->value, &bf); } else { - float_init_float(&result->value, &value->value); + float_init_float(result->value, value->value); } return result; } @@ -12427,43 +13571,22 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - - // cast from [N]T to []const T - // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this - if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { - ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == ZigTypeIdPointer); - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type, result_loc); - } - } - - // cast from [N]T to ?[]const T - // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this + // *[N]T to ?[]const T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && - actual_type->id == ZigTypeIdArray) + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray) { - ZigType *ptr_type = - wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == ZigTypeIdPointer); - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value, nullptr); - if (type_is_invalid(cast1->value.type)) - return ira->codegen->invalid_instruction; + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_instruction; - IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc); - if (type_is_invalid(cast2->value.type)) - return ira->codegen->invalid_instruction; + IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) + return ira->codegen->invalid_instruction; - return cast2; - } + return cast2; } // *[N]T to [*]T and [*c]T @@ -12471,28 +13594,96 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray) + actual_type->data.pointer.child_type->id == ZigTypeIdArray && + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { - if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_instruction; - if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_instruction; - if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + ZigType *actual_array_type = actual_type->data.pointer.child_type; + if (wanted_type->data.pointer.sentinel == nullptr || + (actual_array_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, + actual_array_type->data.array.sentinel))) { - return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + } } } // *[N]T to []T - if (is_slice(wanted_type) && + // *[N]T to E![]T + if ((is_slice(wanted_type) || + (wanted_type->id == ZigTypeIdErrorUnion && + is_slice(wanted_type->data.error_union.payload_type))) && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == ZigTypeIdArray) { - ZigType *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ? + wanted_type->data.error_union.payload_type : wanted_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 + || !actual_type->data.pointer.is_const); + if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + // If the pointers both have ABI align, it works. + // Or if the array length is 0, alignment doesn't matter. + bool ok_align = array_type->data.array.len == 0 || + (slice_ptr_type->data.pointer.explicit_alignment == 0 && + actual_type->data.pointer.explicit_alignment == 0); + if (!ok_align) { + // If either one has non ABI align, we have to resolve them both + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); + } + if (ok_align) { + if (wanted_type->id == ZigTypeIdErrorUnion) { + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, slice_type, value); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_instruction; + + IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) + return ira->codegen->invalid_instruction; + + return cast2; + } else { + return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); + } + } + } + } + + // *[N]T to E![]T + if (wanted_type->id == ZigTypeIdErrorUnion && + is_slice(wanted_type->data.error_union.payload_type) && + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *slice_type = wanted_type->data.error_union.payload_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; assert(slice_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 @@ -12519,26 +13710,47 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); } if (ok_align) { - return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc); + return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); } } } // *@Frame(func) to anyframe->T or anyframe + // *@Frame(func) to ?anyframe->T or ?anyframe + // *@Frame(func) to E!anyframe->T or E!anyframe if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && !actual_type->data.pointer.is_const && - actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame && wanted_type->id == ZigTypeIdAnyFrame) + actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame) { - bool ok = true; - if (wanted_type->data.any_frame.result_type != nullptr) { - ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn; - ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type; - if (wanted_type->data.any_frame.result_type != fn_return_type) { - ok = false; - } + ZigType *anyframe_type; + if (wanted_type->id == ZigTypeIdAnyFrame) { + anyframe_type = wanted_type; + } else if (wanted_type->id == ZigTypeIdOptional && + wanted_type->data.maybe.child_type->id == ZigTypeIdAnyFrame) + { + anyframe_type = wanted_type->data.maybe.child_type; + } else if (wanted_type->id == ZigTypeIdErrorUnion && + wanted_type->data.error_union.payload_type->id == ZigTypeIdAnyFrame) + { + anyframe_type = wanted_type->data.error_union.payload_type; + } else { + anyframe_type = nullptr; } - if (ok) { - return ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, wanted_type); + if (anyframe_type != nullptr) { + bool ok = true; + if (anyframe_type->data.any_frame.result_type != nullptr) { + ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn; + ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type; + if (anyframe_type->data.any_frame.result_type != fn_return_type) { + ok = false; + } + } + if (ok) { + IrInstruction *cast1 = ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, anyframe_type); + if (anyframe_type == wanted_type) + return cast1; + return ir_analyze_cast(ira, source_instr, wanted_type, cast1); + } } } @@ -12563,35 +13775,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type); } - // cast from [N]T to E![]const T - if (wanted_type->id == ZigTypeIdErrorUnion && - is_slice(wanted_type->data.error_union.payload_type) && - actual_type->id == ZigTypeIdArray) - { - ZigType *ptr_type = - wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == ZigTypeIdPointer); - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value, nullptr); - if (type_is_invalid(cast1->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc); - if (type_is_invalid(cast2->value.type)) - return ira->codegen->invalid_instruction; - - return cast2; - } - } - // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { - return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, nullptr); } // cast from typed number to integer or float literal. @@ -12614,10 +13802,10 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdEnum)) { IrInstruction *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.maybe.child_type); - if (result == ira->codegen->invalid_instruction) + if (result == ira->codegen->invalid_instruction) return result; - return ir_analyze_optional_wrap(ira, result, value, wanted_type, result_loc); + return ir_analyze_optional_wrap(ira, result, value, wanted_type, nullptr); } // cast from enum literal to error union when payload is an enum @@ -12625,10 +13813,10 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdEnum)) { IrInstruction *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.error_union.payload_type); - if (result == ira->codegen->invalid_instruction) + if (result == ira->codegen->invalid_instruction) return result; - - return ir_analyze_err_wrap_payload(ira, result, value, wanted_type, result_loc); + + return ir_analyze_err_wrap_payload(ira, result, value, wanted_type, nullptr); } // cast from union to the enum type of the union @@ -12658,35 +13846,39 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst types_match_const_cast_only(ira, array_type->data.array.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && - // This should be the job of `types_match_const_cast_only` - // but `types_match_const_cast_only` only gets info for child_types - ((wanted_type->data.pointer.is_const && actual_type->data.pointer.is_const) || - !actual_type->data.pointer.is_const)) + // `types_match_const_cast_only` only gets info for child_types + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { - if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { + if ((err = ir_cast_ptr_align(ira, source_instr, wanted_type, actual_type, value->source_node))) return ira->codegen->invalid_instruction; - } - if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { - return ira->codegen->invalid_instruction; - } - uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type); - uint32_t actual_align = get_ptr_align(ira->codegen, actual_type); - if (wanted_align > actual_align) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align)); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align)); - return ira->codegen->invalid_instruction; - } + return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); } } + // [:x]T to [*:x]T + // [:x]T to [*c]T + if (wanted_type->id == ZigTypeIdPointer && is_slice(actual_type) && + ((wanted_type->data.pointer.ptr_len == PtrLenUnknown && wanted_type->data.pointer.sentinel != nullptr) || + wanted_type->data.pointer.ptr_len == PtrLenC)) + { + ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, + actual_type->data.structure.fields[slice_ptr_index]); + if (types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + slice_ptr_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && + (slice_ptr_type->data.pointer.sentinel != nullptr && + (wanted_type->data.pointer.ptr_len == PtrLenC || + const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, + slice_ptr_type->data.pointer.sentinel)))) + { + TypeStructField *ptr_field = actual_type->data.structure.fields[slice_ptr_index]; + IrInstruction *slice_ptr = ir_analyze_struct_value_field_value(ira, source_instr, value, ptr_field); + return ir_implicit_cast2(ira, source_instr, slice_ptr, wanted_type); + } + } + // cast from *T and [*]T to *c_void and ?*c_void // but don't do it if the actual type is a double pointer if (is_pointery_and_elem_is_not_pointery(actual_type)) { @@ -12711,10 +13903,10 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { - if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) { + bool has_bits; + if ((err = type_has_bits2(ira->codegen, actual_type, &has_bits))) return ira->codegen->invalid_instruction; - } - if (!type_has_bits(actual_type)) { + if (!has_bits) { return ir_get_ref(ira, source_instr, value, false, false); } } @@ -12725,7 +13917,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst types_match_const_cast_only(ira, wanted_type->data.array.child_type, actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, result_loc); + return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, nullptr); } // cast from [N]T to @Vector(N, T) @@ -12754,11 +13946,48 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); } + // cast from inferred struct type to array, union, or struct + if (is_anon_container(actual_type)) { + AstNode *decl_node = actual_type->data.structure.decl_node; + ir_assert(decl_node->type == NodeTypeContainerInitExpr, source_instr); + ContainerInitKind init_kind = decl_node->data.container_init_expr.kind; + uint32_t field_count = actual_type->data.structure.src_field_count; + if (wanted_type->id == ZigTypeIdArray && (init_kind == ContainerInitKindArray || field_count == 0) && + wanted_type->data.array.len == field_count) + { + return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); + } else if (wanted_type->id == ZigTypeIdStruct && + (init_kind == ContainerInitKindStruct || field_count == 0)) + { + return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); + } else if (wanted_type->id == ZigTypeIdUnion && init_kind == ContainerInitKindStruct && field_count == 1) { + return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type); + } + } + // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } + // T to ?U, where T implicitly casts to U + if (wanted_type->id == ZigTypeIdOptional && actual_type->id != ZigTypeIdOptional) { + IrInstruction *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.maybe.child_type); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_instruction; + return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); + } + + // T to E!U, where T implicitly casts to U + if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorUnion && + actual_type->id != ZigTypeIdErrorSet) + { + IrInstruction *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.error_union.payload_type); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_instruction; + return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), @@ -12767,85 +13996,127 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } -static IrInstruction *ir_implicit_cast_with_result(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type, - ResultLoc *result_loc) +static IrInstruction *ir_implicit_cast2(IrAnalyze *ira, IrInstruction *value_source_instr, + IrInstruction *value, ZigType *expected_type) { assert(value); assert(value != ira->codegen->invalid_instruction); assert(!expected_type || !type_is_invalid(expected_type)); - assert(value->value.type); - assert(!type_is_invalid(value->value.type)); + assert(value->value->type); + assert(!type_is_invalid(value->value->type)); if (expected_type == nullptr) return value; // anything will do - if (expected_type == value->value.type) + if (expected_type == value->value->type) return value; // match - if (value->value.type->id == ZigTypeIdUnreachable) + if (value->value->type->id == ZigTypeIdUnreachable) return value; - return ir_analyze_cast(ira, value, expected_type, value, result_loc); + return ir_analyze_cast(ira, value_source_instr, expected_type, value); } static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) { - return ir_implicit_cast_with_result(ira, value, expected_type, nullptr); + return ir_implicit_cast2(ira, value, value, expected_type); +} + +static ZigType *get_ptr_elem_type(CodeGen *g, IrInstruction *ptr) { + ir_assert(ptr->value->type->id == ZigTypeIdPointer, ptr); + ZigType *elem_type = ptr->value->type->data.pointer.child_type; + if (elem_type != g->builtin_types.entry_var) + return elem_type; + + if (ir_resolve_lazy(g, ptr->source_node, ptr->value)) + return g->builtin_types.entry_invalid; + + assert(value_is_comptime(ptr->value)); + ZigValue *pointee = const_ptr_pointee_unchecked(g, ptr->value); + return pointee->type; } static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr, ResultLoc *result_loc) { Error err; - ZigType *type_entry = ptr->value.type; - if (type_is_invalid(type_entry)) + ZigType *ptr_type = ptr->value->type; + if (type_is_invalid(ptr_type)) return ira->codegen->invalid_instruction; - if (type_entry->id != ZigTypeIdPointer) { + if (ptr_type->id != ZigTypeIdPointer) { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non-pointer type '%s'", - buf_ptr(&type_entry->name))); + buf_ptr(&ptr_type->name))); return ira->codegen->invalid_instruction; } - ZigType *child_type = type_entry->data.pointer.child_type; + ZigType *child_type = ptr_type->data.pointer.child_type; + if (type_is_invalid(child_type)) + return ira->codegen->invalid_instruction; // if the child type has one possible value, the deref is comptime switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueYes: - return ir_const(ira, source_instruction, child_type); + return ir_const_move(ira, source_instruction, + get_the_one_possible_value(ira->codegen, child_type)); case OnePossibleValueNo: break; } if (instr_is_comptime(ptr)) { - if (ptr->value.special == ConstValSpecialUndef) { + if (ptr->value->special == ConstValSpecialUndef) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value")); return ira->codegen->invalid_instruction; } - if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value); + if (child_type == ira->codegen->builtin_types.entry_var) { + child_type = pointee->type; + } if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, source_instruction, child_type); - if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value, - &ptr->value))) + if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, result->value, + ptr->value))) { return ira->codegen->invalid_instruction; } - result->value.type = child_type; + result->value->type = child_type; return result; } } } + // if the instruction is a const ref instruction we can skip it if (ptr->id == IrInstructionIdRef) { IrInstructionRef *ref_inst = reinterpret_cast(ptr); return ref_inst->value; } + // If the instruction is a element pointer instruction to a vector, we emit + // vector element extract instruction rather than load pointer. If the + // pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been + // possible to implement this in the codegen for IrInstructionLoadPtrGen. + // However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error + // if the vector index cannot be determined right here, right now, because + // the type information does not contain enough information to actually + // perform a dereference. + if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + if (ptr->id == IrInstructionIdElemPtr) { + IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr; + IrInstruction *vector_loaded = ir_get_deref(ira, elem_ptr->array_ptr, + elem_ptr->array_ptr, nullptr); + IrInstruction *elem_index = elem_ptr->elem_index; + return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index); + } + ir_add_error(ira, ptr, + buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *result_loc_inst; - if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) { + if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, true, false, true); - if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { + if (type_is_invalid(result_loc_inst->value->type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } } else { @@ -12856,7 +14127,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } static bool ir_resolve_const_align(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, - ConstExprValue *const_val, uint32_t *out) + ZigValue *const_val, uint32_t *out) { Error err; if ((err = ir_resolve_const_val(codegen, exec, source_node, const_val, UndefBad))) @@ -12879,15 +14150,15 @@ static bool ir_resolve_const_align(CodeGen *codegen, IrExecutable *exec, AstNode } static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, ZigType *elem_type, uint32_t *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; // Look for this pattern: `*align(@alignOf(T)) T`. // This can be resolved to be `*out = 0` without resolving any alignment. - if (elem_type != nullptr && value->value.special == ConstValSpecialLazy && - value->value.data.x_lazy->id == LazyValueIdAlignOf) + if (elem_type != nullptr && value->value->special == ConstValSpecialLazy && + value->value->data.x_lazy->id == LazyValueIdAlignOf) { - LazyValueAlignOf *lazy_align_of = reinterpret_cast(value->value.data.x_lazy); + LazyValueAlignOf *lazy_align_of = reinterpret_cast(value->value->data.x_lazy); ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type); if (type_is_invalid(lazy_elem_type)) @@ -12900,22 +14171,22 @@ static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, ZigType *elem } IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; return ir_resolve_const_align(ira->codegen, ira->new_irb.exec, value->source_node, - &casted_value->value, out); + casted_value->value, out); } static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, ZigType *int_type, uint64_t *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -12928,14 +14199,14 @@ static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out } static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -12952,18 +14223,16 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) } static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; - ConstExprValue *atomic_order_val = get_builtin_value(ira->codegen, "AtomicOrder"); - assert(atomic_order_val->type->id == ZigTypeIdMetaType); - ZigType *atomic_order_type = atomic_order_val->data.x_type; + ZigType *atomic_order_type = get_builtin_type(ira->codegen, "AtomicOrder"); IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_order_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -12972,18 +14241,16 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic } static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, AtomicRmwOp *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; - ConstExprValue *atomic_rmw_op_val = get_builtin_value(ira->codegen, "AtomicRmwOp"); - assert(atomic_rmw_op_val->type->id == ZigTypeIdMetaType); - ZigType *atomic_rmw_op_type = atomic_rmw_op_val->data.x_type; + ZigType *atomic_rmw_op_type = get_builtin_type(ira->codegen, "AtomicRmwOp"); IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -12992,18 +14259,16 @@ static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, Atomi } static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; - ConstExprValue *global_linkage_val = get_builtin_value(ira->codegen, "GlobalLinkage"); - assert(global_linkage_val->type->id == ZigTypeIdMetaType); - ZigType *global_linkage_type = global_linkage_val->data.x_type; + ZigType *global_linkage_type = get_builtin_type(ira->codegen, "GlobalLinkage"); IrInstruction *casted_value = ir_implicit_cast(ira, value, global_linkage_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -13012,18 +14277,16 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, Glob } static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return false; - ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); - assert(float_mode_val->type->id == ZigTypeIdMetaType); - ZigType *float_mode_type = float_mode_val->data.x_type; + ZigType *float_mode_type = get_builtin_type(ira->codegen, "FloatMode"); IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return false; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return false; @@ -13031,37 +14294,36 @@ static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMod return true; } - static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return nullptr; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return nullptr; - ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_val) return nullptr; - ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index]; + ZigValue *ptr_field = const_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *len_field = const_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); - ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; - if (array_val->data.x_array.special == ConstArraySpecialBuf) { - return array_val->data.x_array.data.s_buf; - } + ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); size_t len = bigint_as_usize(&len_field->data.x_bigint); + if (array_val->data.x_array.special == ConstArraySpecialBuf && len == buf_len(array_val->data.x_array.data.s_buf)) { + return array_val->data.x_array.data.s_buf; + } Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; - ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; + ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; if (char_val->special == ConstValSpecialUndef) { ir_add_error(ira, casted_value, buf_sprintf("use of undefined value")); return nullptr; @@ -13078,7 +14340,7 @@ static IrInstruction *ir_analyze_instruction_add_implicit_return_type(IrAnalyze IrInstructionAddImplicitReturnType *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ir_unreach_error(ira); if (instruction->result_loc_ret == nullptr || !instruction->result_loc_ret->implicit_return_type_done) { @@ -13090,21 +14352,11 @@ static IrInstruction *ir_analyze_instruction_add_implicit_return_type(IrAnalyze static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *instruction) { IrInstruction *operand = instruction->operand->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return ir_unreach_error(ira); - if (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr && - handle_is_ptr(ira->explicit_return_type)) - { - // result location mechanism took care of it. - IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr); - result->value.type = ira->codegen->builtin_types.entry_unreachable; - return ir_finish_anal(ira, result); - } - IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type); - if (type_is_invalid(casted_operand->value.type)) { + if (type_is_invalid(casted_operand->value->type)) { AstNode *source_node = ira->explicit_return_type_source_node; if (source_node != nullptr) { ErrorMsg *msg = ira->codegen->errors.last(); @@ -13114,9 +14366,19 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio return ir_unreach_error(ira); } - if (casted_operand->value.special == ConstValSpecialRuntime && - casted_operand->value.type->id == ZigTypeIdPointer && - casted_operand->value.data.rh_ptr == RuntimeHintPtrStack) + if (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr && + handle_is_ptr(ira->explicit_return_type)) + { + // result location mechanism took care of it. + IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr); + result->value->type = ira->codegen->builtin_types.entry_unreachable; + return ir_finish_anal(ira, result); + } + + if (casted_operand->value->special == ConstValSpecialRuntime && + casted_operand->value->type->id == ZigTypeIdPointer && + casted_operand->value->data.rh_ptr == RuntimeHintPtrStack) { ir_add_error(ira, casted_operand, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); @@ -13124,23 +14386,21 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_operand); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - copy_const_val(&result->value, &instruction->base.value, true); - return result; + return ir_const_move(ira, &instruction->base, instruction->base.value); } static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; ZigType *bool_type = ira->codegen->builtin_types.entry_bool; @@ -13154,16 +14414,16 @@ static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; - assert(casted_op1->value.type->id == ZigTypeIdBool); - assert(casted_op2->value.type->id == ZigTypeIdBool); + assert(casted_op1->value->type->id == ZigTypeIdBool); + assert(casted_op2->value->type->id == ZigTypeIdBool); bool result_bool; if (bin_op_instruction->op_id == IrBinOpBoolOr) { result_bool = op1_val->data.x_bool || op2_val->data.x_bool; @@ -13178,7 +14438,7 @@ static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); - result->value.type = bool_type; + result->value->type = bool_type; return result; } @@ -13201,7 +14461,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { } } -static bool optional_value_is_null(ConstExprValue *val) { +static bool optional_value_is_null(ZigValue *val) { assert(val->special == ConstValSpecialStatic); if (get_codegen_ptr_type(val->type) != nullptr) { if (val->data.x_ptr.special == ConstPtrSpecialNull) { @@ -13218,24 +14478,39 @@ static bool optional_value_is_null(ConstExprValue *val) { } } +static void set_optional_value_to_null(ZigValue *val) { + assert(val->special == ConstValSpecialStatic); + if (val->type->id == ZigTypeIdNull) return; // nothing to do + assert(val->type->id == ZigTypeIdOptional); + if (get_codegen_ptr_type(val->type) != nullptr) { + val->data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(val->type)) { + val->data.x_err_set = nullptr; + } else { + val->data.x_optional = nullptr; + } +} + +static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { + assert(opt_val->special == ConstValSpecialStatic); + assert(opt_val->type->id == ZigTypeIdOptional); + if (payload == nullptr) { + set_optional_value_to_null(opt_val); + } else if (is_opt_err_set(opt_val->type)) { + assert(payload->type->id == ZigTypeIdErrorSet); + opt_val->data.x_err_set = payload->data.x_err_set; + } else { + opt_val->data.x_optional = payload; + } +} + static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, - ConstExprValue *op1_val, ConstExprValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id, + ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id, bool one_possible_value) { if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &bin_op_instruction->base, resolved_type); - if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) { - if (float_is_nan(op1_val) || float_is_nan(op2_val)) { - return ir_const_bool(ira, &bin_op_instruction->base, op_id == IrBinOpCmpNotEq); - } - Cmp cmp_result = float_cmp(op1_val, op2_val); - bool answer = resolve_cmp_op_id(op_id, cmp_result); - return ir_const_bool(ira, &bin_op_instruction->base, answer); - } else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) { - Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); - bool answer = resolve_cmp_op_id(op_id, cmp_result); - return ir_const_bool(ira, &bin_op_instruction->base, answer); - } else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { + if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || op1_val->data.x_ptr.special == ConstPtrSpecialNull) && (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || @@ -13272,7 +14547,7 @@ static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_t } // Returns ErrorNotLazy when the value cannot be determined -static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *result) { +static Error lazy_cmp_zero(AstNode *source_node, ZigValue *val, Cmp *result) { Error err; switch (val->special) { @@ -13285,6 +14560,12 @@ static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *resul case ZigTypeIdInt: *result = bigint_cmp_zero(&val->data.x_bigint); return ErrorNone; + case ZigTypeIdComptimeFloat: + case ZigTypeIdFloat: + if (float_is_nan(val)) + return ErrorNotLazy; + *result = float_cmp_zero(val); + return ErrorNone; default: return ErrorNotLazy; } @@ -13299,7 +14580,7 @@ static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *resul LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_size_of->ira; bool is_zero_bits; - if ((err = type_val_resolve_zero_bits(ira->codegen, &lazy_size_of->target_type->value, + if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_size_of->target_type->value, nullptr, nullptr, &is_zero_bits))) { return err; @@ -13314,37 +14595,480 @@ static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *resul zig_unreachable(); } -static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { +static ErrorMsg *ir_eval_bin_op_cmp_scalar(IrAnalyze *ira, IrInstruction *source_instr, + ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) +{ + Error err; + { + // Before resolving the values, we special case comparisons against zero. These can often + // be done without resolving lazy values, preventing potential dependency loops. + Cmp op1_cmp_zero; + if ((err = lazy_cmp_zero(source_instr->source_node, op1_val, &op1_cmp_zero))) { + if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; + return ira->codegen->trace_err; + } + Cmp op2_cmp_zero; + if ((err = lazy_cmp_zero(source_instr->source_node, op2_val, &op2_cmp_zero))) { + if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; + return ira->codegen->trace_err; + } + bool can_cmp_zero = false; + Cmp cmp_result; + if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpEQ; + } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpGT; + } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) { + can_cmp_zero = true; + cmp_result = CmpGT; + } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) { + can_cmp_zero = true; + cmp_result = CmpGT; + } + if (can_cmp_zero) { + bool answer = resolve_cmp_op_id(op_id, cmp_result); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = answer; + return nullptr; + } + } +never_mind_just_calculate_it_normally: + + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, + op1_val, UndefOk))) + { + return ira->codegen->trace_err; + } + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, + op2_val, UndefOk))) + { + return ira->codegen->trace_err; + } + + + if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef || + op1_val->type->id == ZigTypeIdUndefined || op2_val->type->id == ZigTypeIdUndefined) + { + out_val->special = ConstValSpecialUndef; + return nullptr; + } + + bool op1_is_float = op1_val->type->id == ZigTypeIdFloat || op1_val->type->id == ZigTypeIdComptimeFloat; + bool op2_is_float = op2_val->type->id == ZigTypeIdFloat || op2_val->type->id == ZigTypeIdComptimeFloat; + if (op1_is_float && op2_is_float) { + if (float_is_nan(op1_val) || float_is_nan(op2_val)) { + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = op_id == IrBinOpCmpNotEq; + return nullptr; + } + if (op1_val->type->id == ZigTypeIdComptimeFloat) { + IrInstruction *tmp = ir_const_noval(ira, source_instr); + tmp->value = op1_val; + IrInstruction *casted = ir_implicit_cast(ira, tmp, op2_val->type); + op1_val = casted->value; + } else if (op2_val->type->id == ZigTypeIdComptimeFloat) { + IrInstruction *tmp = ir_const_noval(ira, source_instr); + tmp->value = op2_val; + IrInstruction *casted = ir_implicit_cast(ira, tmp, op1_val->type); + op2_val = casted->value; + } + Cmp cmp_result = float_cmp(op1_val, op2_val); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); + return nullptr; + } + + bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt; + bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt; + + BigInt *op1_bigint; + BigInt *op2_bigint; + bool need_to_free_op1_bigint = false; + bool need_to_free_op2_bigint = false; + if (op1_is_float) { + op1_bigint = allocate(1, "BigInt"); + need_to_free_op1_bigint = true; + float_init_bigint(op1_bigint, op1_val); + } else { + assert(op1_is_int); + op1_bigint = &op1_val->data.x_bigint; + } + if (op2_is_float) { + op2_bigint = allocate(1, "BigInt"); + need_to_free_op2_bigint = true; + float_init_bigint(op2_bigint, op2_val); + } else { + assert(op2_is_int); + op2_bigint = &op2_val->data.x_bigint; + } + + Cmp cmp_result = bigint_cmp(op1_bigint, op2_bigint); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); + + if (need_to_free_op1_bigint) destroy(op1_bigint, "BigInt"); + if (need_to_free_op2_bigint) destroy(op2_bigint, "BigInt"); + return nullptr; +} + +static IrInstruction *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *op1, IrInstruction *op2, IrBinOp op_id) +{ Error err; + ZigType *scalar_result_type = ira->codegen->builtin_types.entry_bool; + ZigType *result_type = scalar_result_type; + ZigType *op1_scalar_type = op1->value->type; + ZigType *op2_scalar_type = op2->value->type; + if (op1->value->type->id == ZigTypeIdVector && op2->value->type->id == ZigTypeIdVector) { + if (op1->value->type->data.vector.len != op2->value->type->data.vector.len) { + ir_add_error(ira, source_instr, + buf_sprintf("vector length mismatch: %" PRIu32 " and %" PRIu32, + op1->value->type->data.vector.len, op2->value->type->data.vector.len)); + return ira->codegen->invalid_instruction; + } + result_type = get_vector_type(ira->codegen, op1->value->type->data.vector.len, scalar_result_type); + op1_scalar_type = op1->value->type->data.vector.elem_type; + op2_scalar_type = op2->value->type->data.vector.elem_type; + } else if (op1->value->type->id == ZigTypeIdVector || op2->value->type->id == ZigTypeIdVector) { + ir_add_error(ira, source_instr, + buf_sprintf("mixed scalar and vector operands to comparison operator: '%s' and '%s'", + buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_instruction; + } + + bool opv_op1; + switch (type_has_one_possible_value(ira->codegen, op1->value->type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + opv_op1 = true; + break; + case OnePossibleValueNo: + opv_op1 = false; + break; + } + bool opv_op2; + switch (type_has_one_possible_value(ira->codegen, op2->value->type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + opv_op2 = true; + break; + case OnePossibleValueNo: + opv_op2 = false; + break; + } + Cmp op1_cmp_zero; + bool have_op1_cmp_zero = false; + if ((err = lazy_cmp_zero(source_instr->source_node, op1->value, &op1_cmp_zero))) { + if (err != ErrorNotLazy) return ira->codegen->invalid_instruction; + } else { + have_op1_cmp_zero = true; + } + Cmp op2_cmp_zero; + bool have_op2_cmp_zero = false; + if ((err = lazy_cmp_zero(source_instr->source_node, op2->value, &op2_cmp_zero))) { + if (err != ErrorNotLazy) return ira->codegen->invalid_instruction; + } else { + have_op2_cmp_zero = true; + } + if (((opv_op1 || instr_is_comptime(op1)) && (opv_op2 || instr_is_comptime(op2))) || + (have_op1_cmp_zero && have_op2_cmp_zero)) + { + IrInstruction *result_instruction = ir_const(ira, source_instr, result_type); + ZigValue *out_val = result_instruction->value; + if (result_type->id == ZigTypeIdVector) { + size_t len = result_type->data.vector.len; + expand_undef_array(ira->codegen, op1->value); + expand_undef_array(ira->codegen, op2->value); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + for (size_t i = 0; i < len; i += 1) { + ZigValue *scalar_op1_val = &op1->value->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_op2_val = &op2->value->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_out_val->type == scalar_result_type); + ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, + scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_instruction; + } + } + out_val->type = result_type; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_bin_op_cmp_scalar(ira, source_instr, op1->value, op_id, + op2->value, out_val) != nullptr) + { + return ira->codegen->invalid_instruction; + } + } + return result_instruction; + } + + // If one operand has a comptime-known comparison with 0, and the other operand is unsigned, we might + // know the answer, depending on the operator. + // TODO make this work with vectors + if (have_op1_cmp_zero && op2_scalar_type->id == ZigTypeIdInt && !op2_scalar_type->data.integral.is_signed) { + if (op1_cmp_zero == CmpEQ) { + // 0 <= unsigned_x // true + // 0 > unsigned_x // false + switch (op_id) { + case IrBinOpCmpLessOrEq: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } else if (op1_cmp_zero == CmpLT) { + // -1 != unsigned_x // true + // -1 <= unsigned_x // true + // -1 < unsigned_x // true + // -1 == unsigned_x // false + // -1 >= unsigned_x // false + // -1 > unsigned_x // false + switch (op_id) { + case IrBinOpCmpNotEq: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpLessThan: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } + } + if (have_op2_cmp_zero && op1_scalar_type->id == ZigTypeIdInt && !op1_scalar_type->data.integral.is_signed) { + if (op2_cmp_zero == CmpEQ) { + // unsigned_x < 0 // false + // unsigned_x >= 0 // true + switch (op_id) { + case IrBinOpCmpLessThan: + return ir_const_bool(ira, source_instr, false); + case IrBinOpCmpGreaterOrEq: + return ir_const_bool(ira, source_instr, true); + default: + break; + } + } else if (op2_cmp_zero == CmpLT) { + // unsigned_x != -1 // true + // unsigned_x >= -1 // true + // unsigned_x > -1 // true + // unsigned_x == -1 // false + // unsigned_x < -1 // false + // unsigned_x <= -1 // false + switch (op_id) { + case IrBinOpCmpNotEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpLessOrEq: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } + } + + // It must be a runtime comparison. + // For floats, emit a float comparison instruction. + bool op1_is_float = op1_scalar_type->id == ZigTypeIdFloat || op1_scalar_type->id == ZigTypeIdComptimeFloat; + bool op2_is_float = op2_scalar_type->id == ZigTypeIdFloat || op2_scalar_type->id == ZigTypeIdComptimeFloat; + if (op1_is_float && op2_is_float) { + // Implicit cast the smaller one to the larger one. + ZigType *dest_scalar_type; + if (op1_scalar_type->id == ZigTypeIdComptimeFloat) { + dest_scalar_type = op2_scalar_type; + } else if (op2_scalar_type->id == ZigTypeIdComptimeFloat) { + dest_scalar_type = op1_scalar_type; + } else if (op1_scalar_type->data.floating.bit_count >= op2_scalar_type->data.floating.bit_count) { + dest_scalar_type = op1_scalar_type; + } else { + dest_scalar_type = op2_scalar_type; + } + ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? + get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; + IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type); + if (type_is_invalid(casted_op1->value->type) || type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_instruction; + return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); + } + + // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. + // For mixed signed and unsigned integers, implicit cast both operands to a signed + // integer with + 1 bit. + // For mixed floats and integers, extract the integer part from the float, cast that to + // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, + // add/subtract 1. + bool dest_int_is_signed = false; + if (have_op1_cmp_zero) { + if (op1_cmp_zero == CmpLT) dest_int_is_signed = true; + } else if (op1_is_float) { + dest_int_is_signed = true; + } else if (op1_scalar_type->id == ZigTypeIdInt && op1_scalar_type->data.integral.is_signed) { + dest_int_is_signed = true; + } + if (have_op2_cmp_zero) { + if (op2_cmp_zero == CmpLT) dest_int_is_signed = true; + } else if (op2_is_float) { + dest_int_is_signed = true; + } else if (op2->value->type->id == ZigTypeIdInt && op2->value->type->data.integral.is_signed) { + dest_int_is_signed = true; + } + ZigType *dest_float_type = nullptr; + uint32_t op1_bits; + if (instr_is_comptime(op1)) { + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk); + if (op1_val == nullptr) + return ira->codegen->invalid_instruction; + if (op1_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); + if (result_type->id == ZigTypeIdVector) { + ir_add_error(ira, op1, buf_sprintf("compiler bug: TODO: support comptime vector here")); + return ira->codegen->invalid_instruction; + } + bool is_unsigned; + if (op1_is_float) { + BigInt bigint = {}; + float_init_bigint(&bigint, op1_val); + Cmp zcmp = float_cmp_zero(op1_val); + if (float_has_fraction(op1_val)) { + if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { + return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); + } + if (zcmp == CmpLT) { + bigint_decr(&bigint); + } else { + bigint_incr(&bigint); + } + } + op1_bits = bigint_bits_needed(&bigint); + is_unsigned = zcmp != CmpLT; + } else { + op1_bits = bigint_bits_needed(&op1_val->data.x_bigint); + is_unsigned = bigint_cmp_zero(&op1_val->data.x_bigint) != CmpLT; + } + if (is_unsigned && dest_int_is_signed) { + op1_bits += 1; + } + } else if (op1_is_float) { + dest_float_type = op1_scalar_type; + } else { + ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr); + op1_bits = op1_scalar_type->data.integral.bit_count; + if (!op1_scalar_type->data.integral.is_signed && dest_int_is_signed) { + op1_bits += 1; + } + } + uint32_t op2_bits; + if (instr_is_comptime(op2)) { + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk); + if (op2_val == nullptr) + return ira->codegen->invalid_instruction; + if (op2_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); + if (result_type->id == ZigTypeIdVector) { + ir_add_error(ira, op2, buf_sprintf("compiler bug: TODO: support comptime vector here")); + return ira->codegen->invalid_instruction; + } + bool is_unsigned; + if (op2_is_float) { + BigInt bigint = {}; + float_init_bigint(&bigint, op2_val); + Cmp zcmp = float_cmp_zero(op2_val); + if (float_has_fraction(op2_val)) { + if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { + return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); + } + if (zcmp == CmpLT) { + bigint_decr(&bigint); + } else { + bigint_incr(&bigint); + } + } + op2_bits = bigint_bits_needed(&bigint); + is_unsigned = zcmp != CmpLT; + } else { + op2_bits = bigint_bits_needed(&op2_val->data.x_bigint); + is_unsigned = bigint_cmp_zero(&op2_val->data.x_bigint) != CmpLT; + } + if (is_unsigned && dest_int_is_signed) { + op2_bits += 1; + } + } else if (op2_is_float) { + dest_float_type = op2_scalar_type; + } else { + ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr); + op2_bits = op2_scalar_type->data.integral.bit_count; + if (!op2_scalar_type->data.integral.is_signed && dest_int_is_signed) { + op2_bits += 1; + } + } + ZigType *dest_scalar_type = (dest_float_type == nullptr) ? + get_int_type(ira->codegen, dest_int_is_signed, (op1_bits > op2_bits) ? op1_bits : op2_bits) : + dest_float_type; + ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? + get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; + + IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_instruction; + IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_instruction; + return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); +} + +static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; AstNode *source_node = bin_op_instruction->base.source_node; IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - if (is_equality_cmp && op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) { + if (is_equality_cmp && op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdNull) { return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq)); } else if (is_equality_cmp && - ((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) || - (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional))) + ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdOptional) || + (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdOptional))) { IrInstruction *maybe_op; - if (op1->value.type->id == ZigTypeIdNull) { + if (op1->value->type->id == ZigTypeIdNull) { maybe_op = op2; - } else if (op2->value.type->id == ZigTypeIdNull) { + } else if (op2->value->type->id == ZigTypeIdNull) { maybe_op = op1; } else { zig_unreachable(); } if (instr_is_comptime(maybe_op)) { - ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); + ZigValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->invalid_instruction; bool is_null = optional_value_is_null(maybe_val); @@ -13354,32 +15078,32 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope, source_node, maybe_op); - is_non_null->value.type = ira->codegen->builtin_types.entry_bool; + is_non_null->value->type = ira->codegen->builtin_types.entry_bool; if (op_id == IrBinOpCmpEq) { IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, is_non_null); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } else { return is_non_null; } } else if (is_equality_cmp && - ((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer && - op2->value.type->data.pointer.ptr_len == PtrLenC) || - (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer && - op1->value.type->data.pointer.ptr_len == PtrLenC))) + ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdPointer && + op2->value->type->data.pointer.ptr_len == PtrLenC) || + (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdPointer && + op1->value->type->data.pointer.ptr_len == PtrLenC))) { IrInstruction *c_ptr_op; - if (op1->value.type->id == ZigTypeIdNull) { + if (op1->value->type->id == ZigTypeIdNull) { c_ptr_op = op2; - } else if (op2->value.type->id == ZigTypeIdNull) { + } else if (op2->value->type->id == ZigTypeIdNull) { c_ptr_op = op1; } else { zig_unreachable(); } if (instr_is_comptime(c_ptr_op)) { - ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk); + ZigValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk); if (!c_ptr_val) return ira->codegen->invalid_instruction; if (c_ptr_val->special == ConstValSpecialUndef) @@ -13392,46 +15116,46 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * } IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope, source_node, c_ptr_op); - is_non_null->value.type = ira->codegen->builtin_types.entry_bool; + is_non_null->value->type = ira->codegen->builtin_types.entry_bool; if (op_id == IrBinOpCmpEq) { IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, is_non_null); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } else { return is_non_null; } - } else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) { - ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type; + } else if (op1->value->type->id == ZigTypeIdNull || op2->value->type->id == ZigTypeIdNull) { + ZigType *non_null_type = (op1->value->type->id == ZigTypeIdNull) ? op2->value->type : op1->value->type; ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null", buf_ptr(&non_null_type->name))); return ira->codegen->invalid_instruction; } else if (is_equality_cmp && ( - (op1->value.type->id == ZigTypeIdEnumLiteral && op2->value.type->id == ZigTypeIdUnion) || - (op2->value.type->id == ZigTypeIdEnumLiteral && op1->value.type->id == ZigTypeIdUnion))) + (op1->value->type->id == ZigTypeIdEnumLiteral && op2->value->type->id == ZigTypeIdUnion) || + (op2->value->type->id == ZigTypeIdEnumLiteral && op1->value->type->id == ZigTypeIdUnion))) { // Support equality comparison between a union's tag value and a enum literal - IrInstruction *union_val = op1->value.type->id == ZigTypeIdUnion ? op1 : op2; - IrInstruction *enum_val = op1->value.type->id == ZigTypeIdUnion ? op2 : op1; + IrInstruction *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2; + IrInstruction *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1; - ZigType *tag_type = union_val->value.type->data.unionation.tag_type; + ZigType *tag_type = union_val->value->type->data.unionation.tag_type; assert(tag_type != nullptr); IrInstruction *casted_union = ir_implicit_cast(ira, union_val, tag_type); - if (type_is_invalid(casted_union->value.type)) + if (type_is_invalid(casted_union->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_val = ir_implicit_cast(ira, enum_val, tag_type); - if (type_is_invalid(casted_val->value.type)) + if (type_is_invalid(casted_val->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_union)) { - ConstExprValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad); + ZigValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad); if (!const_union_val) return ira->codegen->invalid_instruction; - ConstExprValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad); + ZigValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad); if (!const_enum_val) return ira->codegen->invalid_instruction; @@ -13444,17 +15168,17 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, op_id, casted_union, casted_val, bin_op_instruction->safety_check_on); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } - if (op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) { + if (op1->value->type->id == ZigTypeIdErrorSet && op2->value->type->id == ZigTypeIdErrorSet) { if (!is_equality_cmp) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); return ira->codegen->invalid_instruction; } - ZigType *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node); + ZigType *intersect_type = get_error_set_intersection(ira, op1->value->type, op2->value->type, source_node); if (type_is_invalid(intersect_type)) { return ira->codegen->invalid_instruction; } @@ -13467,7 +15191,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * // (and make it comptime known) // this is a function which is evaluated at comptime and returns an inferred error set will have an empty // error set. - if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) { + if (op1->value->type->data.error_set.err_count == 0 || op2->value->type->data.error_set.err_count == 0) { bool are_equal = false; bool answer; if (op_id == IrBinOpCmpEq) { @@ -13484,10 +15208,10 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (intersect_type->data.error_set.err_count == 0) { ir_add_error_node(ira, source_node, buf_sprintf("error sets '%s' and '%s' have no common errors", - buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name))); + buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } - if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) { + if (op1->value->type->data.error_set.err_count == 1 && op2->value->type->data.error_set.err_count == 1) { bool are_equal = true; bool answer; if (op_id == IrBinOpCmpEq) { @@ -13502,10 +15226,10 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * } if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; @@ -13525,10 +15249,17 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, op_id, op1, op2, bin_op_instruction->safety_check_on); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } + if (type_is_numeric(op1->value->type) && type_is_numeric(op2->value->type)) { + // This operation allows any combination of integer and float types, regardless of the + // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for + // numeric types. + return ir_analyze_bin_op_cmp_numeric(ira, &bin_op_instruction->base, op1, op2, op_id); + } + IrInstruction *instructions[] = {op1, op2}; ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) @@ -13544,8 +15275,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdVector: - operator_allowed = true; - break; + zig_unreachable(); // handled with the type_is_numeric checks above case ZigTypeIdBool: case ZigTypeIdMetaType: @@ -13554,7 +15284,6 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdFn: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdEnum: case ZigTypeIdEnumLiteral: case ZigTypeIdAnyFrame: @@ -13606,125 +15335,44 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * } if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { - { - // Before resolving the values, we special case comparisons against zero. These can often be done - // without resolving lazy values, preventing potential dependency loops. - Cmp op1_cmp_zero; - if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op1->value, &op1_cmp_zero))) { - if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; - return ira->codegen->invalid_instruction; - } - Cmp op2_cmp_zero; - if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op2->value, &op2_cmp_zero))) { - if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; - return ira->codegen->invalid_instruction; - } - bool can_cmp_zero = false; - Cmp cmp_result; - if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpEQ; - } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpGT; - } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) { - can_cmp_zero = true; - cmp_result = CmpGT; - } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) { - can_cmp_zero = true; - cmp_result = CmpGT; - } - if (can_cmp_zero) { - bool answer = resolve_cmp_op_id(op_id, cmp_result); - return ir_const_bool(ira, &bin_op_instruction->base, answer); - } - } -never_mind_just_calculate_it_normally: - - ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); + ZigValue *op1_val = one_possible_value ? casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = one_possible_value ? casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; if (resolved_type->id != ZigTypeIdVector) return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, bin_op_instruction, op_id, one_possible_value); IrInstruction *result = ir_const(ira, &bin_op_instruction->base, get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); - result->value.data.x_array.data.s_none.elements = + result->value->data.x_array.data.s_none.elements = create_const_vals(resolved_type->data.vector.len); - expand_undef_array(ira->codegen, &result->value); + expand_undef_array(ira->codegen, result->value); for (size_t i = 0;i < resolved_type->data.vector.len;i++) { IrInstruction *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, &op1_val->data.x_array.data.s_none.elements[i], &op2_val->data.x_array.data.s_none.elements[i], bin_op_instruction, op_id, one_possible_value); - copy_const_val(&result->value.data.x_array.data.s_none.elements[i], &cur_res->value, false); + copy_const_val(&result->value->data.x_array.data.s_none.elements[i], cur_res->value); } return result; } - // some comparisons with unsigned numbers can be evaluated - if (resolved_type->id == ZigTypeIdInt && !resolved_type->data.integral.is_signed) { - ConstExprValue *known_left_val; - IrBinOp flipped_op_id; - if (instr_is_comptime(casted_op1)) { - known_left_val = ir_resolve_const(ira, casted_op1, UndefBad); - if (known_left_val == nullptr) - return ira->codegen->invalid_instruction; - - flipped_op_id = op_id; - } else if (instr_is_comptime(casted_op2)) { - known_left_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (known_left_val == nullptr) - return ira->codegen->invalid_instruction; - - if (op_id == IrBinOpCmpLessThan) { - flipped_op_id = IrBinOpCmpGreaterThan; - } else if (op_id == IrBinOpCmpGreaterThan) { - flipped_op_id = IrBinOpCmpLessThan; - } else if (op_id == IrBinOpCmpLessOrEq) { - flipped_op_id = IrBinOpCmpGreaterOrEq; - } else if (op_id == IrBinOpCmpGreaterOrEq) { - flipped_op_id = IrBinOpCmpLessOrEq; - } else { - flipped_op_id = op_id; - } - } else { - known_left_val = nullptr; - } - if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ && - (flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan)) - { - bool answer = (flipped_op_id == IrBinOpCmpLessOrEq); - return ir_const_bool(ira, &bin_op_instruction->base, answer); - } - } - IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); if (resolved_type->id == ZigTypeIdVector) { - result->value.type = get_vector_type(ira->codegen, resolved_type->data.vector.len, + result->value->type = get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool); } else { - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; } return result; } static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry, - ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) + ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) { bool is_int; bool is_float; @@ -13765,7 +15413,6 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_in case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: - case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBinOr: assert(is_int); @@ -13867,7 +15514,7 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_in } } else { float_div_trunc(out_val, op1_val, op2_val); - ConstExprValue remainder = {}; + ZigValue remainder = {}; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); @@ -13905,10 +15552,10 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_in // This works on operands that have already been checked to be comptime known. static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr, - ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val) + ZigType *type_entry, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val) { IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry); - ConstExprValue *out_val = &result_instruction->value; + ZigValue *out_val = result_instruction->value; if (type_entry->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, op1_val); expand_undef_array(ira->codegen, op2_val); @@ -13917,9 +15564,9 @@ static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_i size_t len = type_entry->data.vector.len; ZigType *scalar_type = type_entry->data.vector.elem_type; for (size_t i = 0; i < len; i += 1) { - ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; - ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; - ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_op1_val->type == scalar_type); assert(scalar_op2_val->type == scalar_type); assert(scalar_out_val->type == scalar_type); @@ -13943,52 +15590,56 @@ static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_i static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; - if (op1->value.type->id != ZigTypeIdInt && op1->value.type->id != ZigTypeIdComptimeInt) { + if (op1->value->type->id != ZigTypeIdInt && op1->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, bin_op_instruction->op1, buf_sprintf("bit shifting operation expected integer type, found '%s'", - buf_ptr(&op1->value.type->name))); + buf_ptr(&op1->value->type->name))); return ira->codegen->invalid_instruction; } IrInstruction *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; - if (op2->value.type->id != ZigTypeIdInt && op2->value.type->id != ZigTypeIdComptimeInt) { + if (op2->value->type->id != ZigTypeIdInt && op2->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, bin_op_instruction->op2, buf_sprintf("shift amount has to be an integer type, but found '%s'", - buf_ptr(&op2->value.type->name))); + buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } IrInstruction *casted_op2; IrBinOp op_id = bin_op_instruction->op_id; - if (op1->value.type->id == ZigTypeIdComptimeInt) { + if (op1->value->type->id == ZigTypeIdComptimeInt) { casted_op2 = op2; if (op_id == IrBinOpBitShiftLeftLossy) { op_id = IrBinOpBitShiftLeftExact; } - if (casted_op2->value.data.x_bigint.is_negative) { + if (casted_op2->value->data.x_bigint.is_negative) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &casted_op2->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &casted_op2->value->data.x_bigint, 10); ir_add_error(ira, casted_op2, buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); return ira->codegen->invalid_instruction; } } else { ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, - op1->value.type->data.integral.bit_count - 1); + op1->value->type->data.integral.bit_count - 1); if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy && - op2->value.type->id == ZigTypeIdComptimeInt) { - if (!bigint_fits_in_bits(&op2->value.data.x_bigint, + op2->value->type->id == ZigTypeIdComptimeInt) { + + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_instruction; + if (!bigint_fits_in_bits(&op2_val->data.x_bigint, shift_amt_type->data.integral.bit_count, - op2->value.data.x_bigint.is_negative)) { + op2_val->data.x_bigint.is_negative)) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); ErrorMsg* msg = ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("RHS of shift is too large for LHS type")); @@ -14009,30 +15660,30 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b } if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; - return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val); - } else if (op1->value.type->id == ZigTypeIdComptimeInt) { + return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value->type, op1_val, op_id, op2_val); + } else if (op1->value->type->id == ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); return ira->codegen->invalid_instruction; - } else if (instr_is_comptime(casted_op2) && bigint_cmp_zero(&casted_op2->value.data.x_bigint) == CmpEQ) { + } else if (instr_is_comptime(casted_op2) && bigint_cmp_zero(&casted_op2->value->data.x_bigint) == CmpEQ) { IrInstruction *result = ir_build_cast(&ira->new_irb, bin_op_instruction->base.scope, - bin_op_instruction->base.source_node, op1->value.type, op1, CastOpNoop); - result->value.type = op1->value.type; + bin_op_instruction->base.source_node, op1->value->type, op1, CastOpNoop); + result->value->type = op1->value->type; return result; } IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, bin_op_instruction->base.source_node, op_id, op1, casted_op2, bin_op_instruction->safety_check_on); - result->value.type = op1->value.type; + result->value->type = op1->value->type; return result; } @@ -14072,15 +15723,12 @@ static bool ok_float_op(IrBinOp op) { case IrBinOpRemUnspecified: case IrBinOpArrayCat: case IrBinOpArrayMult: - case IrBinOpMergeErrorSets: return false; } zig_unreachable(); } static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { - if (lhs_type->id != ZigTypeIdPointer) - return false; switch (op) { case IrBinOpAdd: case IrBinOpSub: @@ -14088,66 +15736,100 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { default: return false; } + if (lhs_type->id != ZigTypeIdPointer) + return false; switch (lhs_type->data.pointer.ptr_len) { case PtrLenSingle: - return false; + return lhs_type->data.pointer.child_type->id == ZigTypeIdArray; case PtrLenUnknown: case PtrLenC: - break; + return true; } - return true; + zig_unreachable(); } static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) { Error err; IrInstruction *op1 = instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; IrBinOp op_id = instruction->op_id; // look for pointer math - if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) { + if (is_pointer_arithmetic_allowed(op1->value->type, op_id)) { IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); - if (type_is_invalid(casted_op2->value.type)) + if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_instruction; // If either operand is undef, result is undef. - ConstExprValue *op1_val = nullptr; - ConstExprValue *op2_val = nullptr; + ZigValue *op1_val = nullptr; + ZigValue *op2_val = nullptr; if (instr_is_comptime(op1)) { op1_val = ir_resolve_const(ira, op1, UndefOk); if (op1_val == nullptr) return ira->codegen->invalid_instruction; if (op1_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base, op1->value.type); + return ir_const_undef(ira, &instruction->base, op1->value->type); } if (instr_is_comptime(casted_op2)) { op2_val = ir_resolve_const(ira, casted_op2, UndefOk); if (op2_val == nullptr) return ira->codegen->invalid_instruction; if (op2_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base, op1->value.type); + return ir_const_undef(ira, &instruction->base, op1->value->type); } - if (op2_val != nullptr && op1_val != nullptr && - (op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr || - op1->value.data.x_ptr.special == ConstPtrSpecialNull)) - { - uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ? - 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; + ZigType *elem_type = op1->value->type->data.pointer.child_type; + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + // NOTE: this variable is meaningful iff op2_val is not null! + uint64_t byte_offset; + if (op2_val != nullptr) { uint64_t elem_offset; if (!ir_resolve_usize(ira, casted_op2, &elem_offset)) return ira->codegen->invalid_instruction; - ZigType *elem_type = op1_val->type->data.pointer.child_type; - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + + byte_offset = type_size(ira->codegen, elem_type) * elem_offset; + } + + // Fast path for cases where the RHS is zero + if (op2_val != nullptr && byte_offset == 0) { + return op1; + } + + ZigType *result_type = op1->value->type; + // Calculate the new alignment of the pointer + { + uint32_t align_bytes; + if ((err = resolve_ptr_align(ira, op1->value->type, &align_bytes))) return ira->codegen->invalid_instruction; - uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset; + + // If the addend is not a comptime-known value we can still count on + // it being a multiple of the type size + uint32_t addend = op2_val ? byte_offset : type_size(ira->codegen, elem_type); + + // The resulting pointer is aligned to the lcd between the + // offset (an arbitrary number) and the alignment factor (always + // a power of two, non zero) + uint32_t new_align = 1 << ctzll(addend | align_bytes); + // Rough guard to prevent overflows + assert(new_align); + result_type = adjust_ptr_align(ira->codegen, result_type, new_align); + } + + if (op2_val != nullptr && op1_val != nullptr && + (op1->value->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op1->value->data.x_ptr.special == ConstPtrSpecialNull)) + { + uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ? + 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; uint64_t new_addr; if (op_id == IrBinOpAdd) { new_addr = start_addr + byte_offset; @@ -14156,16 +15838,16 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { zig_unreachable(); } - IrInstruction *result = ir_const(ira, &instruction->base, op1_val->type); - result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value.data.x_ptr.data.hard_coded_addr.addr = new_addr; + IrInstruction *result = ir_const(ira, &instruction->base, result_type); + result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + result->value->data.x_ptr.data.hard_coded_addr.addr = new_addr; return result; } IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope, instruction->base.source_node, op_id, op1, casted_op2, true); - result->value.type = op1->value.type; + result->value->type = result_type; return result; } @@ -14180,21 +15862,21 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp (resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) || resolved_type->id == ZigTypeIdFloat || (resolved_type->id == ZigTypeIdComptimeFloat && - ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) != - (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) || + ((bigfloat_cmp_zero(&op1->value->data.x_bigfloat) != CmpGT) != + (bigfloat_cmp_zero(&op2->value->data.x_bigfloat) != CmpGT))) || (resolved_type->id == ZigTypeIdComptimeInt && - ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) != - (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT))) + ((bigint_cmp_zero(&op1->value->data.x_bigint) != CmpGT) != + (bigint_cmp_zero(&op2->value->data.x_bigint) != CmpGT))) ); if (op_id == IrBinOpDivUnspecified && is_int) { if (is_signed_div) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; @@ -14217,8 +15899,8 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (!ok) { ir_add_error(ira, &instruction->base, buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", - buf_ptr(&op1->value.type->name), - buf_ptr(&op2->value.type->name))); + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } } else { @@ -14228,16 +15910,16 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (is_signed_div && (is_int || is_float)) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; if (is_int) { - ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; - if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { + if (bigint_cmp_zero(&op2->value->data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem ok = true; @@ -14253,17 +15935,17 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; - if (float_cmp_zero(&casted_op2->value) == CmpEQ) { + if (float_cmp_zero(casted_op2->value) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem ok = true; } else { - ConstExprValue rem_result = {}; - ConstExprValue mod_result = {}; + ZigValue rem_result = {}; + ZigValue mod_result = {}; float_rem(&rem_result, op1_val, op2_val); float_mod(&mod_result, op1_val, op2_val); ok = float_cmp(&rem_result, &mod_result) == CmpEQ; @@ -14273,8 +15955,8 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (!ok) { ir_add_error(ira, &instruction->base, buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", - buf_ptr(&op1->value.type->name), - buf_ptr(&op2->value.type->name))); + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } } @@ -14298,8 +15980,8 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp AstNode *source_node = instruction->base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", - buf_ptr(&op1->value.type->name), - buf_ptr(&op2->value.type->name))); + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } @@ -14322,10 +16004,10 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; @@ -14334,30 +16016,139 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope, instruction->base.source_node, op_id, casted_op1, casted_op2, instruction->safety_check_on); - result->value.type = resolved_type; + result->value->type = resolved_type; + return result; +} + +static IrInstruction *ir_analyze_tuple_cat(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *op1, IrInstruction *op2) +{ + Error err; + ZigType *op1_type = op1->value->type; + ZigType *op2_type = op2->value->type; + + uint32_t op1_field_count = op1_type->data.structure.src_field_count; + uint32_t op2_field_count = op2_type->data.structure.src_field_count; + + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), + source_instr->scope, source_instr->source_node, bare_name); + ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, + ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); + new_type->data.structure.special = StructSpecialInferredTuple; + new_type->data.structure.resolve_status = ResolveStatusBeingInferred; + + bool is_comptime = ir_should_inline(ira->new_irb.exec, source_instr->scope); + + IrInstruction *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), + new_type, nullptr, false, false, true); + uint32_t new_field_count = op1_field_count + op2_field_count; + + new_type->data.structure.src_field_count = new_field_count; + new_type->data.structure.fields = realloc_type_struct_fields(new_type->data.structure.fields, + 0, new_field_count); + for (uint32_t i = 0; i < new_field_count; i += 1) { + TypeStructField *src_field; + if (i < op1_field_count) { + src_field = op1_type->data.structure.fields[i]; + } else { + src_field = op2_type->data.structure.fields[i - op1_field_count]; + } + TypeStructField *new_field = new_type->data.structure.fields[i]; + new_field->name = buf_sprintf("%" PRIu32, i); + new_field->type_entry = src_field->type_entry; + new_field->type_val = src_field->type_val; + new_field->src_index = i; + new_field->decl_node = src_field->decl_node; + new_field->init_val = src_field->init_val; + new_field->is_comptime = src_field->is_comptime; + } + if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + + ZigList const_ptrs = {}; + IrInstruction *first_non_const_instruction = nullptr; + for (uint32_t i = 0; i < new_field_count; i += 1) { + TypeStructField *dst_field = new_type->data.structure.fields[i]; + IrInstruction *src_struct_op; + TypeStructField *src_field; + if (i < op1_field_count) { + src_field = op1_type->data.structure.fields[i]; + src_struct_op = op1; + } else { + src_field = op2_type->data.structure.fields[i - op1_field_count]; + src_struct_op = op2; + } + IrInstruction *field_value = ir_analyze_struct_value_field_value(ira, source_instr, + src_struct_op, src_field); + if (type_is_invalid(field_value->value->type)) + return ira->codegen->invalid_instruction; + IrInstruction *dest_ptr = ir_analyze_struct_field_ptr(ira, source_instr, dst_field, + new_struct_ptr, new_type, true); + if (type_is_invalid(dest_ptr->value->type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(field_value)) { + const_ptrs.append(dest_ptr); + } else { + first_non_const_instruction = field_value; + } + IrInstruction *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, dest_ptr, field_value, + true); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_instruction; + } + if (const_ptrs.length != new_field_count) { + new_struct_ptr->value->special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstruction *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value->special == ConstValSpecialStatic); + if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { + // This field will be generated comptime; no need to do this. + continue; + } + IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr); + elem_result_loc->value->special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false); + } + } + IrInstruction *result = ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); + if (instr_is_comptime(result)) + return result; + + if (is_comptime) { + ir_add_error_node(ira, first_non_const_instruction->source_node, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_instruction; + } + return result; } static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->child; - ZigType *op1_type = op1->value.type; + ZigType *op1_type = op1->value->type; if (type_is_invalid(op1_type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = instruction->op2->child; - ZigType *op2_type = op2->value.type; + ZigType *op2_type = op2->value->type; if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; - ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (is_tuple(op1_type) && is_tuple(op2_type)) { + return ir_analyze_tuple_cat(ira, &instruction->base, op1, op2); + } + + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); if (!op1_val) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); if (!op2_val) return ira->codegen->invalid_instruction; - ConstExprValue *op1_array_val; + ZigValue *sentinel1 = nullptr; + ZigValue *op1_array_val; size_t op1_array_index; size_t op1_array_end; ZigType *child_type; @@ -14366,31 +16157,45 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op1_array_val = op1_val; op1_array_index = 0; op1_array_end = op1_type->data.array.len; + sentinel1 = op1_type->data.array.sentinel; } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && - op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray && - op1_val->data.x_ptr.data.base_array.is_cstr) + op1_type->data.pointer.sentinel != nullptr && + op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { child_type = op1_type->data.pointer.child_type; op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; - op1_array_end = op1_array_val->type->data.array.len - 1; + op1_array_end = op1_array_val->type->data.array.len; + sentinel1 = op1_type->data.pointer.sentinel; } else if (is_slice(op1_type)) { - ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index]->type_entry; child_type = ptr_type->data.pointer.child_type; - ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val = op1_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - ConstExprValue *len_val = &op1_val->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = op1_val->data.x_struct.fields[slice_len_index]; op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint); + sentinel1 = ptr_type->data.pointer.sentinel; + } else if (op1_type->id == ZigTypeIdPointer && op1_type->data.pointer.ptr_len == PtrLenSingle && + op1_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *array_type = op1_type->data.pointer.child_type; + child_type = array_type->data.array.child_type; + op1_array_val = const_ptr_pointee(ira, ira->codegen, op1_val, op1->source_node); + if (op1_array_val == nullptr) + return ira->codegen->invalid_instruction; + op1_array_index = 0; + op1_array_end = array_type->data.array.len; + sentinel1 = array_type->data.array.sentinel; } else { - ir_add_error(ira, op1, - buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); + ir_add_error(ira, op1, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value->type->name))); return ira->codegen->invalid_instruction; } - ConstExprValue *op2_array_val; + ZigValue *sentinel2 = nullptr; + ZigValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; bool op2_type_valid; @@ -14399,133 +16204,191 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; + sentinel2 = op2_type->data.array.sentinel; } else if (op2_type->id == ZigTypeIdPointer && - op2_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && - op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray && - op2_val->data.x_ptr.data.base_array.is_cstr) + op2_type->data.pointer.sentinel != nullptr && + op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray) { - op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8; + op2_type_valid = op2_type->data.pointer.child_type == child_type; op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; - op2_array_end = op2_array_val->type->data.array.len - 1; + op2_array_end = op2_array_val->type->data.array.len; + + sentinel2 = op2_type->data.pointer.sentinel; } else if (is_slice(op2_type)) { - ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index]->type_entry; op2_type_valid = ptr_type->data.pointer.child_type == child_type; - ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val = op2_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = op2_val->data.x_struct.fields[slice_len_index]; op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint); + + sentinel2 = ptr_type->data.pointer.sentinel; + } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle && + op2_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *array_type = op2_type->data.pointer.child_type; + op2_type_valid = array_type->data.array.child_type == child_type; + op2_array_val = const_ptr_pointee(ira, ira->codegen, op2_val, op2->source_node); + if (op2_array_val == nullptr) + return ira->codegen->invalid_instruction; + op2_array_index = 0; + op2_array_end = array_type->data.array.len; + + sentinel2 = array_type->data.array.sentinel; } else { ir_add_error(ira, op2, - buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); + buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } if (!op2_type_valid) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); + buf_ptr(&op2->value->type->name))); return ira->codegen->invalid_instruction; } + ZigValue *sentinel; + if (sentinel1 != nullptr && sentinel2 != nullptr) { + // When there is a sentinel mismatch, no sentinel on the result. The type system + // will catch this if it is a problem. + sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr; + } else if (sentinel1 != nullptr) { + sentinel = sentinel1; + } else if (sentinel2 != nullptr) { + sentinel = sentinel2; + } else { + sentinel = nullptr; + } + // The type of result is populated in the following if blocks IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; - ConstExprValue *out_array_val; + ZigValue *out_array_val; size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); - if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { - result->value.type = get_array_type(ira->codegen, child_type, new_len); + if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) { + out_array_val = create_const_vals(1); + out_array_val->special = ConstValSpecialStatic; + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - out_array_val = out_val; + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = out_array_val; + out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true); } else if (is_slice(op1_type) || is_slice(op2_type)) { - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - true, false, PtrLenUnknown, 0, 0, 0, false); - result->value.type = get_slice_type(ira->codegen, ptr_type); + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type, + true, false, PtrLenUnknown, 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, sentinel); + result->value->type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - out_val->data.x_struct.fields = create_const_vals(2); + out_val->data.x_struct.fields = alloc_const_vals_ptrs(2); - out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type; - out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic; - out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val; - out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0; + out_val->data.x_struct.fields[slice_ptr_index]->type = ptr_type; + out_val->data.x_struct.fields[slice_ptr_index]->special = ConstValSpecialStatic; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.array_val = out_array_val; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.elem_index = 0; - out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize; - out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic; - bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len); + out_val->data.x_struct.fields[slice_len_index]->type = ira->codegen->builtin_types.entry_usize; + out_val->data.x_struct.fields[slice_len_index]->special = ConstValSpecialStatic; + bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index]->data.x_bigint, new_len); + } else if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { + result->value->type = get_array_type(ira->codegen, child_type, new_len, sentinel); + out_array_val = out_val; } else { - new_len += 1; // null byte - - // TODO make this `[*]null T` instead of `[*]T` - result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false); - + result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, + 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len); + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.data.base_array.is_cstr = true; out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; } if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && - op2_array_val->data.x_array.special == ConstArraySpecialUndef) { + op2_array_val->data.x_array.special == ConstArraySpecialUndef) + { out_array_val->data.x_array.special = ConstArraySpecialUndef; return result; } - out_array_val->data.x_array.data.s_none.elements = create_const_vals(new_len); + uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0); + out_array_val->data.x_array.data.s_none.elements = create_const_vals(full_len); // TODO handle the buf case here for an optimization expand_undef_array(ira->codegen, op1_array_val); expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { - copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index], - &op1_array_val->data.x_array.data.s_none.elements[i], true); + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; } for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { - copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index], - &op2_array_val->data.x_array.data.s_none.elements[i], true); + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; } - if (next_index < new_len) { - ConstExprValue *null_byte = &out_array_val->data.x_array.data.s_none.elements[next_index]; - init_const_unsigned_negative(null_byte, child_type, 0, false); + if (next_index < full_len) { + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(elem_dest_val, sentinel); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; next_index += 1; } - assert(next_index == new_len); + assert(next_index == full_len); return result; } static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *array_val = ir_resolve_const(ira, op1, UndefBad); - if (!array_val) + bool want_ptr_to_array = false; + ZigType *array_type; + ZigValue *array_val; + if (op1->value->type->id == ZigTypeIdArray) { + array_type = op1->value->type; + array_val = ir_resolve_const(ira, op1, UndefOk); + if (array_val == nullptr) + return ira->codegen->invalid_instruction; + } else if (op1->value->type->id == ZigTypeIdPointer && op1->value->type->data.pointer.ptr_len == PtrLenSingle && + op1->value->type->data.pointer.child_type->id == ZigTypeIdArray) + { + array_type = op1->value->type->data.pointer.child_type; + IrInstruction *array_inst = ir_get_deref(ira, op1, op1, nullptr); + if (type_is_invalid(array_inst->value->type)) + return ira->codegen->invalid_instruction; + array_val = ir_resolve_const(ira, array_inst, UndefOk); + if (array_val == nullptr) + return ira->codegen->invalid_instruction; + want_ptr_to_array = true; + } else { + ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value->type->name))); return ira->codegen->invalid_instruction; + } uint64_t mult_amt; if (!ir_resolve_usize(ira, op2, &mult_amt)) return ira->codegen->invalid_instruction; - ZigType *array_type = op1->value.type; - if (array_type->id != ZigTypeIdArray) { - ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value.type->name))); - return ira->codegen->invalid_instruction; - } - uint64_t old_array_len = array_type->data.array.len; uint64_t new_array_len; @@ -14535,45 +16398,63 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } ZigType *child_type = array_type->data.array.child_type; + ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len, + array_type->data.array.sentinel); - IrInstruction *result = ir_const(ira, &instruction->base, - get_array_type(ira->codegen, child_type, new_array_len)); - ConstExprValue *out_val = &result->value; - if (array_val->data.x_array.special == ConstArraySpecialUndef) { - out_val->data.x_array.special = ConstArraySpecialUndef; - return result; - } + IrInstruction *array_result; + if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) { + array_result = ir_const_undef(ira, &instruction->base, result_array_type); + } else { + array_result = ir_const(ira, &instruction->base, result_array_type); + ZigValue *out_val = array_result->value; - switch (type_has_one_possible_value(ira->codegen, result->value.type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_instruction; - case OnePossibleValueYes: - return result; - case OnePossibleValueNo: - break; - } + switch (type_has_one_possible_value(ira->codegen, result_array_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + goto skip_computation; + case OnePossibleValueNo: + break; + } - // TODO optimize the buf case - expand_undef_array(ira->codegen, array_val); - out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len); + // TODO optimize the buf case + expand_undef_array(ira->codegen, array_val); + size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0; + out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len + extra_null_term); - uint64_t i = 0; - for (uint64_t x = 0; x < mult_amt; x += 1) { - for (uint64_t y = 0; y < old_array_len; y += 1) { - ConstExprValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; - copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y], false); + uint64_t i = 0; + for (uint64_t x = 0; x < mult_amt; x += 1) { + for (uint64_t y = 0; y < old_array_len; y += 1) { + ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; + copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_val; + elem_dest_val->parent.data.p_array.elem_index = i; + i += 1; + } + } + assert(i == new_array_len); + + if (array_type->data.array.sentinel != nullptr) { + ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; + copy_const_val(elem_dest_val, array_type->data.array.sentinel); elem_dest_val->parent.id = ConstParentIdArray; elem_dest_val->parent.data.p_array.array_val = out_val; elem_dest_val->parent.data.p_array.elem_index = i; i += 1; } } - assert(i == new_array_len); - - return result; +skip_computation: + if (want_ptr_to_array) { + return ir_get_ref(ira, &instruction->base, array_result, true, false); + } else { + return array_result; + } } -static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { +static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, + IrInstructionMergeErrSets *instruction) +{ ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child); if (type_is_invalid(op1_type)) return ira->codegen->invalid_instruction; @@ -14596,18 +16477,20 @@ static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionB return ira->codegen->invalid_instruction; } - ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); + size_t errors_count = ira->codegen->errors_by_index.length; + ErrorTableEntry **errors = allocate(errors_count, "ErrorTableEntry *"); for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } - ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type); - free(errors); + ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); + deallocate(errors, errors_count, "ErrorTableEntry *"); return ir_const_type(ira, &instruction->base, result_type); } + static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; switch (op_id) { @@ -14649,8 +16532,6 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); - case IrBinOpMergeErrorSets: - return ir_analyze_merge_error_sets(ira, bin_op_instruction); } zig_unreachable(); } @@ -14682,31 +16563,31 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruction *var_ptr = decl_var_instruction->ptr->child; // if this is null, a compiler error happened and did not initialize the variable. // if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation. - if (var_ptr == nullptr || type_is_invalid(var_ptr->value.type)) { + if (var_ptr == nullptr || type_is_invalid(var_ptr->value->type)) { ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base); var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } // The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value. - ir_assert(var_ptr->value.type->id == ZigTypeIdPointer, &decl_var_instruction->base); + ir_assert(var_ptr->value->type->id == ZigTypeIdPointer, &decl_var_instruction->base); - ZigType *result_type = var_ptr->value.type->data.pointer.child_type; + ZigType *result_type = var_ptr->value->type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { zig_unreachable(); } - ConstExprValue *init_val = nullptr; - if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { - init_val = const_ptr_pointee(ira, ira->codegen, &var_ptr->value, decl_var_instruction->base.source_node); + ZigValue *init_val = nullptr; + if (instr_is_comptime(var_ptr) && var_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + init_val = const_ptr_pointee(ira, ira->codegen, var_ptr->value, decl_var_instruction->base.source_node); if (is_comptime_var) { if (var->gen_is_const) { var->const_value = init_val; } else { var->const_value = create_const_vals(1); - copy_const_val(var->const_value, init_val, false); + copy_const_val(var->const_value, init_val); } } } @@ -14732,7 +16613,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, result_type = ira->codegen->builtin_types.entry_invalid; } else if (init_val->type->id == ZigTypeIdFn && init_val->special != ConstValSpecialUndef && - init_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + init_val->data.x_ptr.special == ConstPtrSpecialFunction && init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) { var_class_requires_const = true; @@ -14759,7 +16640,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, new_var->owner_exec = var->owner_exec; new_var->align_bytes = var->align_bytes; if (var->mem_slot_index != SIZE_MAX) { - ConstExprValue *vals = create_const_vals(1); + ZigValue *vals = create_const_vals(1); new_var->mem_slot_index = ira->exec_context.mem_slot_list.length; ira->exec_context.mem_slot_list.append(vals); } @@ -14793,30 +16674,31 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, if (init_val != nullptr && value_is_comptime(init_val)) { // Resolve ConstPtrMutInfer if (var->gen_is_const) { - var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeConst; } else if (is_comptime_var) { - var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; } else { // we need a runtime ptr but we have a comptime val. // since it's a comptime val there are no instructions for it. // we memcpy the init value here IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr, nullptr); - if (type_is_invalid(deref->value.type)) { + if (type_is_invalid(deref->value->type)) { var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } // If this assertion trips, something is wrong with the IR instructions, because // we expected the above deref to return a constant value, but it created a runtime // instruction. - assert(deref->value.special != ConstValSpecialRuntime); - var_ptr->value.special = ConstValSpecialRuntime; + assert(deref->value->special != ConstValSpecialRuntime); + var_ptr->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref, false); } if (instr_is_comptime(var_ptr) && var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); - ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); - copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); + ZigValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); + copy_const_val(mem_slot, init_val); + ira_ref(var->owner_exec->analysis); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -14837,26 +16719,66 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, } static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { - Error err; - - IrInstruction *name = instruction->name->child; - Buf *symbol_name = ir_resolve_str(ira, name); - if (symbol_name == nullptr) { - return ira->codegen->invalid_instruction; - } - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) { + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_instruction; + + IrInstruction *options = instruction->options->child; + if (type_is_invalid(options->value->type)) + return ira->codegen->invalid_instruction; + + ZigType *options_type = options->value->type; + assert(options_type->id == ZigTypeIdStruct); + + TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name")); + ir_assert(name_field != nullptr, &instruction->base); + IrInstruction *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, name_field); + if (type_is_invalid(name_inst->value->type)) + return ira->codegen->invalid_instruction; + + TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage")); + ir_assert(linkage_field != nullptr, &instruction->base); + IrInstruction *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, linkage_field); + if (type_is_invalid(linkage_inst->value->type)) + return ira->codegen->invalid_instruction; + + TypeStructField *section_field = find_struct_type_field(options_type, buf_create_from_str("section")); + ir_assert(section_field != nullptr, &instruction->base); + IrInstruction *section_inst = ir_analyze_struct_value_field_value(ira, &instruction->base, options, section_field); + if (type_is_invalid(section_inst->value->type)) + return ira->codegen->invalid_instruction; + + // The `section` field is optional, we have to unwrap it first + IrInstruction *non_null_check = ir_analyze_test_non_null(ira, &instruction->base, section_inst); + bool is_non_null; + if (!ir_resolve_bool(ira, non_null_check, &is_non_null)) + return ira->codegen->invalid_instruction; + + IrInstruction *section_str_inst = nullptr; + if (is_non_null) { + section_str_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base, section_inst, false); + if (type_is_invalid(section_str_inst->value->type)) + return ira->codegen->invalid_instruction; + } + + // Resolve all the comptime values + Buf *symbol_name = ir_resolve_str(ira, name_inst); + if (!symbol_name) + return ira->codegen->invalid_instruction; + + if (buf_len(symbol_name) < 1) { + ir_add_error(ira, name_inst, + buf_sprintf("exported symbol name cannot be empty")); return ira->codegen->invalid_instruction; } - GlobalLinkageId global_linkage_id = GlobalLinkageIdStrong; - if (instruction->linkage != nullptr) { - IrInstruction *linkage_value = instruction->linkage->child; - if (!ir_resolve_global_linkage(ira, linkage_value, &global_linkage_id)) { - return ira->codegen->invalid_instruction; - } - } + GlobalLinkageId global_linkage_id; + if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) + return ira->codegen->invalid_instruction; + + Buf *section_name = nullptr; + if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) + return ira->codegen->invalid_instruction; // TODO: This function needs to be audited. // It's not clear how all the different types are supposed to be handled. @@ -14875,14 +16797,15 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio return ira->codegen->invalid_instruction; } + Error err; bool want_var_export = false; - switch (target->value.type->id) { + switch (target->value->type->id) { case ZigTypeIdInvalid: case ZigTypeIdUnreachable: zig_unreachable(); case ZigTypeIdFn: { - assert(target->value.data.x_ptr.special == ConstPtrSpecialFunction); - ZigFn *fn_entry = target->value.data.x_ptr.data.fn.fn_entry; + assert(target->value->data.x_ptr.special == ConstPtrSpecialFunction); + ZigFn *fn_entry = target->value->data.x_ptr.data.fn.fn_entry; tld_fn->fn_entry = fn_entry; CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; switch (cc) { @@ -14897,60 +16820,68 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); } break; case CallingConventionC: - case CallingConventionNaked: case CallingConventionCold: + case CallingConventionNaked: + case CallingConventionInterrupt: + case CallingConventionSignal: case CallingConventionStdcall: - add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, - cc == CallingConventionC); + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: + add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); + fn_entry->section_name = section_name; break; } } break; case ZigTypeIdStruct: - if (is_slice(target->value.type)) { + if (is_slice(target->value->type)) { ir_add_error(ira, target, - buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value.type->name))); - } else if (target->value.type->data.structure.layout != ContainerLayoutExtern) { + buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value->type->name))); + } else if (target->value->type->data.structure.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported struct value must be declared extern")); - add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here")); + add_error_note(ira->codegen, msg, target->value->type->data.structure.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdUnion: - if (target->value.type->data.unionation.layout != ContainerLayoutExtern) { + if (target->value->type->data.unionation.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported union value must be declared extern")); - add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here")); + add_error_note(ira->codegen, msg, target->value->type->data.unionation.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdEnum: - if (target->value.type->data.enumeration.layout != ContainerLayoutExtern) { + if (target->value->type->data.enumeration.layout != ContainerLayoutExtern) { ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported enum value must be declared extern")); - add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here")); + add_error_note(ira->codegen, msg, target->value->type->data.enumeration.decl_node, buf_sprintf("declared here")); } else { want_var_export = true; } break; case ZigTypeIdArray: { bool ok_type; - if ((err = type_allowed_in_extern(ira->codegen, target->value.type->data.array.child_type, &ok_type))) + if ((err = type_allowed_in_extern(ira->codegen, target->value->type->data.array.child_type, &ok_type))) return ira->codegen->invalid_instruction; if (!ok_type) { ir_add_error(ira, target, buf_sprintf("array element type '%s' not extern-compatible", - buf_ptr(&target->value.type->data.array.child_type->name))); + buf_ptr(&target->value->type->data.array.child_type->name))); } else { want_var_export = true; } break; } case ZigTypeIdMetaType: { - ZigType *type_value = target->value.data.x_type; + ZigType *type_value = target->value->data.x_type; switch (type_value->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -15003,7 +16934,6 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: @@ -15026,15 +16956,14 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdVector: - zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); + zig_panic("TODO export const value of type %s", buf_ptr(&target->value->type->name)); case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdEnumLiteral: case ZigTypeIdFnFrame: case ZigTypeIdAnyFrame: ir_add_error(ira, target, - buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value->type->name))); break; } @@ -15045,6 +16974,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio IrInstructionVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); + var->section_name = section_name; } } @@ -15064,7 +16994,7 @@ static IrInstruction *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { IrInstruction *result = ir_const(ira, &instruction->base, optional_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; assert(get_codegen_ptr_type(optional_type) != nullptr); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; out_val->data.x_ptr.data.hard_coded_addr.addr = 0; @@ -15072,13 +17002,13 @@ static IrInstruction *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, instruction->base.source_node, instruction->optional); - new_instruction->value.type = optional_type; + new_instruction->value->type = optional_type; return new_instruction; } else { assert(ira->codegen->have_err_ret_tracing); IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, instruction->base.source_node, instruction->optional); - new_instruction->value.type = ptr_to_stack_trace_type; + new_instruction->value->type = ptr_to_stack_trace_type; return new_instruction; } } @@ -15087,11 +17017,11 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstructionErrorUnion *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueErrUnionType *lazy_err_union_type = allocate(1); - lazy_err_union_type->ira = ira; - result->value.data.x_lazy = &lazy_err_union_type->base; + LazyValueErrUnionType *lazy_err_union_type = allocate(1, "LazyValueErrUnionType"); + lazy_err_union_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_err_union_type->base; lazy_err_union_type->base.id = LazyValueIdErrUnionType; lazy_err_union_type->err_set_type = instruction->err_set->child; @@ -15110,31 +17040,32 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in { Error err; - ConstExprValue *pointee = create_const_vals(1); + ZigValue *pointee = create_const_vals(1); pointee->special = ConstValSpecialUndef; IrInstructionAllocaGen *result = ir_build_alloca_gen(ira, source_inst, align, name_hint); - result->base.value.special = ConstValSpecialStatic; - result->base.value.data.x_ptr.special = ConstPtrSpecialRef; - result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; - result->base.value.data.x_ptr.data.ref.pointee = pointee; + result->base.value->special = ConstValSpecialStatic; + result->base.value->data.x_ptr.special = ConstPtrSpecialRef; + result->base.value->data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; + result->base.value->data.x_ptr.data.ref.pointee = pointee; - if ((err = type_resolve(ira->codegen, var_type, ResolveStatusZeroBitsKnown))) + bool var_type_has_bits; + if ((err = type_has_bits2(ira->codegen, var_type, &var_type_has_bits))) return ira->codegen->invalid_instruction; if (align != 0) { if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; - if (!type_has_bits(var_type)) { - ir_add_error(ira, source_inst, - buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", - name_hint, buf_ptr(&var_type->name))); + if (!var_type_has_bits) { + ir_add_error(ira, source_inst, + buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", + name_hint, buf_ptr(&var_type->name))); return ira->codegen->invalid_instruction; } } - assert(result->base.value.data.x_ptr.special != ConstPtrSpecialInvalid); + assert(result->base.value->data.x_ptr.special != ConstPtrSpecialInvalid); pointee->type = var_type; - result->base.value.type = get_pointer_to_type_extra(ira->codegen, var_type, false, false, + result->base.value->type = get_pointer_to_type_extra(ira->codegen, var_type, false, false, PtrLenSingle, align, 0, 0, false); ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); @@ -15155,9 +17086,10 @@ static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInstruction *suspe case ResultLocIdNone: case ResultLocIdVar: case ResultLocIdBitCast: + case ResultLocIdCast: return nullptr; case ResultLocIdInstruction: - return result_loc->source_instruction->child->value.type; + return result_loc->source_instruction->child->value->type; case ResultLocIdReturn: return ira->explicit_return_type; case ResultLocIdPeer: @@ -15173,7 +17105,6 @@ static bool type_can_bit_cast(ZigType *t) { case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -15189,36 +17120,66 @@ static bool type_can_bit_cast(ZigType *t) { } static void set_up_result_loc_for_inferred_comptime(IrInstruction *ptr) { - ConstExprValue *undef_child = create_const_vals(1); - undef_child->type = ptr->value.type->data.pointer.child_type; + ZigValue *undef_child = create_const_vals(1); + undef_child->type = ptr->value->type->data.pointer.child_type; undef_child->special = ConstValSpecialUndef; - ptr->value.special = ConstValSpecialStatic; - ptr->value.data.x_ptr.mut = ConstPtrMutInfer; - ptr->value.data.x_ptr.special = ConstPtrSpecialRef; - ptr->value.data.x_ptr.data.ref.pointee = undef_child; + ptr->value->special = ConstValSpecialStatic; + ptr->value->data.x_ptr.mut = ConstPtrMutInfer; + ptr->value->data.x_ptr.special = ConstPtrSpecialRef; + ptr->value->data.x_ptr.data.ref.pointee = undef_child; } -static bool ir_result_has_type(ResultLoc *result_loc) { +static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out) { switch (result_loc->id) { case ResultLocIdInvalid: case ResultLocIdPeerParent: zig_unreachable(); case ResultLocIdNone: case ResultLocIdPeer: - return false; + *out = false; + return ErrorNone; case ResultLocIdReturn: case ResultLocIdInstruction: case ResultLocIdBitCast: - return true; + *out = true; + return ErrorNone; + case ResultLocIdCast: { + ResultLocCast *result_cast = reinterpret_cast(result_loc); + ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ErrorSemanticAnalyzeFail; + *out = (dest_type != ira->codegen->builtin_types.entry_var); + return ErrorNone; + } case ResultLocIdVar: - return reinterpret_cast(result_loc)->var->decl_node->data.variable_declaration.type != nullptr; + *out = reinterpret_cast(result_loc)->var->decl_node->data.variable_declaration.type != nullptr; + return ErrorNone; } zig_unreachable(); } +static IrInstruction *ir_resolve_no_result_loc(IrAnalyze *ira, IrInstruction *suspend_source_instr, + ResultLoc *result_loc, ZigType *value_type, bool force_runtime, bool non_null_comptime) +{ + if (type_is_invalid(value_type)) + return ira->codegen->invalid_instruction; + IrInstructionAllocaGen *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, ""); + alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false, + PtrLenSingle, 0, 0, 0, false); + set_up_result_loc_for_inferred_comptime(&alloca_gen->base); + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { + fn_entry->alloca_gen_list.append(alloca_gen); + } + result_loc->written = true; + result_loc->resolved_loc = &alloca_gen->base; + return result_loc->resolved_loc; +} + // when calling this function, at the callsite must check for result type noreturn and propagate it up static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime) + ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, + bool non_null_comptime, bool allow_discard) { Error err; if (result_loc->resolved_loc != nullptr) { @@ -15238,19 +17199,8 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe return nullptr; } // need to return a result location and don't have one. use a stack allocation - IrInstructionAllocaGen *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, ""); - if ((err = type_resolve(ira->codegen, value_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - alloca_gen->base.value.type = get_pointer_to_type_extra(ira->codegen, value_type, false, false, - PtrLenSingle, 0, 0, 0, false); - set_up_result_loc_for_inferred_comptime(&alloca_gen->base); - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { - fn_entry->alloca_gen_list.append(alloca_gen); - } - result_loc->written = true; - result_loc->resolved_loc = &alloca_gen->base; - return result_loc->resolved_loc; + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type, + force_runtime, non_null_comptime); } case ResultLocIdVar: { ResultLocVar *result_loc_var = reinterpret_cast(result_loc); @@ -15267,8 +17217,8 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe bool force_comptime; if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime)) return ira->codegen->invalid_instruction; - bool is_comptime = force_comptime || (value != nullptr && - value->value.special != ConstValSpecialRuntime && result_loc_var->var->gen_is_const); + bool is_comptime = force_comptime || (!force_runtime && value != nullptr && + value->value->special != ConstValSpecialRuntime && result_loc_var->var->gen_is_const); if (alloca_src->base.child == nullptr || is_comptime) { uint32_t align = 0; @@ -15277,15 +17227,19 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe } IrInstruction *alloca_gen; if (is_comptime && value != nullptr) { - if (align > value->value.global_refs->align) { - value->value.global_refs->align = align; + if (align > value->value->llvm_align) { + value->value->llvm_align = align; } alloca_gen = ir_get_ref(ira, result_loc->source_instruction, value, true, false); } else { alloca_gen = ir_analyze_alloca(ira, result_loc->source_instruction, value_type, align, alloca_src->name_hint, force_comptime); + if (force_runtime) { + alloca_gen->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + alloca_gen->value->special = ConstValSpecialRuntime; + } } - if (alloca_src->base.child != nullptr) { + if (alloca_src->base.child != nullptr && !result_loc->written) { alloca_src->base.child->ref_count = 0; } alloca_src->base.child = alloca_gen; @@ -15300,15 +17254,19 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe return result_loc->resolved_loc; } case ResultLocIdReturn: { + if (value != nullptr) { + reinterpret_cast(result_loc)->implicit_return_type_done = true; + ira->src_implicit_return_type_list.append(value); + } if (!non_null_comptime) { - bool is_comptime = value != nullptr && value->value.special != ConstValSpecialRuntime; + bool is_comptime = value != nullptr && value->value->special != ConstValSpecialRuntime; if (is_comptime) return nullptr; } - if ((err = type_resolve(ira->codegen, ira->explicit_return_type, ResolveStatusZeroBitsKnown))) { + bool has_bits; + if ((err = type_has_bits2(ira->codegen, ira->explicit_return_type, &has_bits))) return ira->codegen->invalid_instruction; - } - if (!type_has_bits(ira->explicit_return_type) || !handle_is_ptr(ira->explicit_return_type)) { + if (!has_bits || !handle_is_ptr(ira->explicit_return_type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry == nullptr || fn_entry->inferred_async_node == nullptr) { return nullptr; @@ -15332,8 +17290,8 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe value_type, value, force_runtime, non_null_comptime, true); result_peer->suspend_pos.basic_block_index = SIZE_MAX; result_peer->suspend_pos.instruction_index = SIZE_MAX; - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || - parent_result_loc->value.type->id == ZigTypeIdUnreachable) + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } @@ -15342,10 +17300,10 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe return result_loc->resolved_loc; } - bool is_comptime; - if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_comptime)) + bool is_condition_comptime; + if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_condition_comptime)) return ira->codegen->invalid_instruction; - if (is_comptime) { + if (is_condition_comptime) { peer_parent->skipped = true; if (non_null_comptime) { return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, @@ -15353,14 +17311,22 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe } return nullptr; } - if (ir_result_has_type(peer_parent->parent)) { - if (peer_parent->parent->id == ResultLocIdReturn && value != nullptr) { - reinterpret_cast(peer_parent->parent)->implicit_return_type_done = true; - ira->src_implicit_return_type_list.append(value); - } + bool peer_parent_has_type; + if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) + return ira->codegen->invalid_instruction; + if (peer_parent_has_type) { peer_parent->skipped = true; - return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime || !is_comptime, true, true); + IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, + value_type, value, force_runtime || !is_condition_comptime, true, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + peer_parent->parent->written = true; + result_loc->written = true; + result_loc->resolved_loc = parent_result_loc; + return result_loc->resolved_loc; } if (peer_parent->resolved_type == nullptr) { @@ -15377,17 +17343,100 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, peer_parent->resolved_type, nullptr, force_runtime, non_null_comptime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || - parent_result_loc->value.type->id == ZigTypeIdUnreachable) + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } - // because is_comptime is false, we mark this a runtime pointer - parent_result_loc->value.special = ConstValSpecialRuntime; + // because is_condition_comptime is false, we mark this a runtime pointer + parent_result_loc->value->special = ConstValSpecialRuntime; result_loc->written = true; result_loc->resolved_loc = parent_result_loc; return result_loc->resolved_loc; } + case ResultLocIdCast: { + if (value != nullptr && value->value->special != ConstValSpecialRuntime && !non_null_comptime) + return nullptr; + ResultLocCast *result_cast = reinterpret_cast(result_loc); + ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + + if (dest_type == ira->codegen->builtin_types.entry_var) { + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type, + force_runtime, non_null_comptime); + } + + IrInstruction *casted_value; + if (value != nullptr) { + casted_value = ir_implicit_cast(ira, value, dest_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_instruction; + dest_type = casted_value->value->type; + } else { + casted_value = nullptr; + } + + IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent, + dest_type, casted_value, force_runtime, non_null_comptime, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + + ZigType *parent_ptr_type = parent_result_loc->value->type; + assert(parent_ptr_type->id == ZigTypeIdPointer); + + if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); + if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) { + return ira->codegen->invalid_instruction; + } + if (!type_has_bits(value_type)) { + parent_ptr_align = 0; + } + // If we're casting from a sentinel-terminated array to a non-sentinel-terminated array, + // we actually need the result location pointer to *not* have a sentinel. Otherwise the generated + // memcpy will write an extra byte to the destination, and THAT'S NO GOOD. + ZigType *ptr_elem_type; + if (value_type->id == ZigTypeIdArray && value_type->data.array.sentinel != nullptr && + dest_type->id == ZigTypeIdArray && dest_type->data.array.sentinel == nullptr) + { + ptr_elem_type = get_array_type(ira->codegen, value_type->data.array.child_type, + value_type->data.array.len, nullptr); + } else { + ptr_elem_type = value_type; + } + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ptr_elem_type, + parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, + parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); + + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, + parent_result_loc->value->type, ptr_type, + result_cast->base.source_instruction->source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_instruction; + if (const_cast_result.id != ConstCastResultIdOk) { + if (allow_discard) { + return parent_result_loc; + } + // We will not be able to provide a result location for this value. Create + // a new result location. + result_cast->parent->written = false; + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type, + force_runtime, non_null_comptime); + } + + result_loc->written = true; + result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, + ptr_type, result_cast->base.source_instruction, false); + return result_loc->resolved_loc; + } case ResultLocIdBitCast: { ResultLocBitCast *result_bit_cast = reinterpret_cast(result_loc); ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child); @@ -15421,28 +17470,41 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe IrInstruction *bitcasted_value; if (value != nullptr) { bitcasted_value = ir_analyze_bit_cast(ira, result_loc->source_instruction, value, dest_type); + dest_type = bitcasted_value->value->type; } else { bitcasted_value = nullptr; } - if (bitcasted_value == nullptr || type_is_invalid(bitcasted_value->value.type)) { + if (bitcasted_value == nullptr || type_is_invalid(bitcasted_value->value->type)) { return bitcasted_value; } IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent, dest_type, bitcasted_value, force_runtime, non_null_comptime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || - parent_result_loc->value.type->id == ZigTypeIdUnreachable) + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) { return parent_result_loc; } - ZigType *parent_ptr_type = parent_result_loc->value.type; + ZigType *parent_ptr_type = parent_result_loc->value->type; assert(parent_ptr_type->id == ZigTypeIdPointer); - if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { + ZigType *child_type = parent_ptr_type->data.pointer.child_type; + + bool has_bits; + if ((err = type_has_bits2(ira->codegen, child_type, &has_bits))) { return ira->codegen->invalid_instruction; } + + // This happens when the bitCast result is assigned to _ + if (!has_bits) { + assert(allow_discard); + return parent_result_loc; + } + + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) { + return ira->codegen->invalid_instruction; + } + uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; @@ -15464,68 +17526,162 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s ResultLoc *result_loc_pass1, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime, bool allow_discard) { + Error err; if (!allow_discard && result_loc_pass1->id == ResultLocIdInstruction && instr_is_comptime(result_loc_pass1->source_instruction) && - result_loc_pass1->source_instruction->value.type->id == ZigTypeIdPointer && - result_loc_pass1->source_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard) + result_loc_pass1->source_instruction->value->type->id == ZigTypeIdPointer && + result_loc_pass1->source_instruction->value->data.x_ptr.special == ConstPtrSpecialDiscard) { result_loc_pass1 = no_result_loc(); } + bool was_already_resolved = result_loc_pass1->resolved_loc != nullptr; IrInstruction *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type, - value, force_runtime, non_null_comptime); - if (result_loc == nullptr || (instr_is_unreachable(result_loc) || type_is_invalid(result_loc->value.type))) + value, force_runtime, non_null_comptime, allow_discard); + if (result_loc == nullptr || (instr_is_unreachable(result_loc) || type_is_invalid(result_loc->value->type))) return result_loc; if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) && - result_loc_pass1->written && result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) + result_loc_pass1->written && result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; } - ir_assert(result_loc->value.type->id == ZigTypeIdPointer, suspend_source_instr); - ZigType *actual_elem_type = result_loc->value.type->data.pointer.child_type; + InferredStructField *isf = result_loc->value->type->data.pointer.inferred_struct_field; + if (!was_already_resolved && isf != nullptr) { + // Now it's time to add the field to the struct type. + uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count; + uint32_t new_field_count = old_field_count + 1; + isf->inferred_struct_type->data.structure.src_field_count = new_field_count; + isf->inferred_struct_type->data.structure.fields = realloc_type_struct_fields( + isf->inferred_struct_type->data.structure.fields, old_field_count, new_field_count); + + TypeStructField *field = isf->inferred_struct_type->data.structure.fields[old_field_count]; + field->name = isf->field_name; + field->type_entry = value_type; + field->type_val = create_const_type(ira->codegen, field->type_entry); + field->src_index = old_field_count; + field->decl_node = value ? value->source_node : suspend_source_instr->source_node; + if (value && instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefOk); + if (!val) + return ira->codegen->invalid_instruction; + field->is_comptime = true; + field->init_val = create_const_vals(1); + copy_const_val(field->init_val, val); + return result_loc; + } + + ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false); + IrInstruction *casted_ptr; + if (instr_is_comptime(result_loc)) { + casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type); + copy_const_val(casted_ptr->value, result_loc->value); + casted_ptr->value->type = struct_ptr_type; + } else { + casted_ptr = result_loc; + } + if (instr_is_comptime(casted_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, + suspend_source_instr->source_node); + struct_val->special = ConstValSpecialStatic; + struct_val->data.x_struct.fields = realloc_const_vals_ptrs(struct_val->data.x_struct.fields, + old_field_count, new_field_count); + + ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count]; + field_val->special = ConstValSpecialUndef; + field_val->type = field->type_entry; + field_val->parent.id = ConstParentIdStruct; + field_val->parent.data.p_struct.struct_val = struct_val; + field_val->parent.data.p_struct.field_index = old_field_count; + } + } + + result_loc = ir_analyze_struct_field_ptr(ira, suspend_source_instr, field, casted_ptr, + isf->inferred_struct_type, true); + result_loc_pass1->resolved_loc = result_loc; + } + + + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, suspend_source_instr); + ZigType *actual_elem_type = result_loc->value->type->data.pointer.child_type; if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && value_type->id != ZigTypeIdNull) { - result_loc_pass1->written = false; - return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true); + bool has_bits; + if ((err = type_has_bits2(ira->codegen, value_type, &has_bits))) + return ira->codegen->invalid_instruction; + if (has_bits) { + result_loc_pass1->written = false; + return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true); + } } else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion) { - if (value_type->id == ZigTypeIdErrorSet) { - return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true); - } else { - IrInstruction *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr, - result_loc, false, true); - ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type; - if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && - value_type->id != ZigTypeIdNull) { - return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true); + bool has_bits; + if ((err = type_has_bits2(ira->codegen, value_type, &has_bits))) + return ira->codegen->invalid_instruction; + if (has_bits) { + if (value_type->id == ZigTypeIdErrorSet) { + return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true); } else { - return unwrapped_err_ptr; + IrInstruction *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr, + result_loc, false, true); + ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type; + if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && + value_type->id != ZigTypeIdNull) { + return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true); + } else { + return unwrapped_err_ptr; + } } } - } else if (is_slice(actual_elem_type) && value_type->id == ZigTypeIdArray) { - // need to allow EndExpr to do the implicit cast from array to slice - result_loc_pass1->written = false; } return result_loc; } -static IrInstruction *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstructionImplicitCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_instruction; +static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, + IrInstructionResolveResult *instruction) +{ + ZigType *implicit_elem_type; + if (instruction->ty == nullptr) { + if (instruction->result_loc->id == ResultLocIdCast) { + implicit_elem_type = ir_resolve_type(ira, + instruction->result_loc->source_instruction->child); + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_instruction; + } else if (instruction->result_loc->id == ResultLocIdReturn) { + implicit_elem_type = ira->explicit_return_type; + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_instruction; + } else { + implicit_elem_type = ira->codegen->builtin_types.entry_var; + } + if (implicit_elem_type == ira->codegen->builtin_types.entry_var) { + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), + instruction->base.scope, instruction->base.source_node, bare_name); - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) - return ira->codegen->invalid_instruction; + StructSpecial struct_special = StructSpecialInferredStruct; + if (instruction->base.source_node->type == NodeTypeContainerInitExpr && + instruction->base.source_node->data.container_init_expr.kind == ContainerInitKindArray) + { + struct_special = StructSpecialInferredTuple; + } - return ir_implicit_cast_with_result(ira, target, dest_type, instruction->result_loc); -} - -static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstructionResolveResult *instruction) { - ZigType *implicit_elem_type = ir_resolve_type(ira, instruction->ty->child); - if (type_is_invalid(implicit_elem_type)) - return ira->codegen->invalid_instruction; + ZigType *inferred_struct_type = get_partial_container_type(ira->codegen, + instruction->base.scope, ContainerKindStruct, instruction->base.source_node, + buf_ptr(name), bare_name, ContainerLayoutAuto); + inferred_struct_type->data.structure.special = struct_special; + inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred; + implicit_elem_type = inferred_struct_type; + } + } else { + implicit_elem_type = ir_resolve_type(ira, instruction->ty->child); + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_instruction; + } IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, implicit_elem_type, nullptr, false, true, true); if (result_loc != nullptr) @@ -15538,18 +17694,18 @@ static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrIn result_loc = ir_resolve_result(ira, &instruction->base, no_result_loc(), implicit_elem_type, nullptr, false, true, true); if (result_loc != nullptr && - (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) + (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) { return result_loc; } - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; return result_loc; } IrInstruction *result = ir_const(ira, &instruction->base, implicit_elem_type); - result->value.special = ConstValSpecialUndef; + result->value->special = ConstValSpecialUndef; IrInstruction *ptr = ir_get_ref(ira, &instruction->base, result, false, false); - ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; return ptr; } @@ -15584,6 +17740,7 @@ static void ir_reset_result(ResultLoc *result_loc) { case ResultLocIdNone: case ResultLocIdInstruction: case ResultLocIdBitCast: + case ResultLocIdCast: break; } } @@ -15593,23 +17750,24 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst return ir_const_void(ira, &instruction->base); } -static IrInstruction *get_async_call_result_loc(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, - ZigType *fn_ret_type) +static IrInstruction *get_async_call_result_loc(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *fn_ret_type, bool is_async_call_builtin, IrInstruction **args_ptr, size_t args_len, + IrInstruction *ret_ptr_uncasted) { - ir_assert(call_instruction->is_async_call_builtin, &call_instruction->base); - IrInstruction *ret_ptr_uncasted = call_instruction->args[call_instruction->arg_count]->child; - if (type_is_invalid(ret_ptr_uncasted->value.type)) + ir_assert(is_async_call_builtin, source_instr); + if (type_is_invalid(ret_ptr_uncasted->value->type)) return ira->codegen->invalid_instruction; - if (ret_ptr_uncasted->value.type->id == ZigTypeIdVoid) { + if (ret_ptr_uncasted->value->type->id == ZigTypeIdVoid) { // Result location will be inside the async frame. return nullptr; } return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false)); } -static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, +static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstruction *source_instr, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, - IrInstruction *casted_new_stack) + IrInstruction *casted_new_stack, bool is_async_call_builtin, IrInstruction *ret_ptr_uncasted, + ResultLoc *call_result_loc) { if (fn_entry == nullptr) { if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { @@ -15624,29 +17782,30 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc } if (casted_new_stack != nullptr) { ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type; - IrInstruction *ret_ptr = get_async_call_result_loc(ira, call_instruction, fn_ret_type); - if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value.type)) + IrInstruction *ret_ptr = get_async_call_result_loc(ira, source_instr, fn_ret_type, is_async_call_builtin, + casted_args, arg_count, ret_ptr_uncasted); + if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value->type)) return ira->codegen->invalid_instruction; ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type); - IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, - arg_count, casted_args, FnInlineAuto, CallModifierAsync, casted_new_stack, - call_instruction->is_async_call_builtin, ret_ptr, anyframe_type); + IrInstructionCallGen *call_gen = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, + arg_count, casted_args, CallModifierAsync, casted_new_stack, + is_async_call_builtin, ret_ptr, anyframe_type); return &call_gen->base; } else { ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); - IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, + IrInstruction *result_loc = ir_resolve_result(ira, source_instr, call_result_loc, frame_type, nullptr, true, true, false); - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false)); - if (type_is_invalid(result_loc->value.type)) + if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_instruction; - return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count, - casted_args, FnInlineAuto, CallModifierAsync, casted_new_stack, - call_instruction->is_async_call_builtin, result_loc, frame_type)->base; + return &ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count, + casted_args, CallModifierAsync, casted_new_stack, + is_async_call_builtin, result_loc, frame_type)->base; } } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, @@ -15663,13 +17822,13 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node return false; casted_arg = ir_implicit_cast(ira, arg, param_type); - if (type_is_invalid(casted_arg->value.type)) + if (type_is_invalid(casted_arg->value->type)) return false; } else { casted_arg = arg; } - ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk); + ZigValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk); if (!arg_val) return false; @@ -15703,7 +17862,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod return false; casted_arg = ir_implicit_cast(ira, arg, param_type); - if (type_is_invalid(casted_arg->value.type)) + if (type_is_invalid(casted_arg->value->type)) return false; } else { arg_part_of_generic_id = true; @@ -15711,10 +17870,20 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } } - bool comptime_arg = param_decl_node->data.param_decl.is_comptime || - casted_arg->value.type->id == ZigTypeIdComptimeInt || casted_arg->value.type->id == ZigTypeIdComptimeFloat; + bool comptime_arg = param_decl_node->data.param_decl.is_comptime; + if (!comptime_arg) { + switch (type_requires_comptime(ira->codegen, casted_arg->value->type)) { + case ReqCompTimeInvalid: + return false; + case ReqCompTimeYes: + comptime_arg = true; + break; + case ReqCompTimeNo: + break; + } + } - ConstExprValue *arg_val; + ZigValue *arg_val; if (comptime_arg) { arg_part_of_generic_id = true; @@ -15722,10 +17891,10 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!arg_val) return false; } else { - arg_val = create_const_runtime(casted_arg->value.type); + arg_val = create_const_runtime(casted_arg->value->type); } if (arg_part_of_generic_id) { - copy_const_val(&generic_id->params[generic_id->param_count], arg_val, true); + copy_const_val(&generic_id->params[generic_id->param_count], arg_val); generic_id->param_count += 1; } @@ -15738,8 +17907,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod var->shadowable = !comptime_arg; *next_proto_i += 1; - } else if (casted_arg->value.type->id == ZigTypeIdComptimeInt || - casted_arg->value.type->id == ZigTypeIdComptimeFloat) + } else if (casted_arg->value->type->id == ZigTypeIdComptimeInt || + casted_arg->value->type->id == ZigTypeIdComptimeFloat) { ir_add_error(ira, casted_arg, buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); @@ -15747,20 +17916,9 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } if (!comptime_arg) { - switch (type_requires_comptime(ira->codegen, casted_arg->value.type)) { - case ReqCompTimeYes: - ir_add_error(ira, casted_arg, - buf_sprintf("parameter of type '%s' requires comptime", buf_ptr(&casted_arg->value.type->name))); - return false; - case ReqCompTimeInvalid: - return false; - case ReqCompTimeNo: - break; - } - casted_args[fn_type_id->param_count] = casted_arg; FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; - param_info->type = casted_arg->value.type; + param_info->type = casted_arg->value->type; param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; fn_type_id->param_count += 1; @@ -15769,23 +17927,6 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod return true; } -static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { - FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[index]; - if (!type_has_bits(src_param_info->type)) - return nullptr; - - size_t next_var_i = 0; - for (size_t param_i = 0; param_i < index; param_i += 1) { - FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[param_i]; - if (!type_has_bits(src_param_info->type)) { - continue; - } - - next_var_i += 1; - } - return fn_entry->variable_list.at(next_var_i); -} - static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; @@ -15798,7 +17939,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; - ConstExprValue *mem_slot = nullptr; + ZigValue *mem_slot = nullptr; bool comptime_var_mem = ir_get_var_is_comptime(var); bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern; @@ -15806,10 +17947,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, IrInstruction *result = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - result->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, + result->value->type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false); - if (linkage_makes_it_runtime) + if (linkage_makes_it_runtime || var->is_thread_local) goto no_mem_slot; if (value_is_comptime(var->const_value)) { @@ -15839,10 +17980,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - result->value.special = ConstValSpecialStatic; - result->value.data.x_ptr.mut = ptr_mut; - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = mem_slot; + result->value->special = ConstValSpecialStatic; + result->value->data.x_ptr.mut = ptr_mut; + result->value->data.x_ptr.special = ConstPtrSpecialRef; + result->value->data.x_ptr.data.ref.pointee = mem_slot; return result; } } @@ -15852,13 +17993,13 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, no_mem_slot: bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); - result->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; + result->value->data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; return result; } // This function is called when a comptime value becomes accessible at runtime. -static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *val) { +static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *val) { ir_assert(value_is_comptime(val), source_instr); if (val->special == ConstValSpecialUndef) return; @@ -15874,11 +18015,11 @@ static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_ins static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const) { - assert(ptr->value.type->id == ZigTypeIdPointer); + assert(ptr->value->type->id == ZigTypeIdPointer); - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - if (uncasted_value->value.type->id == ZigTypeIdErrorUnion || - uncasted_value->value.type->id == ZigTypeIdErrorSet) + if (ptr->value->data.x_ptr.special == ConstPtrSpecialDiscard) { + if (uncasted_value->value->type->id == ZigTypeIdErrorUnion || + uncasted_value->value->type->id == ZigTypeIdErrorSet) { ir_add_error(ira, source_instr, buf_sprintf("error is discarded")); return ira->codegen->invalid_instruction; @@ -15886,13 +18027,12 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ir_const_void(ira, source_instr); } - ZigType *child_type = ptr->value.type->data.pointer.child_type; - - if (ptr->value.type->data.pointer.is_const && !allow_write_through_const) { + if (ptr->value->type->data.pointer.is_const && !allow_write_through_const) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } + ZigType *child_type = ptr->value->type->data.pointer.child_type; IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); if (value == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; @@ -15906,28 +18046,23 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source break; } - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { + if (instr_is_comptime(ptr) && ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (!allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar || - ptr->value.data.x_ptr.mut == ConstPtrMutInfer) + if ((allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) || + ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar || + ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(value)) { - ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node); + ZigValue *dest_val = const_ptr_pointee(ira, ira->codegen, ptr->value, source_instr->source_node); if (dest_val == nullptr) return ira->codegen->invalid_instruction; if (dest_val->special != ConstValSpecialRuntime) { - // TODO this allows a value stored to have the original value modified and then - // have that affect what should be a copy. We need some kind of advanced copy-on-write - // system to make these two tests pass at the same time: - // * "string literal used as comptime slice is memoized" - // * "comptime modification of const struct field" - except modified to avoid - // ConstPtrMutComptimeVar, thus defeating the logic below. - bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; - copy_const_val(dest_val, &value->value, same_global_refs); - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar && + copy_const_val(dest_val, value->value); + + if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar && !ira->new_irb.current_basic_block->must_be_comptime_source_instr) { ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; @@ -15935,12 +18070,12 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ir_const_void(ira, source_instr); } } - if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { - ptr->value.special = ConstValSpecialRuntime; + if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { + ptr->value->special = ConstValSpecialRuntime; } else { ir_add_error(ira, source_instr, buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + ZigValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, ptr->value); dest_val->type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; @@ -15948,11 +18083,17 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } } + if (ptr->value->type->data.pointer.inferred_struct_field != nullptr && + child_type == ira->codegen->builtin_types.entry_var) + { + child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type; + } + switch (type_requires_comptime(ira->codegen, child_type)) { case ReqCompTimeInvalid: return ira->codegen->invalid_instruction; case ReqCompTimeYes: - switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) { + switch (type_has_one_possible_value(ira->codegen, ptr->value->type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueNo: @@ -15968,7 +18109,25 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source } if (instr_is_comptime(value)) { - mark_comptime_value_escape(ira, source_instr, &value->value); + mark_comptime_value_escape(ira, source_instr, value->value); + } + + // If this is a store to a pointer with a runtime-known vector index, + // we have to figure out the IrInstruction which represents the index and + // emit a IrInstructionVectorStoreElem, or emit a compile error + // explaining why it is impossible for this store to work. Which is that + // the pointer address is of the vector; without the element index being known + // we cannot properly perform the insertion. + if (ptr->value->type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + if (ptr->id == IrInstructionIdElemPtr) { + IrInstructionElemPtr *elem_ptr = (IrInstructionElemPtr *)ptr; + return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr, + elem_ptr->elem_index, value); + } + ir_add_error(ira, ptr, + buf_sprintf("unable to determine vector element index of type '%s'", + buf_ptr(&ptr->value->type->name))); + return ira->codegen->invalid_instruction; } IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope, @@ -15976,19 +18135,23 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return &store_ptr->base; } -static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, - ZigFn *fn_entry) +static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *new_stack, bool is_async_call_builtin, ZigFn *fn_entry) { - if (call_instruction->new_stack == nullptr) + if (new_stack == nullptr) return nullptr; - IrInstruction *new_stack = call_instruction->new_stack->child; - if (type_is_invalid(new_stack->value.type)) - return ira->codegen->invalid_instruction; + if (!is_async_call_builtin && + arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) + { + ir_add_error(ira, source_instr, + buf_sprintf("target arch '%s' does not support calling with a new stack", + target_arch_name(ira->codegen->zig_target->arch))); + } - if (call_instruction->is_async_call_builtin && - fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer && - new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + if (is_async_call_builtin && + fn_entry != nullptr && new_stack->value->type->id == ZigTypeIdPointer && + new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { ZigType *needed_frame_type = get_pointer_to_type(ira->codegen, get_fn_frame_type(ira->codegen, fn_entry), false); @@ -16002,9 +18165,11 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCall } } -static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, +static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstruction *source_instr, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, - IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) + IrInstruction *first_arg_ptr, CallModifier modifier, + IrInstruction *new_stack, bool is_async_call_builtin, + IrInstruction **args_ptr, size_t args_len, IrInstruction *ret_ptr, ResultLoc *call_result_loc) { Error err; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; @@ -16019,17 +18184,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; } size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; - - size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; - for (size_t i = 0; i < call_instruction->arg_count; i += 1) { - ConstExprValue *arg_tuple_value = &call_instruction->args[i]->child->value; - if (arg_tuple_value->type->id == ZigTypeIdArgTuple) { - call_param_count -= 1; - call_param_count += arg_tuple_value->data.x_arg_tuple.end_index - - arg_tuple_value->data.x_arg_tuple.start_index; - } - } - AstNode *source_node = call_instruction->base.source_node; + size_t call_param_count = args_len + first_arg_1_or_0; + AstNode *source_node = source_instr->source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; @@ -16041,7 +18197,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c return ira->codegen->invalid_instruction; } - if (fn_type_id->is_var_args) { if (call_param_count < src_param_count) { ErrorMsg *msg = ir_add_error_node(ira, source_node, @@ -16062,14 +18217,14 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c return ira->codegen->invalid_instruction; } - if (comptime_fn_call) { + if (modifier == CallModifierCompileTime) { // No special handling is needed for compile time evaluation of generic functions. if (!fn_entry || fn_entry->body_node == nullptr) { ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_instruction; } - if (!ir_emit_backward_branch(ira, &call_instruction->base)) + if (!ir_emit_backward_branch(ira, source_instr)) return ira->codegen->invalid_instruction; // Fork a scope of the function with known values for the parameters. @@ -16077,7 +18232,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c size_t next_proto_i = 0; if (first_arg_ptr) { - assert(first_arg_ptr->value.type->id == ZigTypeIdPointer); + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); bool first_arg_known_bare = false; if (fn_type_id->next_param_index >= 1) { @@ -16088,11 +18243,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } IrInstruction *first_arg; - if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value.type)) + if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_instruction; } @@ -16101,16 +18256,14 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } if (fn_proto_node->data.fn_proto.is_var_args) { - ir_add_error(ira, &call_instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313")); return ira->codegen->invalid_instruction; } - for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { - IrInstruction *old_arg = call_instruction->args[call_i]->child; - if (type_is_invalid(old_arg->value.type)) - return ira->codegen->invalid_instruction; + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstruction *old_arg = args_ptr[call_i]; if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) return ira->codegen->invalid_instruction; @@ -16132,7 +18285,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - ConstExprValue *result = nullptr; + ZigValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -16144,11 +18297,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c AstNode *body_node = fn_entry->body_node; result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, - nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node, + nullptr, source_instr->source_node, nullptr, ira->new_irb.exec, return_type_node, UndefOk); if (inferred_err_set_type != nullptr) { - inferred_err_set_type->data.error_set.infer_fn = nullptr; + inferred_err_set_type->data.error_set.incomplete = false; if (result->type->id == ZigTypeIdErrorUnion) { ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { @@ -16174,32 +18327,18 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); - copy_const_val(&new_instruction->value, result, true); - new_instruction->value.type = return_type; + IrInstruction *new_instruction = ir_const_move(ira, source_instr, result); return ir_finish_anal(ira, new_instruction); } if (fn_type->data.fn.is_generic) { if (!fn_entry) { - ir_add_error(ira, call_instruction->fn_ref, + ir_add_error(ira, fn_ref, buf_sprintf("calling a generic function requires compile-time known function value")); return ira->codegen->invalid_instruction; } - // Count the arguments of the function type id we are creating - size_t new_fn_arg_count = first_arg_1_or_0; - for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { - IrInstruction *arg = call_instruction->args[call_i]->child; - if (type_is_invalid(arg->value.type)) - return ira->codegen->invalid_instruction; - - if (arg->value.type->id == ZigTypeIdArgTuple) { - new_fn_arg_count += arg->value.data.x_arg_tuple.end_index - arg->value.data.x_arg_tuple.start_index; - } else { - new_fn_arg_count += 1; - } - } + size_t new_fn_arg_count = first_arg_1_or_0 + args_len; IrInstruction **casted_args = allocate(new_fn_arg_count); @@ -16211,7 +18350,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn); impl_fn->child_scope = &impl_fn->fndef_scope->base; FnTypeId inst_fn_type_id = {0}; - init_fn_type_id(&inst_fn_type_id, fn_proto_node, new_fn_arg_count); + init_fn_type_id(&inst_fn_type_id, fn_proto_node, fn_type_id->cc, new_fn_arg_count); inst_fn_type_id.param_count = 0; inst_fn_type_id.is_var_args = false; @@ -16224,7 +18363,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c size_t next_proto_i = 0; if (first_arg_ptr) { - assert(first_arg_ptr->value.type->id == ZigTypeIdPointer); + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); bool first_arg_known_bare = false; if (fn_type_id->next_param_index >= 1) { @@ -16235,11 +18374,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } IrInstruction *first_arg; - if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value.type)) + if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_instruction; } @@ -16250,89 +18389,30 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } } - bool found_first_var_arg = false; - size_t first_var_arg; - ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); assert(parent_fn_entry); - for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { - IrInstruction *arg = call_instruction->args[call_i]->child; - if (type_is_invalid(arg->value.type)) - return ira->codegen->invalid_instruction; + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstruction *arg = args_ptr[call_i]; - if (arg->value.type->id == ZigTypeIdArgTuple) { - for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index; - arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1) - { - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - bool is_var_args = param_decl_node->data.param_decl.is_var_args; - if (is_var_args && !found_first_var_arg) { - first_var_arg = inst_fn_type_id.param_count; - found_first_var_arg = true; - } - - ZigVar *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i); - if (arg_var == nullptr) { - ir_add_error(ira, arg, - buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557")); - return ira->codegen->invalid_instruction; - } - IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var); - if (type_is_invalid(arg_var_ptr_inst->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *arg_tuple_arg = ir_get_deref(ira, arg, arg_var_ptr_inst, nullptr); - if (type_is_invalid(arg_tuple_arg->value.type)) - return ira->codegen->invalid_instruction; - - if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg_tuple_arg, &impl_fn->child_scope, - &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->invalid_instruction; - } - } - } else { - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - bool is_var_args = param_decl_node->data.param_decl.is_var_args; - if (is_var_args && !found_first_var_arg) { - first_var_arg = inst_fn_type_id.param_count; - found_first_var_arg = true; - } - - if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, - &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->invalid_instruction; - } - } - } - - if (fn_proto_node->data.fn_proto.is_var_args) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - Buf *param_name = param_decl_node->data.param_decl.name; + assert(param_decl_node->type == NodeTypeParamDecl); - if (!found_first_var_arg) { - first_var_arg = inst_fn_type_id.param_count; + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, + &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) + { + return ira->codegen->invalid_instruction; } - - ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, - first_var_arg, inst_fn_type_id.param_count); - ZigVar *var = add_variable(ira->codegen, param_decl_node, - impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type); - impl_fn->child_scope = var->child_scope; } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ZigValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr, UndefBad); IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); - copy_const_val(&const_instruction->base.value, align_result, true); + copy_const_val(const_instruction->base.value, align_result); uint32_t align_bytes = 0; ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes); @@ -16357,8 +18437,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c switch (type_requires_comptime(ira->codegen, specified_return_type)) { case ReqCompTimeYes: // Throw out our work and call the function as if it were comptime. - return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, - true, FnInlineAuto); + return ir_analyze_fn_call(ira, source_instr, fn_entry, fn_type, fn_ref, first_arg_ptr, + CallModifierCompileTime, new_stack, is_async_call_builtin, args_ptr, args_len, + ret_ptr, call_result_loc); case ReqCompTimeInvalid: return ira->codegen->invalid_instruction; case ReqCompTimeNo: @@ -16376,9 +18457,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c if (type_is_invalid(impl_fn->type_entry)) return ira->codegen->invalid_instruction; - impl_fn->ir_executable.source_node = call_instruction->base.source_node; - impl_fn->ir_executable.parent_exec = ira->new_irb.exec; - impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; + impl_fn->ir_executable->source_node = source_instr->source_node; + impl_fn->ir_executable->parent_exec = ira->new_irb.exec; + impl_fn->analyzed_executable.source_node = source_instr->source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; impl_fn->analyzed_executable.is_generic_instantiation = true; @@ -16392,33 +18473,40 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c parent_fn_entry->calls_or_awaits_errorable_fn = true; } - IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, impl_fn); - if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type)) + IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, + is_async_call_builtin, impl_fn); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) return ira->codegen->invalid_instruction; size_t impl_param_count = impl_fn_type_id->param_count; - if (call_instruction->modifier == CallModifierAsync) { - IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, - nullptr, casted_args, impl_param_count, casted_new_stack); + if (modifier == CallModifierAsync) { + IrInstruction *result = ir_analyze_async_call(ira, source_instr, impl_fn, impl_fn->type_entry, + nullptr, casted_args, impl_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, + call_result_loc); return ir_finish_anal(ira, result); } IrInstruction *result_loc; if (handle_is_ptr(impl_fn_type_id->return_type)) { - result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, + result_loc = ir_resolve_result(ira, source_instr, call_result_loc, impl_fn_type_id->return_type, nullptr, true, true, false); if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } - if (!handle_is_ptr(result_loc->value.type->data.pointer.child_type)) { - ir_reset_result(call_instruction->result_loc); + ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; + if (res_child_type == ira->codegen->builtin_types.entry_var) { + res_child_type = impl_fn_type_id->return_type; + } + if (!handle_is_ptr(res_child_type)) { + ir_reset_result(call_result_loc); result_loc = nullptr; } } - } else if (call_instruction->is_async_call_builtin) { - result_loc = get_async_call_result_loc(ira, call_instruction, impl_fn_type_id->return_type); - if (result_loc != nullptr && type_is_invalid(result_loc->value.type)) + } else if (is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, source_instr, impl_fn_type_id->return_type, + is_async_call_builtin, args_ptr, args_len, ret_ptr); + if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_instruction; } else { result_loc = nullptr; @@ -16426,18 +18514,17 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr && - call_instruction->modifier != CallModifierNoAsync) + modifier != CallModifierNoAsync) { parent_fn_entry->inferred_async_node = fn_ref->source_node; parent_fn_entry->inferred_async_fn = impl_fn; } - IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, - impl_fn, nullptr, impl_param_count, casted_args, fn_inline, - call_instruction->modifier, casted_new_stack, call_instruction->is_async_call_builtin, result_loc, - impl_fn_type_id->return_type); + IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, source_instr, + impl_fn, nullptr, impl_param_count, casted_args, modifier, casted_new_stack, + is_async_call_builtin, result_loc, impl_fn_type_id->return_type); - if (get_scope_typeof(call_instruction->base.scope) == nullptr) { + if (get_scope_typeof(source_instr->scope) == nullptr) { parent_fn_entry->call_list.append(new_call_instruction); } @@ -16455,7 +18542,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { - assert(first_arg_ptr->value.type->id == ZigTypeIdPointer); + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); ZigType *param_type = fn_type_id->param_info[next_arg_index].type; if (type_is_invalid(param_type)) @@ -16463,76 +18550,41 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction *first_arg; if (param_type->id == ZigTypeIdPointer && - handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) + handle_is_ptr(first_arg_ptr->value->type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value.type)) + if (type_is_invalid(first_arg->value->type)) return ira->codegen->invalid_instruction; } IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); - if (type_is_invalid(casted_arg->value.type)) + if (type_is_invalid(casted_arg->value->type)) return ira->codegen->invalid_instruction; casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } - for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { - IrInstruction *old_arg = call_instruction->args[call_i]->child; - if (type_is_invalid(old_arg->value.type)) + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstruction *old_arg = args_ptr[call_i]; + if (type_is_invalid(old_arg->value->type)) return ira->codegen->invalid_instruction; - if (old_arg->value.type->id == ZigTypeIdArgTuple) { - for (size_t arg_tuple_i = old_arg->value.data.x_arg_tuple.start_index; - arg_tuple_i < old_arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1) - { - ZigVar *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i); - if (arg_var == nullptr) { - ir_add_error(ira, old_arg, - buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557")); - return ira->codegen->invalid_instruction; - } - IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, old_arg, arg_var); - if (type_is_invalid(arg_var_ptr_inst->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *arg_tuple_arg = ir_get_deref(ira, old_arg, arg_var_ptr_inst, nullptr); - if (type_is_invalid(arg_tuple_arg->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *casted_arg; - if (next_arg_index < src_param_count) { - ZigType *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_instruction; - casted_arg = ir_implicit_cast(ira, arg_tuple_arg, param_type); - if (type_is_invalid(casted_arg->value.type)) - return ira->codegen->invalid_instruction; - } else { - casted_arg = arg_tuple_arg; - } - - casted_args[next_arg_index] = casted_arg; - next_arg_index += 1; - } + IrInstruction *casted_arg; + if (next_arg_index < src_param_count) { + ZigType *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_instruction; + casted_arg = ir_implicit_cast(ira, old_arg, param_type); + if (type_is_invalid(casted_arg->value->type)) + return ira->codegen->invalid_instruction; } else { - IrInstruction *casted_arg; - if (next_arg_index < src_param_count) { - ZigType *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_instruction; - casted_arg = ir_implicit_cast(ira, old_arg, param_type); - if (type_is_invalid(casted_arg->value.type)) - return ira->codegen->invalid_instruction; - } else { - casted_arg = old_arg; - } - - casted_args[next_arg_index] = casted_arg; - next_arg_index += 1; + casted_arg = old_arg; } + + casted_args[next_arg_index] = casted_arg; + next_arg_index += 1; } assert(next_arg_index == call_param_count); @@ -16541,25 +18593,26 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c if (type_is_invalid(return_type)) return ira->codegen->invalid_instruction; - if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && fn_inline == FnInlineNever) { - ir_add_error(ira, &call_instruction->base, + if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) { + ir_add_error(ira, source_instr, buf_sprintf("no-inline call of inline function")); return ira->codegen->invalid_instruction; } - IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, fn_entry); - if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type)) + IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, + is_async_call_builtin, fn_entry); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) return ira->codegen->invalid_instruction; - if (call_instruction->modifier == CallModifierAsync) { - IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, - casted_args, call_param_count, casted_new_stack); + if (modifier == CallModifierAsync) { + IrInstruction *result = ir_analyze_async_call(ira, source_instr, fn_entry, fn_type, fn_ref, + casted_args, call_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc); return ir_finish_anal(ira, result); } if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr && - call_instruction->modifier != CallModifierNoAsync) + modifier != CallModifierNoAsync) { parent_fn_entry->inferred_async_node = fn_ref->source_node; parent_fn_entry->inferred_async_fn = fn_entry; @@ -16567,88 +18620,258 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction *result_loc; if (handle_is_ptr(return_type)) { - result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, + result_loc = ir_resolve_result(ira, source_instr, call_result_loc, return_type, nullptr, true, true, false); if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } - if (!handle_is_ptr(result_loc->value.type->data.pointer.child_type)) { - ir_reset_result(call_instruction->result_loc); + ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; + if (res_child_type == ira->codegen->builtin_types.entry_var) { + res_child_type = return_type; + } + if (!handle_is_ptr(res_child_type)) { + ir_reset_result(call_result_loc); result_loc = nullptr; } } - } else if (call_instruction->is_async_call_builtin) { - result_loc = get_async_call_result_loc(ira, call_instruction, return_type); - if (result_loc != nullptr && type_is_invalid(result_loc->value.type)) + } else if (is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, source_instr, return_type, is_async_call_builtin, + args_ptr, args_len, ret_ptr); + if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_instruction; } else { result_loc = nullptr; } - IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, - call_param_count, casted_args, fn_inline, call_instruction->modifier, casted_new_stack, - call_instruction->is_async_call_builtin, result_loc, return_type); - if (get_scope_typeof(call_instruction->base.scope) == nullptr) { + IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, + call_param_count, casted_args, modifier, casted_new_stack, + is_async_call_builtin, result_loc, return_type); + if (get_scope_typeof(source_instr->scope) == nullptr) { parent_fn_entry->call_list.append(new_call_instruction); } return ir_finish_anal(ira, &new_call_instruction->base); } -static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction) { - IrInstruction *fn_ref = call_instruction->fn_ref->child; - if (type_is_invalid(fn_ref->value.type)) +static IrInstruction *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, + ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, + IrInstruction *first_arg_ptr, CallModifier modifier) +{ + IrInstruction *new_stack = nullptr; + if (call_instruction->new_stack) { + new_stack = call_instruction->new_stack->child; + if (type_is_invalid(new_stack->value->type)) + return ira->codegen->invalid_instruction; + } + IrInstruction **args_ptr = allocate(call_instruction->arg_count, "IrInstruction *"); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + args_ptr[i] = call_instruction->args[i]->child; + if (type_is_invalid(args_ptr[i]->value->type)) + return ira->codegen->invalid_instruction; + } + IrInstruction *ret_ptr = nullptr; + if (call_instruction->ret_ptr != nullptr) { + ret_ptr = call_instruction->ret_ptr->child; + if (type_is_invalid(ret_ptr->value->type)) + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_analyze_fn_call(ira, &call_instruction->base, fn_entry, fn_type, fn_ref, + first_arg_ptr, modifier, new_stack, call_instruction->is_async_call_builtin, + args_ptr, call_instruction->arg_count, ret_ptr, call_instruction->result_loc); + deallocate(args_ptr, call_instruction->arg_count, "IrInstruction *"); + return result; +} + +static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *pass1_options, IrInstruction *pass1_fn_ref, IrInstruction **args_ptr, size_t args_len, + ResultLoc *result_loc) +{ + IrInstruction *options = pass1_options->child; + if (type_is_invalid(options->value->type)) return ira->codegen->invalid_instruction; - bool is_comptime = call_instruction->is_comptime || - ir_should_inline(ira->new_irb.exec, call_instruction->base.scope); + IrInstruction *fn_ref = pass1_fn_ref->child; + if (type_is_invalid(fn_ref->value->type)) + return ira->codegen->invalid_instruction; - if (is_comptime || instr_is_comptime(fn_ref)) { - if (fn_ref->value.type->id == ZigTypeIdMetaType) { - ZigType *dest_type = ir_resolve_type(ira, fn_ref); - if (type_is_invalid(dest_type)) + TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier")); + ir_assert(modifier_field != nullptr, source_instr); + IrInstruction *modifier_inst = ir_analyze_struct_value_field_value(ira, source_instr, options, modifier_field); + ZigValue *modifier_val = ir_resolve_const(ira, modifier_inst, UndefBad); + if (modifier_val == nullptr) + return ira->codegen->invalid_instruction; + CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag); + + if (ir_should_inline(ira->new_irb.exec, source_instr->scope)) { + switch (modifier) { + case CallModifierBuiltin: + zig_unreachable(); + case CallModifierAsync: + ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @call with async modifier")); return ira->codegen->invalid_instruction; + case CallModifierCompileTime: + case CallModifierNone: + case CallModifierAlwaysInline: + case CallModifierAlwaysTail: + case CallModifierNoAsync: + modifier = CallModifierCompileTime; + break; + case CallModifierNeverInline: + ir_add_error(ira, source_instr, + buf_sprintf("unable to perform 'never_inline' call at compile-time")); + return ira->codegen->invalid_instruction; + case CallModifierNeverTail: + ir_add_error(ira, source_instr, + buf_sprintf("unable to perform 'never_tail' call at compile-time")); + return ira->codegen->invalid_instruction; + } + } - size_t actual_param_count = call_instruction->arg_count; + IrInstruction *first_arg_ptr = nullptr; + ZigFn *fn = nullptr; + if (instr_is_comptime(fn_ref)) { + if (fn_ref->value->type->id == ZigTypeIdBoundFn) { + assert(fn_ref->value->special == ConstValSpecialStatic); + fn = fn_ref->value->data.x_bound_fn.fn; + first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; + if (type_is_invalid(first_arg_ptr->value->type)) + return ira->codegen->invalid_instruction; + } else { + fn = ir_resolve_fn(ira, fn_ref); + } + } - if (actual_param_count != 1) { - ir_add_error_node(ira, call_instruction->base.source_node, - buf_sprintf("cast expression expects exactly one parameter")); + // Some modifiers require the callee to be comptime-known + switch (modifier) { + case CallModifierCompileTime: + case CallModifierAlwaysInline: + case CallModifierAsync: + if (fn == nullptr) { + ir_add_error(ira, modifier_inst, + buf_sprintf("the specified modifier requires a comptime-known function")); return ira->codegen->invalid_instruction; } + default: + break; + } - IrInstruction *arg = call_instruction->args[0]->child; + ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; - IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg, - call_instruction->result_loc); - if (type_is_invalid(cast_instruction->value.type)) + TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); + ir_assert(stack_field != nullptr, source_instr); + IrInstruction *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); + if (type_is_invalid(opt_stack->value->type)) + return ira->codegen->invalid_instruction; + + IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack); + bool stack_is_non_null; + if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) + return ira->codegen->invalid_instruction; + + IrInstruction *stack = nullptr; + if (stack_is_non_null) { + stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false); + if (type_is_invalid(stack->value->type)) + return ira->codegen->invalid_instruction; + } + + return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, + modifier, stack, false, args_ptr, args_len, nullptr, result_loc); +} + +static IrInstruction *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstructionCallExtra *instruction) { + IrInstruction *args = instruction->args->child; + ZigType *args_type = args->value->type; + if (type_is_invalid(args_type)) + return ira->codegen->invalid_instruction; + + if (args_type->id != ZigTypeIdStruct) { + ir_add_error(ira, args, + buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction **args_ptr = nullptr; + size_t args_len = 0; + + if (is_tuple(args_type)) { + args_len = args_type->data.structure.src_field_count; + args_ptr = allocate(args_len, "IrInstruction *"); + for (size_t i = 0; i < args_len; i += 1) { + TypeStructField *arg_field = args_type->data.structure.fields[i]; + args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base, args, arg_field); + if (type_is_invalid(args_ptr[i]->value->type)) return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, cast_instruction); - } else if (fn_ref->value.type->id == ZigTypeIdFn) { + } + } else { + ir_add_error(ira, args, buf_sprintf("TODO: struct args")); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_analyze_call_extra(ira, &instruction->base, instruction->options, + instruction->fn_ref, args_ptr, args_len, instruction->result_loc); + deallocate(args_ptr, args_len, "IrInstruction *"); + return result; +} + +static IrInstruction *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstructionCallSrcArgs *instruction) { + IrInstruction **args_ptr = allocate(instruction->args_len, "IrInstruction *"); + for (size_t i = 0; i < instruction->args_len; i += 1) { + args_ptr[i] = instruction->args_ptr[i]->child; + if (type_is_invalid(args_ptr[i]->value->type)) + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_analyze_call_extra(ira, &instruction->base, instruction->options, + instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc); + deallocate(args_ptr, instruction->args_len, "IrInstruction *"); + return result; +} + +static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction) { + IrInstruction *fn_ref = call_instruction->fn_ref->child; + if (type_is_invalid(fn_ref->value->type)) + return ira->codegen->invalid_instruction; + + bool is_comptime = (call_instruction->modifier == CallModifierCompileTime) || + ir_should_inline(ira->new_irb.exec, call_instruction->base.scope); + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + + if (is_comptime || instr_is_comptime(fn_ref)) { + if (fn_ref->value->type->id == ZigTypeIdMetaType) { + ZigType *ty = ir_resolve_type(ira, fn_ref); + if (ty == nullptr) + return ira->codegen->invalid_instruction; + ErrorMsg *msg = ir_add_error_node(ira, fn_ref->source_node, + buf_sprintf("type '%s' not a function", buf_ptr(&ty->name))); + add_error_note(ira->codegen, msg, call_instruction->base.source_node, + buf_sprintf("use @as builtin for type coercion")); + return ira->codegen->invalid_instruction; + } else if (fn_ref->value->type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); - if (fn_table_entry == nullptr) - return ira->codegen->invalid_instruction; - return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - fn_ref, nullptr, is_comptime, call_instruction->fn_inline); - } else if (fn_ref->value.type->id == ZigTypeIdBoundFn) { - assert(fn_ref->value.special == ConstValSpecialStatic); - ZigFn *fn_table_entry = fn_ref->value.data.x_bound_fn.fn; - IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg; - return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline); + ZigType *fn_type = fn_table_entry ? fn_table_entry->type_entry : fn_ref->value->type; + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_type, + fn_ref, nullptr, modifier); + } else if (fn_ref->value->type->id == ZigTypeIdBoundFn) { + assert(fn_ref->value->special == ConstValSpecialStatic); + ZigFn *fn_table_entry = fn_ref->value->data.x_bound_fn.fn; + IrInstruction *first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, + fn_ref, first_arg_ptr, modifier); } else { ir_add_error_node(ira, fn_ref->source_node, - buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); + buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); return ira->codegen->invalid_instruction; } } - if (fn_ref->value.type->id == ZigTypeIdFn) { - return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type, - fn_ref, nullptr, false, FnInlineAuto); + if (fn_ref->value->type->id == ZigTypeIdFn) { + return ir_analyze_fn_call_src(ira, call_instruction, nullptr, fn_ref->value->type, + fn_ref, nullptr, modifier); } else { ir_add_error_node(ira, fn_ref->source_node, - buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); + buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); return ira->codegen->invalid_instruction; } } @@ -16656,12 +18879,13 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC // out_val->type must be the type to read the pointer as // if the type is different than the actual type then it does a comptime byte reinterpretation static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ConstExprValue *out_val, ConstExprValue *ptr_val) + ZigValue *out_val, ZigValue *ptr_val) { Error err; assert(out_val->type != nullptr); - ConstExprValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); + ZigValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); + src_assert(pointee->type != nullptr, source_node); if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; @@ -16672,8 +18896,8 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { - if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { - copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut != ConstPtrMutComptimeVar); + if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) { + copy_const_val(out_val, pointee); return ErrorNone; } Buf buf = BUF_INIT; @@ -16701,7 +18925,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source return ErrorSemanticAnalyzeFail; } case ConstPtrSpecialBaseArray: { - ConstExprValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; + ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); if (array_val->data.x_array.special != ConstArraySpecialNone) zig_panic("TODO"); @@ -16718,7 +18942,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source Buf buf = BUF_INIT; buf_resize(&buf, elem_count * elem_size); for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; + ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); } if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) @@ -16739,11 +18963,11 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueOptType *lazy_opt_type = allocate(1); - lazy_opt_type->ira = ira; - result->value.data.x_lazy = &lazy_opt_type->base; + LazyValueOptType *lazy_opt_type = allocate(1, "LazyValueOptType"); + lazy_opt_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_opt_type->base; lazy_opt_type->base.id = LazyValueIdOptType; lazy_opt_type->payload_type = instruction->value->child; @@ -16754,7 +18978,7 @@ static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp } static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type, - ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op) + ZigValue *operand_val, ZigValue *scalar_out_val, bool is_wrap_op) { bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); @@ -16790,7 +19014,7 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_i static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) { IrInstruction *value = instruction->value->child; - ZigType *expr_type = value->value.type; + ZigType *expr_type = value->value->type; if (type_is_invalid(expr_type)) return ira->codegen->invalid_instruction; @@ -16808,20 +19032,20 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; if (instr_is_comptime(value)) { - ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad); + ZigValue *operand_val = ir_resolve_const(ira, value, UndefBad); if (!operand_val) return ira->codegen->invalid_instruction; IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type); - ConstExprValue *out_val = &result_instruction->value; + ZigValue *out_val = result_instruction->value; if (expr_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, operand_val); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); size_t len = expr_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { - ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; - ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_operand_val->type == scalar_type); assert(scalar_out_val->type == scalar_type); ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type, @@ -16847,31 +19071,31 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins IrInstruction *result = ir_build_un_op(&ira->new_irb, instruction->base.scope, instruction->base.source_node, instruction->op_id, value); - result->value.type = expr_type; + result->value->type = expr_type; return result; } static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) { IrInstruction *value = instruction->value->child; - ZigType *expr_type = value->value.type; + ZigType *expr_type = value->value->type; if (type_is_invalid(expr_type)) return ira->codegen->invalid_instruction; if (expr_type->id == ZigTypeIdInt) { if (instr_is_comptime(value)) { - ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); + ZigValue *target_const_val = ir_resolve_const(ira, value, UndefBad); if (target_const_val == nullptr) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, expr_type); - bigint_not(&result->value.data.x_bigint, &target_const_val->data.x_bigint, + bigint_not(&result->value->data.x_bigint, &target_const_val->data.x_bigint, expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); return result; } IrInstruction *result = ir_build_un_op(&ira->new_irb, instruction->base.scope, instruction->base.source_node, IrUnOpBinNot, value); - result->value.type = expr_type; + result->value->type = expr_type; return result; } @@ -16892,9 +19116,9 @@ static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstruction return ir_analyze_negation(ira, instruction); case IrUnOpDereference: { IrInstruction *ptr = instruction->value->child; - if (type_is_invalid(ptr->value.type)) + if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr->value.type; + ZigType *ptr_type = ptr->value->type; if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) { ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("index syntax required for unknown-length pointer type '%s'", @@ -16907,9 +19131,9 @@ static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; // If the result needs to be an lvalue, type check it - if (instruction->lval == LValPtr && result->value.type->id != ZigTypeIdPointer) { + if (instruction->lval == LValPtr && result->value->type->id != ZigTypeIdPointer) { ir_add_error(ira, &instruction->base, - buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value.type->name))); + buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value->type->name))); return ira->codegen->invalid_instruction; } @@ -16952,13 +19176,13 @@ static IrInstruction *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr IrInstruction *result = ir_build_br(&ira->new_irb, br_instruction->base.scope, br_instruction->base.source_node, new_bb, nullptr); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { IrInstruction *condition = cond_br_instruction->condition->child; - if (type_is_invalid(condition->value.type)) + if (type_is_invalid(condition->value->type)) return ir_unreach_error(ira); bool is_comptime; @@ -16967,7 +19191,7 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (type_is_invalid(casted_condition->value.type)) + if (type_is_invalid(casted_condition->value->type)) return ir_unreach_error(ira); if (is_comptime || instr_is_comptime(casted_condition)) { @@ -16989,7 +19213,7 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_br(&ira->new_irb, cond_br_instruction->base.scope, cond_br_instruction->base.source_node, new_dest_block, nullptr); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -17008,7 +19232,7 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_cond_br(&ira->new_irb, cond_br_instruction->base.scope, cond_br_instruction->base.source_node, casted_condition, new_then_block, new_else_block, nullptr); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -17017,24 +19241,26 @@ static IrInstruction *ir_analyze_instruction_unreachable(IrAnalyze *ira, { IrInstruction *result = ir_build_unreachable(&ira->new_irb, unreachable_instruction->base.scope, unreachable_instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { + Error err; + if (ira->const_predecessor_bb) { for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor != ira->const_predecessor_bb) continue; IrInstruction *value = phi_instruction->incoming_values[i]->child; - assert(value->value.type); - if (type_is_invalid(value->value.type)) + assert(value->value->type); + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; - if (value->value.special != ConstValSpecialRuntime) { + if (value->value->special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - copy_const_val(&result->value, &value->value, true); + copy_const_val(result->value, value->value); return result; } else { return value; @@ -17060,7 +19286,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh } else { instructions[i] = ir_const(ira, this_peer->base.source_instruction, this_peer->base.implicit_elem_type); - instructions[i]->value.special = ConstValSpecialRuntime; + instructions[i]->value->special = ConstValSpecialRuntime; } } else { instructions[i] = gen_instruction; @@ -17071,6 +19297,8 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh peer_parent->resolved_type = ir_resolve_peer_types(ira, peer_parent->base.source_instruction->source_node, expected_type, instructions, peer_parent->peers.length); + if (type_is_invalid(peer_parent->resolved_type)) + return ira->codegen->invalid_instruction; // the logic below assumes there are no instructions in the new current basic block yet ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base); @@ -17079,7 +19307,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh IrInstruction *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base, peer_parent->parent, peer_parent->resolved_type, nullptr, false, false, true); if (parent_result_loc != nullptr && - (type_is_invalid(parent_result_loc->value.type) || instr_is_unreachable(parent_result_loc))) + (type_is_invalid(parent_result_loc->value->type) || instr_is_unreachable(parent_result_loc))) { return parent_result_loc; } @@ -17092,7 +19320,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (instrs_to_move.length != 0) { IrBasicBlock *predecessor = peer_parent->base.source_instruction->child->owner_bb; IrInstruction *branch_instruction = predecessor->instruction_list.pop(); - ir_assert(branch_instruction->value.type->id == ZigTypeIdUnreachable, &phi_instruction->base); + ir_assert(branch_instruction->value->type->id == ZigTypeIdUnreachable, &phi_instruction->base); while (instrs_to_move.length != 0) { predecessor->instruction_list.append(instrs_to_move.pop()); } @@ -17129,10 +19357,10 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh IrInstruction *old_value = phi_instruction->incoming_values[i]; assert(old_value); IrInstruction *new_value = old_value->child; - if (!new_value || new_value->value.type->id == ZigTypeIdUnreachable || predecessor->other == nullptr) + if (!new_value || new_value->value->type->id == ZigTypeIdUnreachable || predecessor->other == nullptr) continue; - if (type_is_invalid(new_value->value.type)) + if (type_is_invalid(new_value->value->type)) return ira->codegen->invalid_instruction; @@ -17144,7 +19372,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (new_incoming_blocks.length == 0) { IrInstruction *result = ir_build_unreachable(&ira->new_irb, phi_instruction->base.scope, phi_instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -17152,16 +19380,28 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh return new_incoming_values.at(0); } - ZigType *resolved_type; - if (peer_parent != nullptr && ir_result_has_type(peer_parent->parent)) { - if (peer_parent->parent->id == ResultLocIdReturn) { - resolved_type = ira->explicit_return_type; - } else { - ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value.type; - ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base); - resolved_type = resolved_loc_ptr_type->data.pointer.child_type; + ZigType *resolved_type = nullptr; + if (peer_parent != nullptr) { + bool peer_parent_has_type; + if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) + return ira->codegen->invalid_instruction; + if (peer_parent_has_type) { + if (peer_parent->parent->id == ResultLocIdReturn) { + resolved_type = ira->explicit_return_type; + } else if (peer_parent->parent->id == ResultLocIdCast) { + resolved_type = ir_resolve_type(ira, peer_parent->parent->source_instruction->child); + } else if (peer_parent->parent->resolved_loc) { + ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value->type; + ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base); + resolved_type = resolved_loc_ptr_type->data.pointer.child_type; + } + + if (resolved_type != nullptr && type_is_invalid(resolved_type)) + return ira->codegen->invalid_instruction; } - } else { + } + + if (resolved_type == nullptr) { resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr, new_incoming_values.items, new_incoming_values.length); if (type_is_invalid(resolved_type)) @@ -17172,7 +19412,8 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueYes: - return ir_const(ira, &phi_instruction->base, resolved_type); + return ir_const_move(ira, &phi_instruction->base, + get_the_one_possible_value(ira->codegen, resolved_type)); case OnePossibleValueNo: break; } @@ -17201,14 +19442,14 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh IrInstruction *branch_instruction = predecessor->instruction_list.pop(); ir_set_cursor_at_end(&ira->new_irb, predecessor); IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type); - if (type_is_invalid(casted_value->value.type)) { + if (type_is_invalid(casted_value->value->type)) { return ira->codegen->invalid_instruction; } new_incoming_values.items[i] = casted_value; predecessor->instruction_list.append(branch_instruction); - if (all_stack_ptrs && (casted_value->value.special != ConstValSpecialRuntime || - casted_value->value.data.rh_ptr != RuntimeHintPtrStack)) + if (all_stack_ptrs && (casted_value->value->special != ConstValSpecialRuntime || + casted_value->value->data.rh_ptr != RuntimeHintPtrStack)) { all_stack_ptrs = false; } @@ -17218,11 +19459,11 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh IrInstruction *result = ir_build_phi(&ira->new_irb, phi_instruction->base.scope, phi_instruction->base.source_node, new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, nullptr); - result->value.type = resolved_type; + result->value->type = resolved_type; if (all_stack_ptrs) { - assert(result->value.special == ConstValSpecialRuntime); - result->value.data.rh_ptr = RuntimeHintPtrStack; + assert(result->value->special == ConstValSpecialRuntime); + result->value->data.rh_ptr = RuntimeHintPtrStack; } return result; @@ -17245,18 +19486,35 @@ static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructi static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, + return get_pointer_to_type_extra2(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_type->data.pointer.ptr_len, new_align, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - ptr_type->data.pointer.allow_zero); + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + ptr_type->data.pointer.sentinel); +} + +static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + new_sentinel); } static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { assert(is_slice(slice_type)); - ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, + ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry, new_align); return get_slice_type(g, ptr_type); } @@ -17275,13 +19533,13 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { Error err; IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->child; - if (type_is_invalid(array_ptr->value.type)) + if (type_is_invalid(array_ptr->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *orig_array_ptr_val = &array_ptr->value; + ZigValue *orig_array_ptr_val = array_ptr->value; IrInstruction *elem_index = elem_ptr_instruction->elem_index->child; - if (type_is_invalid(elem_index->value.type)) + if (type_is_invalid(elem_index->value->type)) return ira->codegen->invalid_instruction; ZigType *ptr_type = orig_array_ptr_val->type; @@ -17342,39 +19600,38 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); } else if (is_slice(array_type)) { - return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry, + return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index]->type_entry, elem_ptr_instruction->ptr_len); - } else if (array_type->id == ZigTypeIdArgTuple) { - ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); - if (!ptr_val) + } else if (array_type->id == ZigTypeIdVector) { + // This depends on whether the element index is comptime, so it is computed later. + return_type = nullptr; + } else if (elem_ptr_instruction->init_array_type_source_node != nullptr && + array_type->id == ZigTypeIdStruct && + array_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + ZigType *usize = ira->codegen->builtin_types.entry_usize; + IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); + if (type_is_invalid(casted_elem_index->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *args_val = const_ptr_pointee(ira, ira->codegen, ptr_val, elem_ptr_instruction->base.source_node); - if (args_val == nullptr) + ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base); + Buf *field_name = buf_alloc(); + bigint_append_buf(field_name, &casted_elem_index->value->data.x_bigint, 10); + return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base, + array_ptr, array_type); + } else if (is_tuple(array_type)) { + uint64_t elem_index_scalar; + if (!ir_resolve_usize(ira, elem_index, &elem_index_scalar)) return ira->codegen->invalid_instruction; - size_t start = args_val->data.x_arg_tuple.start_index; - size_t end = args_val->data.x_arg_tuple.end_index; - uint64_t elem_index_val; - if (!ir_resolve_usize(ira, elem_index, &elem_index_val)) - return ira->codegen->invalid_instruction; - size_t index = elem_index_val; - size_t len = end - start; - if (index >= len) { - ir_add_error(ira, &elem_ptr_instruction->base, - buf_sprintf("index %" ZIG_PRI_usize " outside argument list of size %" ZIG_PRI_usize "", index, len)); + if (elem_index_scalar >= array_type->data.structure.src_field_count) { + ir_add_error(ira, &elem_ptr_instruction->base, buf_sprintf( + "field index %" ZIG_PRI_u64 " outside tuple '%s' which has %" PRIu32 " fields", + elem_index_scalar, buf_ptr(&array_type->name), + array_type->data.structure.src_field_count)); return ira->codegen->invalid_instruction; } - size_t abs_index = start + index; - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - ZigVar *var = get_fn_var_by_index(fn_entry, abs_index); - bool is_const = true; - bool is_volatile = false; - if (var) { - return ir_get_var_ptr(ira, &elem_ptr_instruction->base, var); - } else { - return ir_get_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val, - ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile, 0); - } + TypeStructField *field = array_type->data.structure.fields[elem_index_scalar]; + return ir_analyze_struct_field_ptr(ira, &elem_ptr_instruction->base, field, array_ptr, + array_type, false); } else { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); @@ -17388,9 +19645,15 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct bool safety_check_on = elem_ptr_instruction->safety_check_on; if (instr_is_comptime(casted_elem_index)) { - uint64_t index = bigint_as_u64(&casted_elem_index->value.data.x_bigint); + uint64_t index = bigint_as_u64(&casted_elem_index->value->data.x_bigint); if (array_type->id == ZigTypeIdArray) { uint64_t array_len = array_type->data.array.len; + if (index == array_len && array_type->data.array.sentinel != nullptr) { + ZigType *elem_type = array_type->data.array.child_type; + IrInstruction *sentinel_elem = ir_const(ira, &elem_ptr_instruction->base, elem_type); + copy_const_val(sentinel_elem->value, array_type->data.array.sentinel); + return ir_get_ref(ira, &elem_ptr_instruction->base, sentinel_elem, true, false); + } if (index >= array_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64, @@ -17399,8 +19662,15 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } safety_check_on = false; } - - if (return_type->data.pointer.explicit_alignment != 0) { + if (array_type->id == ZigTypeIdVector) { + ZigType *elem_type = array_type->data.vector.elem_type; + uint32_t host_vec_len = array_type->data.vector.len; + return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, + nullptr, nullptr); + } else if (return_type->data.pointer.explicit_alignment != 0) { // figure out the largest alignment possible if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) @@ -17429,21 +19699,24 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } if (orig_array_ptr_val->special != ConstValSpecialRuntime && + orig_array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) { - ConstExprValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, + ZigValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, elem_ptr_instruction->base.source_node); if (array_ptr_val == nullptr) return ira->codegen->invalid_instruction; - if (array_ptr_val->special == ConstValSpecialUndef && elem_ptr_instruction->init_array_type != nullptr) { - if (array_type->id == ZigTypeIdArray) { + if (array_ptr_val->special == ConstValSpecialUndef && + elem_ptr_instruction->init_array_type_source_node != nullptr) + { + if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { array_ptr_val->data.x_array.special = ConstArraySpecialNone; array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len); array_ptr_val->special = ConstValSpecialStatic; for (size_t i = 0; i < array_type->data.array.len; i += 1) { - ConstExprValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; + ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; elem_val->special = ConstValSpecialUndef; elem_val->type = array_type->data.array.child_type; elem_val->parent.id = ConstParentIdArray; @@ -17451,23 +19724,26 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct elem_val->parent.data.p_array.elem_index = i; } } else if (is_slice(array_type)) { - ZigType *actual_array_type = ir_resolve_type(ira, elem_ptr_instruction->init_array_type->child); + ir_assert(array_ptr->value->type->id == ZigTypeIdPointer, &elem_ptr_instruction->base); + ZigType *actual_array_type = array_ptr->value->type->data.pointer.child_type; + if (type_is_invalid(actual_array_type)) return ira->codegen->invalid_instruction; if (actual_array_type->id != ZigTypeIdArray) { - ir_add_error(ira, elem_ptr_instruction->init_array_type, - buf_sprintf("expected array type or [_], found slice")); + ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, + buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", + buf_ptr(&actual_array_type->name))); return ira->codegen->invalid_instruction; } - ConstExprValue *array_init_val = create_const_vals(1); + ZigValue *array_init_val = create_const_vals(1); array_init_val->special = ConstValSpecialStatic; array_init_val->type = actual_array_type; array_init_val->data.x_array.special = ConstArraySpecialNone; array_init_val->data.x_array.data.s_none.elements = create_const_vals(actual_array_type->data.array.len); array_init_val->special = ConstValSpecialStatic; for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) { - ConstExprValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i]; + ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i]; elem_val->special = ConstValSpecialUndef; elem_val->type = actual_array_type->data.array.child_type; elem_val->parent.id = ConstParentIdArray; @@ -17477,9 +19753,12 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len, false); - array_ptr_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = ConstPtrMutInfer; + array_ptr_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutInfer; } else { - zig_unreachable(); + ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, + buf_sprintf("expected array type or [_], found '%s'", + buf_ptr(&array_type->name))); + return ira->codegen->invalid_instruction; } } @@ -17489,7 +19768,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct { if (array_type->id == ZigTypeIdPointer) { IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; @@ -17499,18 +19778,37 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: - mem_size = 1; - old_size = 1; - new_index = index; + if (array_ptr_val->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { + ZigValue *array_val = array_ptr_val->data.x_ptr.data.ref.pointee; + new_index = index; + ZigType *array_type = array_val->type; + mem_size = array_type->data.array.len; + if (array_type->data.array.sentinel != nullptr) { + mem_size += 1; + } + old_size = mem_size; - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.data.base_array.array_val = array_val; + out_val->data.x_ptr.data.base_array.elem_index = new_index; + } else { + mem_size = 1; + old_size = 1; + new_index = index; + + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; + } break; case ConstPtrSpecialBaseArray: { size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; new_index = offset + index; - mem_size = array_ptr_val->data.x_ptr.data.base_array.array_val->type->data.array.len; + ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type; + mem_size = array_type->data.array.len; + if (array_type->data.array.sentinel != nullptr) { + mem_size += 1; + } old_size = mem_size - offset; assert(array_ptr_val->data.x_ptr.data.base_array.array_val); @@ -17519,8 +19817,6 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct out_val->data.x_ptr.data.base_array.array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; - out_val->data.x_ptr.data.base_array.is_cstr = - array_ptr_val->data.x_ptr.data.base_array.is_cstr; break; } @@ -17546,20 +19842,23 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } return result; } else if (is_slice(array_type)) { - ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_field = array_ptr_val->data.x_struct.fields[slice_ptr_index]; ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base); if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len, nullptr); - result->value.type = return_type; + result->value->type = return_type; return result; } - ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; + ZigValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; + ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); - if (index >= slice_len) { + uint64_t full_slice_len = slice_len + + ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); + if (index >= full_slice_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, index, slice_len)); @@ -17578,13 +19877,17 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; uint64_t new_index = offset + index; - assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len); + if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != + ConstArraySpecialBuf) + { + ir_assert(new_index < + ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, + &elem_ptr_instruction->base); + } out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = ptr_field->data.x_ptr.data.base_array.array_val; out_val->data.x_ptr.data.base_array.elem_index = new_index; - out_val->data.x_ptr.data.base_array.is_cstr = - ptr_field->data.x_ptr.data.base_array.is_cstr; break; } case ConstPtrSpecialBaseStruct: @@ -17603,18 +19906,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; - } else if (array_type->id == ZigTypeIdArray) { + } else if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { IrInstruction *result; if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, - false, elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type); - result->value.type = return_type; - result->value.special = ConstValSpecialStatic; + false, elem_ptr_instruction->ptr_len, nullptr); + result->value->type = return_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, &elem_ptr_instruction->base, return_type); } - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; @@ -17625,6 +19928,15 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } } } + } else if (array_type->id == ZigTypeIdVector) { + // runtime known element index + ZigType *elem_type = array_type->data.vector.elem_type; + uint32_t host_vec_len = array_type->data.vector.len; + return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME, + nullptr, nullptr); } else { // runtime known element index switch (type_requires_comptime(ira->codegen, return_type)) { @@ -17661,8 +19973,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, - elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type); - result->value.type = return_type; + elem_ptr_instruction->ptr_len, nullptr); + result->value->type = return_type; return result; } @@ -17673,8 +19985,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, if (!is_slice(bare_struct_type)) { ScopeDecls *container_scope = get_container_scope(bare_struct_type); assert(container_scope != nullptr); - auto entry = container_scope->decl_table.maybe_get(field_name); - Tld *tld = entry ? entry->value : nullptr; + auto tld = find_container_decl(ira->codegen, container_scope, field_name); if (tld) { if (tld->id == TldIdFn) { resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); @@ -17724,6 +20035,18 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, return ira->codegen->invalid_instruction; } +static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { + if (field->init_val != nullptr) return; + if (field->decl_node->type != NodeTypeStructField) return; + AstNode *init_node = field->decl_node->data.struct_field.value; + if (init_node == nullptr) return; + // scope is not the scope of the struct init, it's the scope of the struct type decl + Scope *analyze_scope = &get_container_scope(container_type)->base; + // memoize it + field->init_val = analyze_const_value(codegen, analyze_scope, init_node, + field->type_entry, nullptr, UndefOk); +} + static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr, TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing) { @@ -17731,47 +20054,64 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction ZigType *field_type = resolve_struct_field_type(ira->codegen, field); if (field_type == nullptr) return ira->codegen->invalid_instruction; + if (field->is_comptime) { + IrInstruction *elem = ir_const(ira, source_instr, field_type); + memoize_field_init_val(ira->codegen, struct_type, field); + copy_const_val(elem->value, field->init_val); + return ir_get_ref(ira, source_instr, elem, true, false); + } switch (type_has_one_possible_value(ira->codegen, field_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueYes: { - IrInstruction *elem = ir_const(ira, source_instr, field_type); + IrInstruction *elem = ir_const_move(ira, source_instr, + get_the_one_possible_value(ira->codegen, field_type)); return ir_get_ref(ira, source_instr, elem, false, false); } case OnePossibleValueNo: break; } - if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_instruction; - assert(struct_ptr->value.type->id == ZigTypeIdPointer); - uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host; - uint32_t ptr_host_int_bytes = struct_ptr->value.type->data.pointer.host_int_bytes; - uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? - get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; - bool is_const = struct_ptr->value.type->data.pointer.is_const; - bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile; - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, PtrLenSingle, field->align, - (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), - (uint32_t)host_int_bytes_for_result_type, false); + bool is_const = struct_ptr->value->type->data.pointer.is_const; + bool is_volatile = struct_ptr->value->type->data.pointer.is_volatile; + ZigType *ptr_type; + if (is_anon_container(struct_type)) { + ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); + } else { + ResolveStatus needed_resolve_status = + (struct_type->data.structure.layout == ContainerLayoutAuto) ? + ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown; + if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status))) + return ira->codegen->invalid_instruction; + assert(struct_ptr->value->type->id == ZigTypeIdPointer); + uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host; + uint32_t ptr_host_int_bytes = struct_ptr->value->type->data.pointer.host_int_bytes; + uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? + get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; + ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, PtrLenSingle, field->align, + (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), + (uint32_t)host_int_bytes_for_result_type, false); + } if (instr_is_comptime(struct_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (struct_val == nullptr) return ira->codegen->invalid_instruction; if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_instruction; if (initializing && struct_val->special == ConstValSpecialUndef) { - struct_val->data.x_struct.fields = create_const_vals(struct_type->data.structure.src_field_count); + struct_val->data.x_struct.fields = alloc_const_vals_ptrs(struct_type->data.structure.src_field_count); struct_val->special = ConstValSpecialStatic; for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - ConstExprValue *field_val = &struct_val->data.x_struct.fields[i]; + ZigValue *field_val = struct_val->data.x_struct.fields[i]; field_val->special = ConstValSpecialUndef; - field_val->type = struct_type->data.structure.fields[i].type_entry; + field_val->type = resolve_struct_field_type(ira->codegen, + struct_type->data.structure.fields[i]); field_val->parent.id = ConstParentIdStruct; field_val->parent.data.p_struct.struct_val = struct_val; field_val->parent.data.p_struct.field_index = i; @@ -17781,12 +20121,12 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, struct_ptr, field); - result->value.type = ptr_type; - result->value.special = ConstValSpecialStatic; + result->value->type = ptr_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, ptr_type); } - ConstExprValue *const_val = &result->value; + ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; @@ -17796,7 +20136,51 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction } IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, struct_ptr, field); - result->value.type = ptr_type; + result->value->type = ptr_type; + return result; +} + +static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type) +{ + // The type of the field is not available until a store using this pointer happens. + // So, here we create a special pointer type which has the inferred struct type and + // field name encoded in the type. Later, when there is a store via this pointer, + // the field type will then be available, and the field will be added to the inferred + // struct. + + ZigType *container_ptr_type = container_ptr->value->type; + ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr); + + InferredStructField *inferred_struct_field = allocate(1, "InferredStructField"); + inferred_struct_field->inferred_struct_type = container_type; + inferred_struct_field->field_name = field_name; + + ZigType *elem_type = ira->codegen->builtin_types.entry_var; + ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr); + + if (instr_is_comptime(container_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr_type, container_ptr, CastOpNoop); + } else { + result = ir_const(ira, source_instr, field_ptr_type); + } + copy_const_val(result->value, ptr_val); + result->value->type = field_ptr_type; + return result; + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, field_ptr_type, container_ptr, CastOpNoop); + result->value->type = field_ptr_type; return result; } @@ -17806,10 +20190,17 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ Error err; ZigType *bare_type = container_ref_type(container_type); - if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusSizeKnown))) + + if (initializing && bare_type->id == ZigTypeIdStruct && + bare_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type); + } + + if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - assert(container_ptr->value.type->id == ZigTypeIdPointer); + assert(container_ptr->value->type->id == ZigTypeIdPointer); if (bare_type->id == ZigTypeIdStruct) { TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field != nullptr) { @@ -17826,8 +20217,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } if (bare_type->id == ZigTypeIdUnion) { - bool is_const = container_ptr->value.type->data.pointer.is_const; - bool is_volatile = container_ptr->value.type->data.pointer.is_volatile; + bool is_const = container_ptr->value->type->data.pointer.is_const; + bool is_volatile = container_ptr->value->type->data.pointer.is_volatile; TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field == nullptr) { @@ -17842,19 +20233,20 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(container_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; - if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && + ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (union_val == nullptr) return ira->codegen->invalid_instruction; if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; if (initializing) { - ConstExprValue *payload_val = create_const_vals(1); + ZigValue *payload_val = create_const_vals(1); payload_val->special = ConstValSpecialUndef; payload_val->type = field_type; payload_val->parent.id = ConstParentIdUnion; @@ -17863,7 +20255,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ union_val->special = ConstValSpecialStatic; bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); union_val->data.x_union.payload = payload_val; - } else { + } else if (bare_type->data.unionation.layout != ContainerLayoutExtern) { TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); if (actual_field == nullptr) zig_unreachable(); @@ -17876,21 +20268,20 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } } - ConstExprValue *payload_val = union_val->data.x_union.payload; - + ZigValue *payload_val = union_val->data.x_union.payload; IrInstruction *result; if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field, true, initializing); - result->value.type = ptr_type; - result->value.special = ConstValSpecialStatic; + result->value->type = ptr_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, ptr_type); } - ConstExprValue *const_val = &result->value; + ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.mut = container_ptr->value->data.x_ptr.mut; const_val->data.x_ptr.data.ref.pointee = payload_val; return result; } @@ -17898,7 +20289,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field, true, initializing); - result->value.type = ptr_type; + result->value->type = ptr_type; return result; } @@ -18003,10 +20394,10 @@ static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_n static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { Error err; IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child; - if (type_is_invalid(container_ptr->value.type)) + if (type_is_invalid(container_ptr->value->type)) return ira->codegen->invalid_instruction; - ZigType *container_type = container_ptr->value.type->data.pointer.child_type; + ZigType *container_type = container_ptr->value->type->data.pointer.child_type; Buf *field_name = field_ptr_instruction->field_name_buffer; if (!field_name) { @@ -18021,8 +20412,12 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(container_type)) { return ira->codegen->invalid_instruction; + } else if (is_tuple(container_type) && !field_ptr_instruction->initializing && buf_eql_str(field_name, "len")) { + IrInstruction *len_inst = ir_const_unsigned(ira, &field_ptr_instruction->base, + container_type->data.structure.src_field_count); + return ir_get_ref(ira, &field_ptr_instruction->base, len_inst, true, false); } else if (is_slice(container_type) || is_container_ref(container_type)) { - assert(container_ptr->value.type->id == ZigTypeIdPointer); + assert(container_ptr->value->type->id == ZigTypeIdPointer); if (container_type->id == ZigTypeIdPointer) { ZigType *bare_type = container_ref_type(container_type); IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr, nullptr); @@ -18034,39 +20429,13 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) { if (buf_eql_str(field_name, "len")) { - ConstExprValue *len_val = create_const_vals(1); + ZigValue *len_val = create_const_vals(1); if (container_type->id == ZigTypeIdPointer) { init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); } else { init_const_usize(ira->codegen, len_val, container_type->data.array.len); } - ZigType *usize = ira->codegen->builtin_types.entry_usize; - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_get_const_ptr(ira, &field_ptr_instruction->base, len_val, - usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), - buf_ptr(&container_type->name))); - return ira->codegen->invalid_instruction; - } - } else if (container_type->id == ZigTypeIdArgTuple) { - ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); - if (!container_ptr_val) - return ira->codegen->invalid_instruction; - - assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); - if (child_val == nullptr) - return ira->codegen->invalid_instruction; - - if (buf_eql_str(field_name, "len")) { - ConstExprValue *len_val = create_const_vals(1); - size_t len = child_val->data.x_arg_tuple.end_index - child_val->data.x_arg_tuple.start_index; - init_const_usize(ira->codegen, len_val, len); - ZigType *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; bool ptr_is_volatile = false; @@ -18079,12 +20448,12 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } } else if (container_type->id == ZigTypeIdMetaType) { - ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + ZigValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!container_ptr_val) return ira->codegen->invalid_instruction; - assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); + assert(container_ptr->value->type->id == ZigTypeIdPointer); + ZigValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->invalid_instruction; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, @@ -18180,7 +20549,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } err_set_type = child_type; } - ConstExprValue *const_val = create_const_vals(1); + ZigValue *const_val = create_const_vals(1); const_val->special = ConstValSpecialStatic; const_val->type = err_set_type; const_val->data.x_err_set = err_entry; @@ -18365,11 +20734,11 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { IrInstruction *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) + if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_store_ptr(ira, &instruction->base, ptr, value, instruction->allow_write_through_const); @@ -18377,14 +20746,14 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { IrInstruction *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) + if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_instruction; return ir_get_deref(ira, &instruction->base, ptr, nullptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { IrInstruction *expr_value = typeof_instruction->value->child; - ZigType *type_entry = expr_value->value.type; + ZigType *type_entry = expr_value->value->type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; return ir_const_type(ira, &typeof_instruction->base, type_entry); @@ -18547,11 +20916,11 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstructionSliceType *slice_type_instruction) { IrInstruction *result = ir_const(ira, &slice_type_instruction->base, ira->codegen->builtin_types.entry_type); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueSliceType *lazy_slice_type = allocate(1); - lazy_slice_type->ira = ira; - result->value.data.x_lazy = &lazy_slice_type->base; + LazyValueSliceType *lazy_slice_type = allocate(1, "LazyValueSliceType"); + lazy_slice_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_slice_type->base; lazy_slice_type->base.id = LazyValueIdSliceType; if (slice_type_instruction->align_value != nullptr) { @@ -18560,6 +20929,12 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; } + if (slice_type_instruction->sentinel != nullptr) { + lazy_slice_type->sentinel = slice_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_instruction; + } + lazy_slice_type->elem_type = slice_type_instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; @@ -18571,21 +20946,49 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return result; } -static IrInstruction *ir_analyze_instruction_global_asm(IrAnalyze *ira, IrInstructionGlobalAsm *instruction) { - buf_append_char(&ira->codegen->global_asm, '\n'); - buf_append_buf(&ira->codegen->global_asm, instruction->asm_code); +static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsmSrc *asm_instruction) { + Error err; - return ir_const_void(ira, &instruction->base); -} - -static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) { assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr); + AstNode *node = asm_instruction->base.source_node; AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr; + Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child); + if (template_buf == nullptr) + return ira->codegen->invalid_instruction; + + if (asm_instruction->is_global) { + buf_append_char(&ira->codegen->global_asm, '\n'); + buf_append_buf(&ira->codegen->global_asm, template_buf); + + return ir_const_void(ira, &asm_instruction->base); + } + if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base)) return ira->codegen->invalid_instruction; + ZigList tok_list = {}; + if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) { + return ira->codegen->invalid_instruction; + } + + for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { + AsmToken asm_token = tok_list.at(token_i); + if (asm_token.id == AsmTokenIdVar) { + size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf); + if (index == SIZE_MAX) { + const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; + uint32_t len = asm_token.end - asm_token.start - 2; + + add_node_error(ira->codegen, node, + buf_sprintf("could not find '%.*s' in the inputs or outputs", + len, ptr)); + return ira->codegen->invalid_instruction; + } + } + } + // TODO validate the output types and variable types IrInstruction **input_list = allocate(asm_expr->input_list.length); @@ -18604,92 +21007,63 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { IrInstruction *const input_value = asm_instruction->input_list[i]->child; - if (type_is_invalid(input_value->value.type)) + if (type_is_invalid(input_value->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(input_value) && - (input_value->value.type->id == ZigTypeIdComptimeInt || - input_value->value.type->id == ZigTypeIdComptimeFloat)) { + (input_value->value->type->id == ZigTypeIdComptimeInt || + input_value->value->type->id == ZigTypeIdComptimeFloat)) { ir_add_error_node(ira, input_value->source_node, - buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name))); + buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value->type->name))); return ira->codegen->invalid_instruction; } input_list[i] = input_value; } - IrInstruction *result = ir_build_asm(&ira->new_irb, + IrInstruction *result = ir_build_asm_gen(ira, asm_instruction->base.scope, asm_instruction->base.source_node, - asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len, + template_buf, tok_list.items, tok_list.length, input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, asm_instruction->has_side_effects); - result->value.type = return_type; + result->value->type = return_type; return result; } static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstructionArrayType *array_type_instruction) { - Error err; + IrInstruction *result = ir_const(ira, &array_type_instruction->base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; - IrInstruction *size_value = array_type_instruction->size->child; - uint64_t size; - if (!ir_resolve_usize(ira, size_value, &size)) + LazyValueArrayType *lazy_array_type = allocate(1, "LazyValueArrayType"); + lazy_array_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_array_type->base; + lazy_array_type->base.id = LazyValueIdArrayType; + + lazy_array_type->elem_type = array_type_instruction->child_type->child; + if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *child_type_value = array_type_instruction->child_type->child; - ZigType *child_type = ir_resolve_type(ira, child_type_value); - if (type_is_invalid(child_type)) + if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length)) return ira->codegen->invalid_instruction; - switch (child_type->id) { - case ZigTypeIdInvalid: // handled above - zig_unreachable(); - case ZigTypeIdUnreachable: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - ir_add_error_node(ira, array_type_instruction->base.source_node, - buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name))); + + if (array_type_instruction->sentinel != nullptr) { + lazy_array_type->sentinel = array_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr) return ira->codegen->invalid_instruction; - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdBoundFn: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - ZigType *result_type = get_array_type(ira->codegen, child_type, size); - return ir_const_type(ira, &array_type_instruction->base, result_type); - } } - zig_unreachable(); + + return result; } static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueSizeOf *lazy_size_of = allocate(1); - lazy_size_of->ira = ira; - result->value.data.x_lazy = &lazy_size_of->base; + LazyValueSizeOf *lazy_size_of = allocate(1, "LazyValueSizeOf"); + lazy_size_of->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_size_of->base; lazy_size_of->base.id = LazyValueIdSizeOf; lazy_size_of->target_type = instruction->type_value->child; @@ -18700,11 +21074,11 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi } static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { - ZigType *type_entry = value->value.type; + ZigType *type_entry = value->value->type; if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) { if (instr_is_comptime(value)) { - ConstExprValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk); + ZigValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk); if (c_ptr_val == nullptr) return ira->codegen->invalid_instruction; if (c_ptr_val->special == ConstValSpecialUndef) @@ -18717,11 +21091,11 @@ static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, source_inst->scope, source_inst->source_node, value); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdOptional) { if (instr_is_comptime(value)) { - ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefOk); + ZigValue *maybe_val = ir_resolve_const(ira, value, UndefOk); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; if (maybe_val->special == ConstValSpecialUndef) @@ -18732,7 +21106,7 @@ static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, source_inst->scope, source_inst->source_node, value); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { return ir_const_bool(ira, source_inst, false); @@ -18743,7 +21117,7 @@ static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *so static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_test_non_null(ira, &instruction->base, value); @@ -18752,20 +21126,17 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on, bool initializing) { - ZigType *ptr_type = base_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *type_entry = ptr_type->data.pointer.child_type; + ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) { if (instr_is_comptime(base_ptr)) { - ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); + ZigValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); + ZigValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (c_ptr_val == nullptr) return ira->codegen->invalid_instruction; bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || @@ -18793,16 +21164,17 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false); + base_ptr->value->type->data.pointer.is_const, base_ptr->value->type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0, false); - bool same_comptime_repr = types_have_same_zig_comptime_repr(type_entry, child_type); + bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry); if (instr_is_comptime(base_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + ZigValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (optional_val == nullptr) return ira->codegen->invalid_instruction; @@ -18812,7 +21184,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; case OnePossibleValueNo: if (!same_comptime_repr) { - ConstExprValue *payload_val = create_const_vals(1); + ZigValue *payload_val = create_const_vals(1); payload_val->type = child_type; payload_val->special = ConstValSpecialUndef; payload_val->parent.id = ConstParentIdOptionalPayload; @@ -18823,7 +21195,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr } break; case OnePossibleValueYes: { - ConstExprValue *pointee = create_const_vals(1); + ZigValue *pointee = create_const_vals(1); pointee->special = ConstValSpecialStatic; pointee->type = child_type; pointee->parent.id = ConstParentIdOptionalPayload; @@ -18843,12 +21215,12 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr, false, initializing); - result->value.type = result_type; - result->value.special = ConstValSpecialStatic; + result->value->type = result_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } - ConstExprValue *result_val = &result->value; + ZigValue *result_val = result->value; result_val->data.x_ptr.special = ConstPtrSpecialRef; result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; switch (type_has_one_possible_value(ira->codegen, child_type)) { @@ -18873,7 +21245,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr, safety_check_on, initializing); - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -18881,7 +21253,7 @@ static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, IrInstructionOptionalUnwrapPtr *instruction) { IrInstruction *base_ptr = instruction->base_ptr->child; - if (type_is_invalid(base_ptr->value.type)) + if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, @@ -18894,26 +21266,26 @@ static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCt return ira->codegen->invalid_instruction; IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value.type)) + if (type_is_invalid(op->value->type)) return ira->codegen->invalid_instruction; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base, 0); if (instr_is_comptime(op)) { - ConstExprValue *val = ir_resolve_const(ira, op, UndefOk); + ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_instruction; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); - size_t result_usize = bigint_ctz(&op->value.data.x_bigint, int_type->data.integral.bit_count); + size_t result_usize = bigint_ctz(&op->value->data.x_bigint, int_type->data.integral.bit_count); return ir_const_unsigned(ira, &instruction->base, result_usize); } ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); IrInstruction *result = ir_build_ctz(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, op); - result->value.type = return_type; + result->value->type = return_type; return result; } @@ -18923,26 +21295,26 @@ static IrInstruction *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionCl return ira->codegen->invalid_instruction; IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value.type)) + if (type_is_invalid(op->value->type)) return ira->codegen->invalid_instruction; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base, 0); if (instr_is_comptime(op)) { - ConstExprValue *val = ir_resolve_const(ira, op, UndefOk); + ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_instruction; if (val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); - size_t result_usize = bigint_clz(&op->value.data.x_bigint, int_type->data.integral.bit_count); + size_t result_usize = bigint_clz(&op->value->data.x_bigint, int_type->data.integral.bit_count); return ir_const_unsigned(ira, &instruction->base, result_usize); } ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); IrInstruction *result = ir_build_clz(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, op); - result->value.type = return_type; + result->value->type = return_type; return result; } @@ -18952,14 +21324,14 @@ static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value.type)) + if (type_is_invalid(op->value->type)) return ira->codegen->invalid_instruction; if (int_type->data.integral.bit_count == 0) return ir_const_unsigned(ira, &instruction->base, 0); if (instr_is_comptime(op)) { - ConstExprValue *val = ir_resolve_const(ira, op, UndefOk); + ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_instruction; if (val->special == ConstValSpecialUndef) @@ -18976,50 +21348,50 @@ static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstruc ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, op); - result->value.type = return_type; + result->value->type = return_type; return result; } static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; - if (value->value.type->id == ZigTypeIdEnum) { + if (value->value->type->id == ZigTypeIdEnum) { return value; } - if (value->value.type->id != ZigTypeIdUnion) { + if (value->value->type->id != ZigTypeIdUnion) { ir_add_error(ira, value, - buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); + buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); return ira->codegen->invalid_instruction; } - if (!value->value.type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) { + if (!value->value->type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum")); - if (value->value.type->data.unionation.decl_node != nullptr) { - add_error_note(ira->codegen, msg, value->value.type->data.unionation.decl_node, + if (value->value->type->data.unionation.decl_node != nullptr) { + add_error_note(ira->codegen, msg, value->value->type->data.unionation.decl_node, buf_sprintf("declared here")); } return ira->codegen->invalid_instruction; } - ZigType *tag_type = value->value.type->data.unionation.tag_type; + ZigType *tag_type = value->value->type->data.unionation.tag_type; assert(tag_type->id == ZigTypeIdEnum); if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = tag_type; - const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_bigint(&const_instruction->base.value.data.x_enum_tag, &val->data.x_union.tag); + const_instruction->base.value->type = tag_type; + const_instruction->base.value->special = ConstValSpecialStatic; + bigint_init_bigint(&const_instruction->base.value->data.x_enum_tag, &val->data.x_union.tag); return &const_instruction->base; } IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value); - result->value.type = tag_type; + result->value->type = tag_type; return result; } @@ -19027,11 +21399,11 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstructionSwitchBr *switch_br_instruction) { IrInstruction *target_value = switch_br_instruction->target_value->child; - if (type_is_invalid(target_value->value.type)) + if (type_is_invalid(target_value->value->type)) return ir_unreach_error(ira); if (switch_br_instruction->switch_prongs_void != nullptr) { - if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value.type)) { + if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value->type)) { return ir_unreach_error(ira); } } @@ -19044,7 +21416,7 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, return ira->codegen->invalid_instruction; if (is_comptime || instr_is_comptime(target_value)) { - ConstExprValue *target_val = ir_resolve_const(ira, target_value, UndefBad); + ZigValue *target_val = ir_resolve_const(ira, target_value, UndefBad); if (!target_val) return ir_unreach_error(ira); @@ -19052,20 +21424,20 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, for (size_t i = 0; i < case_count; i += 1) { IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstruction *case_value = old_case->value->child; - if (type_is_invalid(case_value->value.type)) + if (type_is_invalid(case_value->value->type)) return ir_unreach_error(ira); - if (case_value->value.type->id == ZigTypeIdEnum) { + if (case_value->value->type->id == ZigTypeIdEnum) { case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); - if (type_is_invalid(case_value->value.type)) + if (type_is_invalid(case_value->value->type)) return ir_unreach_error(ira); } - IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value.type); - if (type_is_invalid(casted_case_value->value.type)) + IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); + if (type_is_invalid(casted_case_value->value->type)) return ir_unreach_error(ira); - ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); + ZigValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); if (!case_val) return ir_unreach_error(ira); @@ -19082,7 +21454,7 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstruction *result = ir_build_br(&ira->new_irb, switch_br_instruction->base.scope, switch_br_instruction->base.source_node, new_dest_block, nullptr); - result->value.type = ira->codegen->builtin_types.entry_unreachable; + result->value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } } @@ -19102,17 +21474,17 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstruction *old_value = old_case->value; IrInstruction *new_value = old_value->child; - if (type_is_invalid(new_value->value.type)) + if (type_is_invalid(new_value->value->type)) continue; - if (new_value->value.type->id == ZigTypeIdEnum) { + if (new_value->value->type->id == ZigTypeIdEnum) { new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value); - if (type_is_invalid(new_value->value.type)) + if (type_is_invalid(new_value->value->type)) continue; } - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value.type); - if (type_is_invalid(casted_new_value->value.type)) + IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value->type); + if (type_is_invalid(casted_new_value->value->type)) continue; if (!ir_resolve_const(ira, casted_new_value, UndefBad)) @@ -19132,7 +21504,7 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstructionSwitchBr *switch_br = ir_build_switch_br(&ira->new_irb, switch_br_instruction->base.scope, switch_br_instruction->base.source_node, target_value, new_else_block, case_count, cases, nullptr, nullptr); - switch_br->base.value.type = ira->codegen->builtin_types.entry_unreachable; + switch_br->base.value->type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, &switch_br->base); } @@ -19141,20 +21513,20 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, { Error err; IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value.type)) + if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_instruction; - if (target_value_ptr->value.type->id == ZigTypeIdMetaType) { + if (target_value_ptr->value->type->id == ZigTypeIdMetaType) { assert(instr_is_comptime(target_value_ptr)); - ZigType *ptr_type = target_value_ptr->value.data.x_type; + ZigType *ptr_type = target_value_ptr->value->data.x_type; assert(ptr_type->id == ZigTypeIdPointer); return ir_const_type(ira, &switch_target_instruction->base, ptr_type->data.pointer.child_type); } - ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; - ConstExprValue *pointee_val = nullptr; - if (instr_is_comptime(target_value_ptr)) { - pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node); + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; + ZigValue *pointee_val = nullptr; + if (instr_is_comptime(target_value_ptr) && target_value_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + pointee_val = const_ptr_pointee(ira, ira->codegen, target_value_ptr->value, target_value_ptr->source_node); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; @@ -19180,13 +21552,13 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdErrorSet: { if (pointee_val) { IrInstruction *result = ir_const(ira, &switch_target_instruction->base, nullptr); - copy_const_val(&result->value, pointee_val, true); - result->value.type = target_type; + copy_const_val(result->value, pointee_val); + result->value->type = target_type; return result; } IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr); - result->value.type = target_type; + result->value->type = target_type; return result; } case ZigTypeIdUnion: { @@ -19205,22 +21577,22 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, assert(tag_type->id == ZigTypeIdEnum); if (pointee_val) { IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type); - bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_union.tag); + bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag); return result; } if (tag_type->data.enumeration.src_field_count == 1) { IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type); TypeEnumField *only_field = &tag_type->data.enumeration.fields[0]; - bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value); + bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); return result; } IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr); - union_value->value.type = target_type; + union_value->value->type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, union_value); - union_tag_inst->value.type = tag_type; + union_tag_inst->value->type = tag_type; return union_tag_inst; } case ZigTypeIdEnum: { @@ -19229,18 +21601,18 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, if (target_type->data.enumeration.src_field_count < 2) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type); - bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value); + bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); return result; } if (pointee_val) { IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type); - bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_enum_tag); + bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_enum_tag); return result; } IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr); - enum_value->value.type = target_type; + enum_value->value->type = target_type; return enum_value; } case ZigTypeIdErrorUnion: @@ -19251,7 +21623,6 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdNull: case ZigTypeIdOptional: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdVector: case ZigTypeIdFnFrame: @@ -19265,12 +21636,12 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) { IrInstruction *target_value_ptr = instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value.type)) + if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_instruction; - ZigType *ref_type = target_value_ptr->value.type; + ZigType *ref_type = target_value_ptr->value->type; assert(ref_type->id == ZigTypeIdPointer); - ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; if (target_type->id == ZigTypeIdUnion) { ZigType *enum_type = target_type->data.unionation.tag_type; assert(enum_type != nullptr); @@ -19278,14 +21649,14 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru assert(instruction->prongs_len > 0); IrInstruction *first_prong_value = instruction->prongs_ptr[0]->child; - if (type_is_invalid(first_prong_value->value.type)) + if (type_is_invalid(first_prong_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type); - if (type_is_invalid(first_casted_prong_value->value.type)) + if (type_is_invalid(first_casted_prong_value->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad); + ZigValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad); if (first_prong_val == nullptr) return ira->codegen->invalid_instruction; @@ -19294,14 +21665,14 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru ErrorMsg *invalid_payload_msg = nullptr; for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) { IrInstruction *this_prong_inst = instruction->prongs_ptr[prong_i]->child; - if (type_is_invalid(this_prong_inst->value.type)) + if (type_is_invalid(this_prong_inst->value->type)) return ira->codegen->invalid_instruction; IrInstruction *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type); - if (type_is_invalid(this_casted_prong_value->value.type)) + if (type_is_invalid(this_casted_prong_value->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad); + ZigValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad); if (this_prong == nullptr) return ira->codegen->invalid_instruction; @@ -19324,18 +21695,18 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru } if (instr_is_comptime(target_value_ptr)) { - ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); + ZigValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); if (!target_value_ptr) return ira->codegen->invalid_instruction; - ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.source_node); + ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.source_node); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, get_pointer_to_type(ira->codegen, first_field->type_entry, target_val_ptr->type->data.pointer.is_const)); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; @@ -19344,8 +21715,8 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target_value_ptr, first_field, false, false); - result->value.type = get_pointer_to_type(ira->codegen, first_field->type_entry, - target_value_ptr->value.type->data.pointer.is_const); + result->value->type = get_pointer_to_type(ira->codegen, first_field->type_entry, + target_value_ptr->value->type->data.pointer.is_const); return result; } else if (target_type->id == ZigTypeIdErrorSet) { // construct an error set from the prong values @@ -19388,12 +21759,12 @@ static IrInstruction *ir_analyze_instruction_switch_else_var(IrAnalyze *ira, IrInstructionSwitchElseVar *instruction) { IrInstruction *target_value_ptr = instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value.type)) + if (type_is_invalid(target_value_ptr->value->type)) return ira->codegen->invalid_instruction; - ZigType *ref_type = target_value_ptr->value.type; + ZigType *ref_type = target_value_ptr->value->type; assert(ref_type->id == ZigTypeIdPointer); - ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; if (target_type->id == ZigTypeIdErrorSet) { // make a new set that has the other cases removed if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.source_node)) { @@ -19409,12 +21780,12 @@ static IrInstruction *ir_analyze_instruction_switch_else_var(IrAnalyze *ira, for (size_t case_i = 0; case_i < instruction->switch_br->case_count; case_i += 1) { IrInstructionSwitchBrCase *br_case = &instruction->switch_br->cases[case_i]; IrInstruction *case_expr = br_case->value->child; - if (case_expr->value.type->id == ZigTypeIdErrorSet) { + if (case_expr->value->type->id == ZigTypeIdErrorSet) { ErrorTableEntry *err = ir_resolve_error(ira, case_expr); if (err == nullptr) return ira->codegen->invalid_instruction; errors[err->value] = err; - } else if (case_expr->value.type->id == ZigTypeIdMetaType) { + } else if (case_expr->value->type->id == ZigTypeIdMetaType) { ZigType *err_set_type = ir_resolve_type(ira, case_expr); if (type_is_invalid(err_set_type)) return ira->codegen->invalid_instruction; @@ -19479,57 +21850,18 @@ static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructio AstNode *source_node = import_instruction->base.source_node; ZigType *import = source_node->owner; + ZigType *target_import; Buf *import_target_path; - Buf *search_dir; - assert(import->data.structure.root_struct->package); - ZigPackage *target_package; - auto package_entry = import->data.structure.root_struct->package->package_table.maybe_get(import_target_str); - SourceKind source_kind; - if (package_entry) { - target_package = package_entry->value; - import_target_path = &target_package->root_src_path; - search_dir = &target_package->root_src_dir; - source_kind = SourceKindPkgMain; - } else { - // try it as a filename - target_package = import->data.structure.root_struct->package; - import_target_path = import_target_str; - - // search relative to importing file - search_dir = buf_alloc(); - os_path_dirname(import->data.structure.root_struct->path, search_dir); - - source_kind = SourceKindNonRoot; - } - Buf full_path = BUF_INIT; - os_path_join(search_dir, import_target_path, &full_path); - - Buf *import_code = buf_alloc(); - Buf *resolved_path = buf_alloc(); - - Buf *resolve_paths[] = { &full_path, }; - *resolved_path = os_path_resolve(resolve_paths, 1); - - auto import_entry = ira->codegen->import_table.maybe_get(resolved_path); - if (import_entry) { - return ir_const_type(ira, &import_instruction->base, import_entry->value); - } - - if (source_kind == SourceKindNonRoot) { - ZigPackage *cur_scope_pkg = scope_package(import_instruction->base.scope); - Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir; - Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); - if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) { + if ((err = analyze_import(ira->codegen, import, import_target_str, &target_import, + &import_target_path, &full_path))) + { + if (err == ErrorImportOutsidePkgPath) { ir_add_error_node(ira, source_node, buf_sprintf("import of file outside package path: '%s'", buf_ptr(import_target_path))); return ira->codegen->invalid_instruction; - } - } - - if ((err = file_fetch(ira->codegen, resolved_path, import_code))) { - if (err == ErrorFileNotFound) { + } else if (err == ErrorFileNotFound) { ir_add_error_node(ira, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return ira->codegen->invalid_instruction; @@ -19540,14 +21872,12 @@ static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructio } } - ZigType *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code, source_kind); - return ir_const_type(ira, &import_instruction->base, target_import); } static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) { IrInstruction *value = ref_instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile); } @@ -19573,13 +21903,13 @@ static IrInstruction *ir_analyze_union_init(IrAnalyze *ira, IrInstruction *sourc if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_instruction; - if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (instr_is_comptime(field_result_loc) && - field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { // nothing } else { - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; } } @@ -19608,7 +21938,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc } IrInstructionContainerInitFieldsField *field = &fields[0]; IrInstruction *field_result_loc = field->result_loc->child; - if (type_is_invalid(field_result_loc->value.type)) + if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_union_init(ira, instruction, field->source_node, container_type, field->name, @@ -19621,6 +21951,11 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } + if (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) { + // We're now done inferring the type. + container_type->data.structure.resolve_status = ResolveStatusUnstarted; + } + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; @@ -19650,7 +21985,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstructionContainerInitFieldsField *field = &fields[i]; IrInstruction *field_result_loc = field->result_loc->child; - if (type_is_invalid(field_result_loc->value.type)) + if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_instruction; TypeStructField *type_field = find_struct_type_field(container_type, field->name); @@ -19674,7 +22009,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc field_assign_nodes[field_index] = field->source_node; if (instr_is_comptime(field_result_loc) && - field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(field_result_loc); } else { @@ -19687,33 +22022,24 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (field_assign_nodes[i] != nullptr) continue; // look for a default field value - TypeStructField *field = &container_type->data.structure.fields[i]; + TypeStructField *field = container_type->data.structure.fields[i]; + memoize_field_init_val(ira->codegen, container_type, field); if (field->init_val == nullptr) { - // it's not memoized. time to go analyze it - assert(field->decl_node->type == NodeTypeStructField); - AstNode *init_node = field->decl_node->data.struct_field.value; - if (init_node == nullptr) { - ir_add_error_node(ira, instruction->source_node, - buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); - any_missing = true; - continue; - } - // scope is not the scope of the struct init, it's the scope of the struct type decl - Scope *analyze_scope = &get_container_scope(container_type)->base; - // memoize it - field->init_val = analyze_const_value(ira->codegen, analyze_scope, init_node, - field->type_entry, nullptr, UndefOk); + ir_add_error_node(ira, instruction->source_node, + buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name))); + any_missing = true; + continue; } if (type_is_invalid(field->init_val->type)) return ira->codegen->invalid_instruction; IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type); - copy_const_val(&runtime_inst->value, field->init_val, true); + copy_const_val(runtime_inst->value, field->init_val); IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, instruction, field, result_loc, container_type, true); ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst, false); - if (instr_is_comptime(field_ptr) && field_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { + if (instr_is_comptime(field_ptr) && field_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(field_ptr); } else { first_non_const_instruction = result_loc; @@ -19722,13 +22048,13 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (any_missing) return ira->codegen->invalid_instruction; - if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (const_ptrs.length != actual_field_count) { - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstruction *field_result_loc = const_ptrs.at(i); IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc, nullptr); - field_result_loc->value.special = ConstValSpecialRuntime; + field_result_loc->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref, false); } } @@ -19748,15 +22074,20 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; + ir_assert(instruction->result_loc != nullptr, &instruction->base); + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value->type)) + return result_loc; + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base); + + ZigType *container_type = result_loc->value->type->data.pointer.child_type; size_t elem_count = instruction->item_count; if (is_slice(container_type)) { - ir_add_error(ira, instruction->container_type, - buf_sprintf("expected array type or [_], found slice")); + ir_add_error_node(ira, instruction->init_array_type_source_node, + buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", + buf_ptr(&container_type->name))); return ira->codegen->invalid_instruction; } @@ -19772,39 +22103,41 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, if (container_type->id == ZigTypeIdStruct && elem_count == 0) { ir_assert(instruction->result_loc != nullptr, &instruction->base); IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) + if (type_is_invalid(result_loc->value->type)) return result_loc; return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc); } - if (container_type->id != ZigTypeIdArray) { + if (container_type->id == ZigTypeIdArray) { + ZigType *child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, nullptr); + + ir_add_error(ira, &instruction->base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_instruction; + } + } else if (container_type->id == ZigTypeIdStruct && + container_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + // We're now done inferring the type. + container_type->data.structure.resolve_status = ResolveStatusUnstarted; + } else if (container_type->id == ZigTypeIdVector) { + // OK + } else { ir_add_error_node(ira, instruction->base.source_node, buf_sprintf("type '%s' does not support array initialization", buf_ptr(&container_type->name))); return ira->codegen->invalid_instruction; } - ir_assert(instruction->result_loc != nullptr, &instruction->base); - IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) - return result_loc; - ir_assert(result_loc->value.type->id == ZigTypeIdPointer, &instruction->base); - - ZigType *child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - - ir_add_error(ira, &instruction->base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_instruction; - } - switch (type_has_one_possible_value(ira->codegen, container_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; case OnePossibleValueYes: - return ir_const(ira, &instruction->base, container_type); + return ir_const_move(ira, &instruction->base, + get_the_one_possible_value(ira->codegen, container_type)); case OnePossibleValueNo: break; } @@ -19834,13 +22167,13 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, for (size_t i = 0; i < elem_count; i += 1) { IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]->child; - if (type_is_invalid(elem_result_loc->value.type)) + if (type_is_invalid(elem_result_loc->value->type)) return ira->codegen->invalid_instruction; - assert(elem_result_loc->value.type->id == ZigTypeIdPointer); + assert(elem_result_loc->value->type->id == ZigTypeIdPointer); if (instr_is_comptime(elem_result_loc) && - elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + elem_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(elem_result_loc); } else { @@ -19848,14 +22181,18 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, } } - if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { if (const_ptrs.length != elem_count) { - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; for (size_t i = 0; i < const_ptrs.length; i += 1) { IrInstruction *elem_result_loc = const_ptrs.at(i); - assert(elem_result_loc->value.special == ConstValSpecialStatic); + assert(elem_result_loc->value->special == ConstValSpecialStatic); + if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { + // This field will be generated comptime; no need to do this. + continue; + } IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr); - elem_result_loc->value.special = ConstValSpecialRuntime; + elem_result_loc->value->special = ConstValSpecialRuntime; ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false); } } @@ -19871,7 +22208,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ZigType *result_elem_type = result_loc->value.type->data.pointer.child_type; + ZigType *result_elem_type = result_loc->value->type->data.pointer.child_type; if (is_slice(result_elem_type)) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'", @@ -19886,16 +22223,14 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - ir_assert(instruction->result_loc != nullptr, &instruction->base); IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) + if (type_is_invalid(result_loc->value->type)) return result_loc; + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base); + ZigType *container_type = result_loc->value->type->data.pointer.child_type; + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, instruction->field_count, instruction->fields, result_loc); } @@ -19918,15 +22253,15 @@ static IrInstruction *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstr fprintf(stderr, "| "); for (size_t i = 0; i < instruction->msg_count; i += 1) { IrInstruction *msg = instruction->msg_list[i]->child; - if (type_is_invalid(msg->value.type)) + if (type_is_invalid(msg->value->type)) return ira->codegen->invalid_instruction; buf_resize(&buf, 0); - if (msg->value.special == ConstValSpecialLazy) { + if (msg->value->special == ConstValSpecialLazy) { // Resolve any lazy value that's passed, we need its value - if (ir_resolve_lazy(ira->codegen, msg->source_node, &msg->value)) + if (ir_resolve_lazy(ira->codegen, msg->source_node, msg->value)) return ira->codegen->invalid_instruction; } - render_const_value(ira->codegen, &buf, &msg->value); + render_const_value(ira->codegen, &buf, msg->value); const char *comma_str = (i != 0) ? ", " : ""; fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); } @@ -19945,28 +22280,28 @@ static IrInstruction *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_instruction; ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (instr_is_comptime(casted_value)) { - ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (val == nullptr) return ira->codegen->invalid_instruction; - ErrorTableEntry *err = casted_value->value.data.x_err_set; + ErrorTableEntry *err = casted_value->value->data.x_err_set; if (!err->cached_error_name_val) { - ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name); + ZigValue *array_val = create_const_str_lit(ira->codegen, &err->name)->data.x_ptr.data.ref.pointee; err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); } IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - copy_const_val(&result->value, err->cached_error_name_val, true); - result->value.type = str_type; + copy_const_val(result->value, err->cached_error_name_val); + result->value->type = str_type; return result; } @@ -19974,25 +22309,25 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_err_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, value); - result->value.type = str_type; + result->value->type = str_type; return result; } static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) { Error err; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - assert(target->value.type->id == ZigTypeIdEnum); + assert(target->value->type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) + if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); - ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); + TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); + ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - init_const_slice(ira->codegen, &result->value, array_val, 0, buf_len(field->name), true); + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); return result; } @@ -20002,7 +22337,7 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); - result->value.type = get_slice_type(ira->codegen, u8_ptr_type); + result->value->type = get_slice_type(ira->codegen, u8_ptr_type); return result; } @@ -20021,7 +22356,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, return ira->codegen->invalid_instruction; IrInstruction *field_ptr = instruction->field_ptr->child; - if (type_is_invalid(field_ptr->value.type)) + if (type_is_invalid(field_ptr->value->type)) return ira->codegen->invalid_instruction; if (container_type->id != ZigTypeIdStruct) { @@ -20041,9 +22376,9 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - if (field_ptr->value.type->id != ZigTypeIdPointer) { + if (field_ptr->value->type->id != ZigTypeIdPointer) { ir_add_error(ira, field_ptr, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value->type->name))); return ira->codegen->invalid_instruction; } @@ -20052,22 +22387,22 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type); ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, - field_ptr->value.type->data.pointer.is_const, - field_ptr->value.type->data.pointer.is_volatile, + field_ptr->value->type->data.pointer.is_const, + field_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, field_ptr_align, 0, 0, false); IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); - if (type_is_invalid(casted_field_ptr->value.type)) + if (type_is_invalid(casted_field_ptr->value->type)) return ira->codegen->invalid_instruction; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type, - casted_field_ptr->value.type->data.pointer.is_const, - casted_field_ptr->value.type->data.pointer.is_volatile, + casted_field_ptr->value->type->data.pointer.is_const, + casted_field_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, parent_ptr_align, 0, 0, false); if (instr_is_comptime(casted_field_ptr)) { - ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); + ZigValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); if (!field_ptr_val) return ira->codegen->invalid_instruction; @@ -20086,7 +22421,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, } IrInstruction *result = ir_const(ira, &instruction->base, result_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val; out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut; @@ -20095,7 +22430,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, IrInstruction *result = ir_build_field_parent_ptr(&ira->new_irb, instruction->base.scope, instruction->base.source_node, type_value, field_name_value, casted_field_ptr, field); - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -20145,7 +22480,7 @@ static IrInstruction *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, IrInstructionByteOffsetOf *instruction) { IrInstruction *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value.type)) + if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *field_name_value = instruction->field_name->child; @@ -20161,7 +22496,7 @@ static IrInstruction *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, IrInstructionBitOffsetOf *instruction) { IrInstruction *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value.type)) + if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *field_name_value = instruction->field_name->child; size_t byte_offset = 0; @@ -20177,17 +22512,17 @@ static void ensure_field_index(ZigType *type, const char *field_name, size_t ind Buf *field_name_buf; assert(type != nullptr && !type_is_invalid(type)); - // Check for our field by creating a buffer in place then using the comma operator to free it so that we don't - // leak memory in debug mode. - assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index && - (buf_deinit(field_name_buf), true)); + field_name_buf = buf_create_from_str(field_name); + TypeStructField *field = find_struct_type_field(type, field_name_buf); + buf_deinit(field_name_buf); + + if (field == nullptr || field->src_index != index) + zig_panic("reference to unknown field %s", field_name); } static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { Error err; - ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_var->type->id == ZigTypeIdMetaType); - ZigType *type_info_type = type_info_var->data.x_type; + ZigType *type_info_type = get_builtin_type(ira->codegen, "TypeInfo"); assert(type_info_type->id == ZigTypeIdUnion); if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) { zig_unreachable(); @@ -20218,7 +22553,7 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, nullptr, var->const_value); } -static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *out_val, +static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *out_val, ScopeDecls *decls_scope) { Error err; @@ -20268,9 +22603,9 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr } } - ConstExprValue *declaration_array = create_const_vals(1); + ZigValue *declaration_array = create_const_vals(1); declaration_array->special = ConstValSpecialStatic; - declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count); + declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr); declaration_array->data.x_array.special = ConstArraySpecialNone; declaration_array->data.x_array.data.s_none.elements = create_const_vals(declaration_count); init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false); @@ -20289,22 +22624,22 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr continue; } - ConstExprValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index]; + ZigValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index]; declaration_val->special = ConstValSpecialStatic; declaration_val->type = type_info_declaration_type; - ConstExprValue *inner_fields = create_const_vals(3); - ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_bool; - inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; - inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = type_info_declaration_data_type; - inner_fields[2].parent.id = ConstParentIdStruct; - inner_fields[2].parent.data.p_struct.struct_val = declaration_val; - inner_fields[2].parent.data.p_struct.field_index = 1; + ZigValue **inner_fields = alloc_const_vals_ptrs(3); + ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(curr_entry->key), true); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[1]->data.x_bool = curr_entry->value->visib_mod == VisibModPub; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = type_info_declaration_data_type; + inner_fields[2]->parent.id = ConstParentIdStruct; + inner_fields[2]->parent.data.p_struct.struct_val = declaration_val; + inner_fields[2]->parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: @@ -20316,18 +22651,19 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type declaration. // 0: Data.Type: type - bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->const_value; + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); + inner_fields[2]->data.x_union.payload = var->const_value; } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type - bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 1); - ConstExprValue *payload = create_const_vals(1); + ZigValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = var->const_value->type; - inner_fields[2].data.x_union.payload = payload; + inner_fields[2]->data.x_union.payload = payload; } break; @@ -20335,7 +22671,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr case TldIdFn: { // 2: Data.Fn: Data.FnDecl - bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 2); ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); @@ -20347,91 +22683,86 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto; - ConstExprValue *fn_decl_val = create_const_vals(1); + ZigValue *fn_decl_val = create_const_vals(1); fn_decl_val->special = ConstValSpecialStatic; fn_decl_val->type = type_info_fn_decl_type; fn_decl_val->parent.id = ConstParentIdUnion; - fn_decl_val->parent.data.p_union.union_val = &inner_fields[2]; + fn_decl_val->parent.data.p_union.union_val = inner_fields[2]; - ConstExprValue *fn_decl_fields = create_const_vals(9); + ZigValue **fn_decl_fields = alloc_const_vals_ptrs(9); fn_decl_val->data.x_struct.fields = fn_decl_fields; // fn_type: type ensure_field_index(fn_decl_val->type, "fn_type", 0); - fn_decl_fields[0].special = ConstValSpecialStatic; - fn_decl_fields[0].type = ira->codegen->builtin_types.entry_type; - fn_decl_fields[0].data.x_type = fn_entry->type_entry; + fn_decl_fields[0]->special = ConstValSpecialStatic; + fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type; + fn_decl_fields[0]->data.x_type = fn_entry->type_entry; // inline_type: Data.FnDecl.Inline ensure_field_index(fn_decl_val->type, "inline_type", 1); - fn_decl_fields[1].special = ConstValSpecialStatic; - fn_decl_fields[1].type = type_info_fn_decl_inline_type; - bigint_init_unsigned(&fn_decl_fields[1].data.x_enum_tag, fn_entry->fn_inline); - // calling_convention: TypeInfo.CallingConvention - ensure_field_index(fn_decl_val->type, "calling_convention", 2); - fn_decl_fields[2].special = ConstValSpecialStatic; - fn_decl_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr); - bigint_init_unsigned(&fn_decl_fields[2].data.x_enum_tag, fn_node->cc); + fn_decl_fields[1]->special = ConstValSpecialStatic; + fn_decl_fields[1]->type = type_info_fn_decl_inline_type; + bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline); // is_var_args: bool - ensure_field_index(fn_decl_val->type, "is_var_args", 3); + ensure_field_index(fn_decl_val->type, "is_var_args", 2); bool is_varargs = fn_node->is_var_args; - fn_decl_fields[3].special = ConstValSpecialStatic; - fn_decl_fields[3].type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[3].data.x_bool = is_varargs; + fn_decl_fields[2]->special = ConstValSpecialStatic; + fn_decl_fields[2]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[2]->data.x_bool = is_varargs; // is_extern: bool - ensure_field_index(fn_decl_val->type, "is_extern", 4); - fn_decl_fields[4].special = ConstValSpecialStatic; - fn_decl_fields[4].type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[4].data.x_bool = fn_node->is_extern; + ensure_field_index(fn_decl_val->type, "is_extern", 3); + fn_decl_fields[3]->special = ConstValSpecialStatic; + fn_decl_fields[3]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[3]->data.x_bool = fn_node->is_extern; // is_export: bool - ensure_field_index(fn_decl_val->type, "is_export", 5); - fn_decl_fields[5].special = ConstValSpecialStatic; - fn_decl_fields[5].type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[5].data.x_bool = fn_node->is_export; + ensure_field_index(fn_decl_val->type, "is_export", 4); + fn_decl_fields[4]->special = ConstValSpecialStatic; + fn_decl_fields[4]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[4]->data.x_bool = fn_node->is_export; // lib_name: ?[]const u8 - ensure_field_index(fn_decl_val->type, "lib_name", 6); - fn_decl_fields[6].special = ConstValSpecialStatic; + ensure_field_index(fn_decl_val->type, "lib_name", 5); + fn_decl_fields[5]->special = ConstValSpecialStatic; ZigType *u8_ptr = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0, false); - fn_decl_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + fn_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) { - fn_decl_fields[6].data.x_optional = create_const_vals(1); - ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0, + fn_decl_fields[5]->data.x_optional = create_const_vals(1); + ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); } else { - fn_decl_fields[6].data.x_optional = nullptr; + fn_decl_fields[5]->data.x_optional = nullptr; } // return_type: type - ensure_field_index(fn_decl_val->type, "return_type", 7); - fn_decl_fields[7].special = ConstValSpecialStatic; - fn_decl_fields[7].type = ira->codegen->builtin_types.entry_type; - fn_decl_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + ensure_field_index(fn_decl_val->type, "return_type", 6); + fn_decl_fields[6]->special = ConstValSpecialStatic; + fn_decl_fields[6]->type = ira->codegen->builtin_types.entry_type; + fn_decl_fields[6]->data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; // arg_names: [][] const u8 - ensure_field_index(fn_decl_val->type, "arg_names", 8); + ensure_field_index(fn_decl_val->type, "arg_names", 7); size_t fn_arg_count = fn_entry->variable_list.length; - ConstExprValue *fn_arg_name_array = create_const_vals(1); + ZigValue *fn_arg_name_array = create_const_vals(1); fn_arg_name_array->special = ConstValSpecialStatic; fn_arg_name_array->type = get_array_type(ira->codegen, - get_slice_type(ira->codegen, u8_ptr), fn_arg_count); + get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); - init_const_slice(ira->codegen, &fn_decl_fields[8], fn_arg_name_array, 0, fn_arg_count, false); + init_const_slice(ira->codegen, fn_decl_fields[7], fn_arg_name_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); - ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; - ConstExprValue *arg_name = create_const_str_lit(ira->codegen, - buf_create_from_str(arg_var->name)); + ZigValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; + ZigValue *arg_name = create_const_str_lit(ira->codegen, + buf_create_from_str(arg_var->name))->data.x_ptr.data.ref.pointee; init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, strlen(arg_var->name), true); fn_arg_name_val->parent.id = ConstParentIdArray; fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } - inner_fields[2].data.x_union.payload = fn_decl_val; + inner_fields[2]->data.x_union.payload = fn_decl_val; break; } case TldIdContainer: @@ -20441,13 +22772,14 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr return ErrorSemanticAnalyzeFail; // This is a type. - bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); - ConstExprValue *payload = create_const_vals(1); + ZigValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; payload->type = ira->codegen->builtin_types.entry_type; payload->data.x_type = type_entry; - inner_fields[2].data.x_union.payload = payload; + inner_fields[2]->data.x_union.payload = payload; break; } @@ -20456,7 +22788,7 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr } declaration_val->data.x_struct.fields = inner_fields; - declaration_index++; + declaration_index += 1; } assert(declaration_index == declaration_count); @@ -20488,12 +22820,12 @@ static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) { zig_unreachable(); } -static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { +static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { Error err; ZigType *attrs_type; BuiltinPtrSize size_enum_index; if (is_slice(ptr_type_entry)) { - attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry; + attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index]->type_entry; size_enum_index = BuiltinPtrSizeSlice; } else if (ptr_type_entry->id == ZigTypeIdPointer) { attrs_type = ptr_type_entry; @@ -20502,76 +22834,85 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty zig_unreachable(); } - if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusSizeKnown))) return nullptr; ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown)); - ConstExprValue *result = create_const_vals(1); + ZigValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = type_info_pointer_type; - ConstExprValue *fields = create_const_vals(6); + ZigValue **fields = alloc_const_vals_ptrs(7); result->data.x_struct.fields = fields; // size: Size ensure_field_index(result->type, "size", 0); ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); assertNoError(type_resolve(ira->codegen, type_info_pointer_size_type, ResolveStatusSizeKnown)); - fields[0].special = ConstValSpecialStatic; - fields[0].type = type_info_pointer_size_type; - bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = type_info_pointer_size_type; + bigint_init_unsigned(&fields[0]->data.x_enum_tag, size_enum_index); // is_const: bool ensure_field_index(result->type, "is_const", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = attrs_type->data.pointer.is_const; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_bool; + fields[1]->data.x_bool = attrs_type->data.pointer.is_const; // is_volatile: bool ensure_field_index(result->type, "is_volatile", 2); - fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_bool; - fields[2].data.x_bool = attrs_type->data.pointer.is_volatile; + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = ira->codegen->builtin_types.entry_bool; + fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; // alignment: u32 ensure_field_index(result->type, "alignment", 3); - fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type)); + fields[3]->special = ConstValSpecialStatic; + fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[3]->data.x_bigint, get_ptr_align(ira->codegen, attrs_type)); // child: type ensure_field_index(result->type, "child", 4); - fields[4].special = ConstValSpecialStatic; - fields[4].type = ira->codegen->builtin_types.entry_type; - fields[4].data.x_type = attrs_type->data.pointer.child_type; + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = ira->codegen->builtin_types.entry_type; + fields[4]->data.x_type = attrs_type->data.pointer.child_type; // is_allowzero: bool ensure_field_index(result->type, "is_allowzero", 5); - fields[5].special = ConstValSpecialStatic; - fields[5].type = ira->codegen->builtin_types.entry_bool; - fields[5].data.x_bool = attrs_type->data.pointer.allow_zero; + fields[5]->special = ConstValSpecialStatic; + fields[5]->type = ira->codegen->builtin_types.entry_bool; + fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; + // sentinel: var + ensure_field_index(result->type, "sentinel", 6); + fields[6]->special = ConstValSpecialStatic; + if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) { + fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); + set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); + } else { + fields[6]->type = ira->codegen->builtin_types.entry_null; + } return result; }; -static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field, +static void make_enum_field_val(IrAnalyze *ira, ZigValue *enum_field_val, TypeEnumField *enum_field, ZigType *type_info_enum_field_type) { enum_field_val->special = ConstValSpecialStatic; enum_field_val->type = type_info_enum_field_type; - ConstExprValue *inner_fields = create_const_vals(2); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; + ZigValue **inner_fields = alloc_const_vals_ptrs(2); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; - ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); + ZigValue *name = create_const_str_lit(ira->codegen, enum_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(enum_field->name), true); - bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); + bigint_init_bigint(&inner_fields[1]->data.x_bigint, &enum_field->value); enum_field_val->data.x_struct.fields = inner_fields; } static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry, - ConstExprValue **out) + ZigValue **out) { Error err; assert(type_entry != nullptr); @@ -20586,7 +22927,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr return ErrorNone; } - ConstExprValue *result = nullptr; + ZigValue *result = nullptr; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -20599,9 +22940,8 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr case ZigTypeIdEnumLiteral: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: - result = &ira->codegen->const_void_val; + result = ira->codegen->intern.for_void(); break; case ZigTypeIdInt: { @@ -20609,19 +22949,19 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int", nullptr); - ConstExprValue *fields = create_const_vals(2); + ZigValue **fields = alloc_const_vals_ptrs(2); result->data.x_struct.fields = fields; // is_signed: bool ensure_field_index(result->type, "is_signed", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_bool; - fields[0].data.x_bool = type_entry->data.integral.is_signed; + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_bool; + fields[0]->data.x_bool = type_entry->data.integral.is_signed; // bits: u8 ensure_field_index(result->type, "bits", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.integral.bit_count); break; } @@ -20631,14 +22971,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float", nullptr); - ConstExprValue *fields = create_const_vals(1); + ZigValue **fields = alloc_const_vals_ptrs(1); result->data.x_struct.fields = fields; // bits: u8 ensure_field_index(result->type, "bits", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.floating.bit_count); break; } @@ -20655,20 +22995,23 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array", nullptr); - ConstExprValue *fields = create_const_vals(2); + ZigValue **fields = alloc_const_vals_ptrs(3); result->data.x_struct.fields = fields; // len: usize ensure_field_index(result->type, "len", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.array.len); // child: type ensure_field_index(result->type, "child", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - fields[1].data.x_type = type_entry->data.array.child_type; - + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.array.child_type; + // sentinel: var + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type); + fields[2]->data.x_optional = type_entry->data.array.sentinel; break; } case ZigTypeIdVector: { @@ -20676,19 +23019,19 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Vector", nullptr); - ConstExprValue *fields = create_const_vals(2); + ZigValue **fields = alloc_const_vals_ptrs(2); result->data.x_struct.fields = fields; // len: usize ensure_field_index(result->type, "len", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.vector.len); // child: type ensure_field_index(result->type, "child", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - fields[1].data.x_type = type_entry->data.vector.elem_type; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.vector.elem_type; break; } @@ -20698,14 +23041,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Optional", nullptr); - ConstExprValue *fields = create_const_vals(1); + ZigValue **fields = alloc_const_vals_ptrs(1); result->data.x_struct.fields = fields; // child: type ensure_field_index(result->type, "child", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_type; - fields[0].data.x_type = type_entry->data.maybe.child_type; + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_type; + fields[0]->data.x_type = type_entry->data.maybe.child_type; break; } @@ -20714,14 +23057,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr); - ConstExprValue *fields = create_const_vals(1); + ZigValue **fields = alloc_const_vals_ptrs(1); result->data.x_struct.fields = fields; // child: ?type ensure_field_index(result->type, "child", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); - fields[0].data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr : + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[0]->data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr : create_const_type(ira->codegen, type_entry->data.any_frame.result_type); break; } @@ -20731,19 +23074,19 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum", nullptr); - ConstExprValue *fields = create_const_vals(4); + ZigValue **fields = alloc_const_vals_ptrs(4); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.enumeration.layout); // tag_type: type ensure_field_index(result->type, "tag_type", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - fields[1].data.x_type = type_entry->data.enumeration.tag_int_type; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.enumeration.tag_int_type; // fields: []TypeInfo.EnumField ensure_field_index(result->type, "fields", 2); @@ -20753,18 +23096,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; - ConstExprValue *enum_field_array = create_const_vals(1); + ZigValue *enum_field_array = create_const_vals(1); enum_field_array->special = ConstValSpecialStatic; - enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr); enum_field_array->data.x_array.special = ConstArraySpecialNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); - init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); + init_const_slice(ira->codegen, fields[2], enum_field_array, 0, enum_field_count, false); for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; - ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; + ZigValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); enum_field_val->parent.id = ConstParentIdArray; enum_field_val->parent.data.p_array.array_val = enum_field_array; @@ -20772,7 +23115,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 3); - if ((err = ir_make_type_info_decls(ira, source_instr, &fields[3], + if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], type_entry->data.enumeration.decls_scope))) { return err; @@ -20797,35 +23140,35 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) { zig_unreachable(); } - ConstExprValue *slice_val = create_const_vals(1); + ZigValue *slice_val = create_const_vals(1); result->data.x_optional = slice_val; uint32_t error_count = type_entry->data.error_set.err_count; - ConstExprValue *error_array = create_const_vals(1); + ZigValue *error_array = create_const_vals(1); error_array->special = ConstValSpecialStatic; - error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr); error_array->data.x_array.special = ConstArraySpecialNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false); for (uint32_t error_index = 0; error_index < error_count; error_index++) { ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; - ConstExprValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index]; + ZigValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index]; error_val->special = ConstValSpecialStatic; error_val->type = type_info_error_type; - ConstExprValue *inner_fields = create_const_vals(2); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; + ZigValue **inner_fields = alloc_const_vals_ptrs(2); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; - ConstExprValue *name = nullptr; + ZigValue *name = nullptr; if (error->cached_error_name_val != nullptr) name = error->cached_error_name_val; if (name == nullptr) - name = create_const_str_lit(ira->codegen, &error->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true); - bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); + name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); + bigint_init_unsigned(&inner_fields[1]->data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; error_val->parent.id = ConstParentIdArray; @@ -20841,20 +23184,20 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr); - ConstExprValue *fields = create_const_vals(2); + ZigValue **fields = alloc_const_vals_ptrs(2); result->data.x_struct.fields = fields; // error_set: type ensure_field_index(result->type, "error_set", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_type; - fields[0].data.x_type = type_entry->data.error_union.err_set_type; + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_type; + fields[0]->data.x_type = type_entry->data.error_union.err_set_type; // payload: type ensure_field_index(result->type, "payload", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - fields[1].data.x_type = type_entry->data.error_union.payload_type; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.error_union.payload_type; break; } @@ -20864,30 +23207,30 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Union", nullptr); - ConstExprValue *fields = create_const_vals(4); + ZigValue **fields = alloc_const_vals_ptrs(4); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.unionation.layout); // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || union_decl_node->data.container_decl.init_arg_expr != nullptr) { - ConstExprValue *tag_type = create_const_vals(1); + ZigValue *tag_type = create_const_vals(1); tag_type->special = ConstValSpecialStatic; tag_type->type = ira->codegen->builtin_types.entry_type; tag_type->data.x_type = type_entry->data.unionation.tag_type; - fields[1].data.x_optional = tag_type; + fields[1]->data.x_optional = tag_type; } else { - fields[1].data.x_optional = nullptr; + fields[1]->data.x_optional = nullptr; } // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); @@ -20897,40 +23240,40 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr zig_unreachable(); uint32_t union_field_count = type_entry->data.unionation.src_field_count; - ConstExprValue *union_field_array = create_const_vals(1); + ZigValue *union_field_array = create_const_vals(1); union_field_array->special = ConstValSpecialStatic; - union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr); union_field_array->data.x_array.special = ConstArraySpecialNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); - init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); + init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false); ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; - ConstExprValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index]; + ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index]; union_field_val->special = ConstValSpecialStatic; union_field_val->type = type_info_union_field_type; - ConstExprValue *inner_fields = create_const_vals(3); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type); + ZigValue **inner_fields = alloc_const_vals_ptrs(3); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = get_optional_type(ira->codegen, type_info_enum_field_type); - if (fields[1].data.x_optional == nullptr) { - inner_fields[1].data.x_optional = nullptr; + if (fields[1]->data.x_optional == nullptr) { + inner_fields[1]->data.x_optional = nullptr; } else { - inner_fields[1].data.x_optional = create_const_vals(1); - make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); + inner_fields[1]->data.x_optional = create_const_vals(1); + make_enum_field_val(ira, inner_fields[1]->data.x_optional, union_field->enum_field, type_info_enum_field_type); } - inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = ira->codegen->builtin_types.entry_type; - inner_fields[2].data.x_type = union_field->type_entry; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = ira->codegen->builtin_types.entry_type; + inner_fields[2]->data.x_type = union_field->type_entry; - ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); + ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; union_field_val->parent.id = ConstParentIdArray; @@ -20939,7 +23282,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 3); - if ((err = ir_make_type_info_decls(ira, source_instr, &fields[3], + if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], type_entry->data.unionation.decls_scope))) { return err; @@ -20949,7 +23292,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } case ZigTypeIdStruct: { - if (type_entry->data.structure.is_slice) { + if (type_entry->data.structure.special == StructSpecialSlice) { result = create_ptr_like_type_info(ira, type_entry); if (result == nullptr) return ErrorSemanticAnalyzeFail; @@ -20960,14 +23303,14 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct", nullptr); - ConstExprValue *fields = create_const_vals(3); + ZigValue **fields = alloc_const_vals_ptrs(3); result->data.x_struct.fields = fields; // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); // fields: []TypeInfo.StructField ensure_field_index(result->type, "fields", 1); @@ -20977,24 +23320,24 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } uint32_t struct_field_count = type_entry->data.structure.src_field_count; - ConstExprValue *struct_field_array = create_const_vals(1); + ZigValue *struct_field_array = create_const_vals(1); struct_field_array->special = ConstValSpecialStatic; - struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr); struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); - init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); + init_const_slice(ira->codegen, fields[1], struct_field_array, 0, struct_field_count, false); for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { - TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; - ConstExprValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index]; + TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; + ZigValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index]; struct_field_val->special = ConstValSpecialStatic; struct_field_val->type = type_info_struct_field_type; - ConstExprValue *inner_fields = create_const_vals(3); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); + ZigValue **inner_fields = alloc_const_vals_ptrs(3); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); ZigType *field_type = resolve_struct_field_type(ira->codegen, struct_field); if (field_type == nullptr) @@ -21002,21 +23345,21 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) return err; if (!type_has_bits(struct_field->type_entry)) { - inner_fields[1].data.x_optional = nullptr; + inner_fields[1]->data.x_optional = nullptr; } else { size_t byte_offset = struct_field->offset; - inner_fields[1].data.x_optional = create_const_vals(1); - inner_fields[1].data.x_optional->special = ConstValSpecialStatic; - inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset); + inner_fields[1]->data.x_optional = create_const_vals(1); + inner_fields[1]->data.x_optional->special = ConstValSpecialStatic; + inner_fields[1]->data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&inner_fields[1]->data.x_optional->data.x_bigint, byte_offset); } - inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = ira->codegen->builtin_types.entry_type; - inner_fields[2].data.x_type = struct_field->type_entry; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = ira->codegen->builtin_types.entry_type; + inner_fields[2]->data.x_type = struct_field->type_entry; - ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); + ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; struct_field_val->parent.id = ConstParentIdArray; @@ -21025,7 +23368,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr } // decls: []TypeInfo.Declaration ensure_field_index(result->type, "decls", 2); - if ((err = ir_make_type_info_decls(ira, source_instr, &fields[2], + if ((err = ir_make_type_info_decls(ira, source_instr, fields[2], type_entry->data.structure.decls_scope))) { return err; @@ -21039,38 +23382,38 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn", nullptr); - ConstExprValue *fields = create_const_vals(5); + ZigValue **fields = alloc_const_vals_ptrs(5); result->data.x_struct.fields = fields; // calling_convention: TypeInfo.CallingConvention ensure_field_index(result->type, "calling_convention", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ir_type_info_get_type(ira, "CallingConvention", nullptr); - bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); // is_generic: bool ensure_field_index(result->type, "is_generic", 1); bool is_generic = type_entry->data.fn.is_generic; - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = is_generic; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_bool; + fields[1]->data.x_bool = is_generic; // is_varargs: bool ensure_field_index(result->type, "is_var_args", 2); bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; - fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_bool; - fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = ira->codegen->builtin_types.entry_bool; + fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; // return_type: ?type ensure_field_index(result->type, "return_type", 3); - fields[3].special = ConstValSpecialStatic; - fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[3]->special = ConstValSpecialStatic; + fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) - fields[3].data.x_optional = nullptr; + fields[3]->data.x_optional = nullptr; else { - ConstExprValue *return_type = create_const_vals(1); + ZigValue *return_type = create_const_vals(1); return_type->special = ConstValSpecialStatic; return_type->type = ira->codegen->builtin_types.entry_type; return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; - fields[3].data.x_optional = return_type; + fields[3]->data.x_optional = return_type; } // args: []TypeInfo.FnArg ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); @@ -21080,17 +23423,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); - ConstExprValue *fn_arg_array = create_const_vals(1); + ZigValue *fn_arg_array = create_const_vals(1); fn_arg_array->special = ConstValSpecialStatic; - fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr); fn_arg_array->data.x_array.special = ConstArraySpecialNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); - init_const_slice(ira->codegen, &fields[4], fn_arg_array, 0, fn_arg_count, false); + init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; - ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index]; + ZigValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index]; fn_arg_val->special = ConstValSpecialStatic; fn_arg_val->type = type_info_fn_arg_type; @@ -21098,24 +23441,24 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr bool arg_is_generic = fn_param_info->type == nullptr; if (arg_is_generic) assert(is_generic); - ConstExprValue *inner_fields = create_const_vals(3); - inner_fields[0].special = ConstValSpecialStatic; - inner_fields[0].type = ira->codegen->builtin_types.entry_bool; - inner_fields[0].data.x_bool = arg_is_generic; - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_bool; - inner_fields[1].data.x_bool = fn_param_info->is_noalias; - inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + ZigValue **inner_fields = alloc_const_vals_ptrs(3); + inner_fields[0]->special = ConstValSpecialStatic; + inner_fields[0]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[0]->data.x_bool = arg_is_generic; + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[1]->data.x_bool = fn_param_info->is_noalias; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) - inner_fields[2].data.x_optional = nullptr; + inner_fields[2]->data.x_optional = nullptr; else { - ConstExprValue *arg_type = create_const_vals(1); + ZigValue *arg_type = create_const_vals(1); arg_type->special = ConstValSpecialStatic; arg_type->type = ira->codegen->builtin_types.entry_type; arg_type->data.x_type = fn_param_info->type; - inner_fields[2].data.x_optional = arg_type; + inner_fields[2]->data.x_optional = arg_type; } fn_arg_val->data.x_struct.fields = inner_fields; @@ -21156,12 +23499,12 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); - ConstExprValue *payload; + ZigValue *payload; if ((err = ir_make_type_info_value(ira, &instruction->base, type_entry, &payload))) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, result_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); out_val->data.x_union.payload = payload; @@ -21173,35 +23516,71 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, return result; } -static ConstExprValue *get_const_field(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +static ZigValue *get_const_field(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index) { + Error err; ensure_field_index(struct_value->type, name, field_index); - assert(struct_value->data.x_struct.fields[field_index].special == ConstValSpecialStatic); - return &struct_value->data.x_struct.fields[field_index]; + ZigValue *val = struct_value->data.x_struct.fields[field_index]; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_node, val, UndefBad))) + return nullptr; + return val; } -static bool get_const_field_bool(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +static Error get_const_field_sentinel(IrAnalyze *ira, IrInstruction *source_instr, ZigValue *struct_value, + const char *name, size_t field_index, ZigType *elem_type, ZigValue **result) { - ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + ZigValue *field_val = get_const_field(ira, source_instr->source_node, struct_value, name, field_index); + if (field_val == nullptr) + return ErrorSemanticAnalyzeFail; + + IrInstruction *field_inst = ir_const_move(ira, source_instr, field_val); + IrInstruction *casted_field_inst = ir_implicit_cast(ira, field_inst, + get_optional_type(ira->codegen, elem_type)); + if (type_is_invalid(casted_field_inst->value->type)) + return ErrorSemanticAnalyzeFail; + + if (optional_value_is_null(casted_field_inst->value)) { + *result = nullptr; + } else { + assert(type_has_optional_repr(casted_field_inst->value->type)); + *result = casted_field_inst->value->data.x_optional; + } + + return ErrorNone; +} + +static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index, bool *out) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ErrorSemanticAnalyzeFail; assert(value->type == ira->codegen->builtin_types.entry_bool); - return value->data.x_bool; + *out = value->data.x_bool; + return ErrorNone; } -static BigInt *get_const_field_lit_int(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { - ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return nullptr; assert(value->type == ira->codegen->builtin_types.entry_num_lit_int); return &value->data.x_bigint; } -static ZigType *get_const_field_meta_type(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index) +static ZigType *get_const_field_meta_type(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) { - ConstExprValue *value = get_const_field(ira, struct_value, name, field_index); + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ira->codegen->invalid_instruction->value->type; assert(value->type == ira->codegen->builtin_types.entry_type); return value->data.x_type; } -static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, ZigTypeId tagTypeId, ConstExprValue *payload) { +static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, ZigTypeId tagTypeId, ZigValue *payload) { + Error err; switch (tagTypeId) { case ZigTypeIdInvalid: zig_unreachable(); @@ -21213,17 +23592,25 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi return ira->codegen->builtin_types.entry_bool; case ZigTypeIdUnreachable: return ira->codegen->builtin_types.entry_unreachable; - case ZigTypeIdInt: + case ZigTypeIdInt: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr)); - return get_int_type(ira->codegen, - get_const_field_bool(ira, payload, "is_signed", 0), - bigint_as_u32(get_const_field_lit_int(ira, payload, "bits", 1))); + BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "bits", 1); + if (bi == nullptr) + return ira->codegen->invalid_instruction->value->type; + bool is_signed; + if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_signed", 0, &is_signed))) + return ira->codegen->invalid_instruction->value->type; + return get_int_type(ira->codegen, is_signed, bigint_as_u32(bi)); + } case ZigTypeIdFloat: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr)); - uint32_t bits = bigint_as_u32(get_const_field_lit_int(ira, payload, "bits", 0)); + BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "bits", 0); + if (bi == nullptr) + return ira->codegen->invalid_instruction->value->type; + uint32_t bits = bigint_as_u32(bi); switch (bits) { case 16: return ira->codegen->builtin_types.entry_f16; case 32: return ira->codegen->builtin_types.entry_f32; @@ -21232,38 +23619,80 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi } ir_add_error(ira, instruction, buf_sprintf("%d-bit float unsupported", bits)); - return nullptr; + return ira->codegen->invalid_instruction->value->type; } case ZigTypeIdPointer: { ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); assert(payload->special == ConstValSpecialStatic); assert(payload->type == type_info_pointer_type); - ConstExprValue *size_value = get_const_field(ira, payload, "size", 0); + ZigValue *size_value = get_const_field(ira, instruction->source_node, payload, "size", 0); assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, - get_const_field_meta_type(ira, payload, "child", 4), - get_const_field_bool(ira, payload, "is_const", 1), - get_const_field_bool(ira, payload, "is_volatile", 2), + ZigType *elem_type = get_const_field_meta_type(ira, instruction->source_node, payload, "child", 4); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction->value->type; + ZigValue *sentinel; + if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 6, + elem_type, &sentinel))) + { + return ira->codegen->invalid_instruction->value->type; + } + BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "alignment", 3); + if (bi == nullptr) + return ira->codegen->invalid_instruction->value->type; + + bool is_const; + if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_const", 1, &is_const))) + return ira->codegen->invalid_instruction->value->type; + + bool is_volatile; + if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_volatile", 2, + &is_volatile))) + { + return ira->codegen->invalid_instruction->value->type; + } + + bool is_allowzero; + if ((err = get_const_field_bool(ira, instruction->source_node, payload, "is_allowzero", 5, + &is_allowzero))) + { + return ira->codegen->invalid_instruction->value->type; + } + + + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, + elem_type, + is_const, + is_volatile, ptr_len, - bigint_as_u32(get_const_field_lit_int(ira, payload, "alignment", 3)), + bigint_as_u32(bi), 0, // bit_offset_in_host 0, // host_int_bytes - get_const_field_bool(ira, payload, "is_allowzero", 5) - ); + is_allowzero, + VECTOR_INDEX_NONE, nullptr, sentinel); if (size_enum_index != 2) return ptr_type; return get_slice_type(ira->codegen, ptr_type); } - case ZigTypeIdArray: + case ZigTypeIdArray: { assert(payload->special == ConstValSpecialStatic); assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr)); - return get_array_type(ira->codegen, - get_const_field_meta_type(ira, payload, "child", 1), - bigint_as_u64(get_const_field_lit_int(ira, payload, "len", 0)) - ); + ZigType *elem_type = get_const_field_meta_type(ira, instruction->source_node, payload, "child", 1); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction->value->type; + ZigValue *sentinel; + if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 2, + elem_type, &sentinel))) + { + return ira->codegen->invalid_instruction->value->type; + } + BigInt *bi = get_const_field_lit_int(ira, instruction->source_node, payload, "len", 0); + if (bi == nullptr) + return ira->codegen->invalid_instruction->value->type; + return get_array_type(ira->codegen, elem_type, bigint_as_u64(bi), sentinel); + } case ZigTypeIdComptimeFloat: return ira->codegen->builtin_types.entry_num_lit_float; case ZigTypeIdComptimeInt: @@ -21283,34 +23712,33 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi case ZigTypeIdEnumLiteral: ir_add_error(ira, instruction, buf_sprintf( "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId))); - return nullptr; + return ira->codegen->invalid_instruction->value->type; case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdStruct: ir_add_error(ira, instruction, buf_sprintf( "@Type not availble for 'TypeInfo.%s'", type_id_name(tagTypeId))); - return nullptr; + return ira->codegen->invalid_instruction->value->type; } zig_unreachable(); } static IrInstruction *ir_analyze_instruction_type(IrAnalyze *ira, IrInstructionType *instruction) { IrInstruction *type_info_ir = instruction->type_info->child; - if (type_is_invalid(type_info_ir->value.type)) + if (type_is_invalid(type_info_ir->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_ir = ir_implicit_cast(ira, type_info_ir, ir_type_info_get_type(ira, nullptr, nullptr)); - if (type_is_invalid(casted_ir->value.type)) + if (type_is_invalid(casted_ir->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *type_info_value = ir_resolve_const(ira, casted_ir, UndefBad); + ZigValue *type_info_value = ir_resolve_const(ira, casted_ir, UndefBad); if (!type_info_value) return ira->codegen->invalid_instruction; ZigTypeId typeId = type_id_at_index(bigint_as_usize(&type_info_value->data.x_union.tag)); ZigType *type = type_info_to_type(ira, type_info_ir, typeId, type_info_value->data.x_union.payload); - if (!type) + if (type_is_invalid(type)) return ira->codegen->invalid_instruction; return ir_const_type(ira, &instruction->base, type); } @@ -21323,12 +23751,10 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId"); - assert(var_value->type->id == ZigTypeIdMetaType); - ZigType *result_type = var_value->data.x_type; + ZigType *result_type = get_builtin_type(ira->codegen, "TypeId"); IrInstruction *result = ir_const(ira, &instruction->base, result_type); - bigint_init_unsigned(&result->value.data.x_enum_tag, type_id_index(type_entry)); + bigint_init_unsigned(&result->value->data.x_enum_tag, type_id_index(type_entry)); return result; } @@ -21356,7 +23782,7 @@ static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstruc type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry)); } IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - copy_const_val(&result->value, type_entry->cached_const_name_val, true); + copy_const_val(result->value, type_entry->cached_const_name_val); return result; } @@ -21377,7 +23803,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; - ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ZigValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr, nullptr, UndefBad); if (type_is_invalid(cimport_result->type)) @@ -21455,14 +23881,14 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct clang_argv.append(nullptr); // to make the [start...end] argument work - AstNode *root_node; Stage2ErrorMsg *errors_ptr; size_t errors_len; + Stage2Ast *ast; const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir); - if ((err = parse_h_file(ira->codegen, &root_node, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), Stage2TranslateModeImport, resources_path))) + if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len, + &clang_argv.at(0), &clang_argv.last(), resources_path))) { if (err != ErrorCCompileErrors) { ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); @@ -21476,15 +23902,15 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct } for (size_t i = 0; i < errors_len; i += 1) { Stage2ErrorMsg *clang_err = &errors_ptr[i]; - // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null - if (clang_err->source && clang_err->filename_ptr) { + // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null + if (clang_err->source && clang_err->filename_ptr) { ErrorMsg *err_msg = err_msg_create_with_offset( clang_err->filename_ptr ? buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), clang_err->line, clang_err->column, clang_err->offset, clang_err->source, buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); err_msg_add_note(parent_err_msg, err_msg); - } + } } return ira->codegen->invalid_instruction; @@ -21513,7 +23939,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct buf_sprintf("C import failed: unable to open output file: %s", strerror(errno))); return ira->codegen->invalid_instruction; } - ast_render(out_file, root_node, 4); + stage2_render_ast(ast, out_file); if (fclose(out_file) != 0) { ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno))); @@ -21545,7 +23971,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) { IrInstruction *name_value = instruction->name->child; - if (type_is_invalid(name_value->value.type)) + if (type_is_invalid(name_value->value->type)) return ira->codegen->invalid_instruction; Buf *include_name = ir_resolve_str(ira, name_value); @@ -21563,7 +23989,7 @@ static IrInstruction *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) { IrInstruction *name = instruction->name->child; - if (type_is_invalid(name->value.type)) + if (type_is_invalid(name->value->type)) return ira->codegen->invalid_instruction; Buf *define_name = ir_resolve_str(ira, name); @@ -21571,12 +23997,12 @@ static IrInstruction *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; Buf *define_value = nullptr; // The second parameter is either a string or void (equivalent to "") - if (value->value.type->id != ZigTypeIdVoid) { + if (value->value->type->id != ZigTypeIdVoid) { define_value = ir_resolve_str(ira, value); if (!define_value) return ira->codegen->invalid_instruction; @@ -21594,7 +24020,7 @@ static IrInstruction *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) { IrInstruction *name = instruction->name->child; - if (type_is_invalid(name->value.type)) + if (type_is_invalid(name->value->type)) return ira->codegen->invalid_instruction; Buf *undef_name = ir_resolve_str(ira, name); @@ -21612,7 +24038,7 @@ static IrInstruction *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructi static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) { IrInstruction *name = instruction->name->child; - if (type_is_invalid(name->value.type)) + if (type_is_invalid(name->value->type)) return ira->codegen->invalid_instruction; Buf *rel_file_path = ir_resolve_str(ira, name); @@ -21645,9 +24071,9 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru } ZigType *result_type = get_array_type(ira->codegen, - ira->codegen->builtin_types.entry_u8, buf_len(file_contents)); + ira->codegen->builtin_types.entry_u8, buf_len(file_contents), nullptr); IrInstruction *result = ir_const(ira, &instruction->base, result_type); - init_const_str_lit(ira->codegen, &result->value, file_contents); + init_const_str_lit(ira->codegen, result->value, file_contents); return result; } @@ -21656,26 +24082,32 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; + if (operand_type->id == ZigTypeIdFloat) { + ir_add_error(ira, instruction->type_value->child, + buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) + if (type_is_invalid(ptr->value->type)) return ira->codegen->invalid_instruction; // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); - if (type_is_invalid(casted_ptr->value.type)) + if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *cmp_value = instruction->cmp_value->child; - if (type_is_invalid(cmp_value->value.type)) + if (type_is_invalid(cmp_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *new_value = instruction->new_value->child; - if (type_is_invalid(new_value->value.type)) + if (type_is_invalid(new_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *success_order_value = instruction->success_order_value->child; - if (type_is_invalid(success_order_value->value.type)) + if (type_is_invalid(success_order_value->value->type)) return ira->codegen->invalid_instruction; AtomicOrder success_order; @@ -21683,7 +24115,7 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi return ira->codegen->invalid_instruction; IrInstruction *failure_order_value = instruction->failure_order_value->child; - if (type_is_invalid(failure_order_value->value.type)) + if (type_is_invalid(failure_order_value->value->type)) return ira->codegen->invalid_instruction; AtomicOrder failure_order; @@ -21691,11 +24123,11 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi return ira->codegen->invalid_instruction; IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); - if (type_is_invalid(casted_cmp_value->value.type)) + if (type_is_invalid(casted_cmp_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type); - if (type_is_invalid(casted_new_value->value.type)) + if (type_is_invalid(casted_new_value->value->type)) return ira->codegen->invalid_instruction; if (success_order < AtomicOrderMonotonic) { @@ -21719,7 +24151,8 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi return ira->codegen->invalid_instruction; } - if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { + if (instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar && + instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { zig_panic("TODO compile-time execution of cmpxchg"); } @@ -21728,7 +24161,7 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi if (handle_is_ptr(result_type)) { result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, result_type, nullptr, true, false, true); - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } } else { @@ -21742,7 +24175,7 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { IrInstruction *order_value = instruction->order_value->child; - if (type_is_invalid(order_value->value.type)) + if (type_is_invalid(order_value->value->type)) return ira->codegen->invalid_instruction; AtomicOrder order; @@ -21757,7 +24190,7 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_build_fence(&ira->new_irb, instruction->base.scope, instruction->base.source_node, order_value, order); - result->value.type = ira->codegen->builtin_types.entry_void; + result->value->type = ira->codegen->builtin_types.entry_void; return result; } @@ -21775,7 +24208,7 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct } IrInstruction *target = instruction->target->child; - ZigType *src_type = target->value.type; + ZigType *src_type = target->value->type; if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; @@ -21791,19 +24224,19 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct } if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (val == nullptr) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint, + bigint_truncate(&result->value->data.x_bigint, &val->data.x_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); return result; } if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - bigint_init_unsigned(&result->value.data.x_bigint, 0); + bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } @@ -21819,7 +24252,7 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, instruction->base.source_node, dest_type_value, target); - new_instruction->value.type = dest_type; + new_instruction->value->type = dest_type; return new_instruction; } @@ -21834,12 +24267,12 @@ static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstruct } IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) { + if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'", - buf_ptr(&target->value.type->name))); + buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } @@ -21868,15 +24301,15 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id == ZigTypeIdComptimeInt || - target->value.type->id == ZigTypeIdComptimeFloat) + if (target->value->type->id == ZigTypeIdComptimeInt || + target->value->type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { CastOp op; - if (target->value.type->id == ZigTypeIdComptimeInt) { + if (target->value->type->id == ZigTypeIdComptimeInt) { op = CastOpIntToFloat; } else { op = CastOpNumLitToConcrete; @@ -21887,9 +24320,9 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } } - if (target->value.type->id != ZigTypeIdFloat) { + if (target->value->type->id != ZigTypeIdFloat) { ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", - buf_ptr(&target->value.type->name))); + buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } @@ -21908,12 +24341,12 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst } IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdErrorSet) { + if (target->value->type->id != ZigTypeIdErrorSet) { ir_add_error(ira, instruction->target, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } @@ -21928,20 +24361,20 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; bool src_ptr_const; bool src_ptr_volatile; uint32_t src_ptr_align; - if (target->value.type->id == ZigTypeIdPointer) { - src_ptr_const = target->value.type->data.pointer.is_const; - src_ptr_volatile = target->value.type->data.pointer.is_volatile; + if (target->value->type->id == ZigTypeIdPointer) { + src_ptr_const = target->value->type->data.pointer.is_const; + src_ptr_volatile = target->value->type->data.pointer.is_volatile; - if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align))) + if ((err = resolve_ptr_align(ira, target->value->type, &src_ptr_align))) return ira->codegen->invalid_instruction; - } else if (is_slice(target->value.type)) { - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + } else if (is_slice(target->value->type)) { + ZigType *src_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; src_ptr_const = src_ptr_type->data.pointer.is_const; src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; @@ -21951,10 +24384,10 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru src_ptr_const = true; src_ptr_volatile = false; - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown))) + if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; - src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); + src_ptr_align = get_abi_alignment(ira->codegen, target->value->type); } if (src_ptr_align != 0) { @@ -21973,18 +24406,18 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_instruction; bool have_known_len = false; uint64_t known_len; if (instr_is_comptime(casted_value)) { - ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = val->data.x_struct.fields[slice_len_index]; if (value_is_comptime(len_val)) { known_len = bigint_as_u64(&len_val->data.x_bigint); have_known_len = true; @@ -21993,12 +24426,18 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, dest_slice_type, nullptr, true, false, true); - if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { + if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) { return result_loc; } - if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { - known_len = casted_value->value.data.rh_slice.len; + if (target->value->type->id == ZigTypeIdPointer && + target->value->type->data.pointer.ptr_len == PtrLenSingle && + target->value->type->data.pointer.child_type->id == ZigTypeIdArray) + { + known_len = target->value->type->data.pointer.child_type->data.array.len; + have_known_len = true; + } else if (casted_value->value->data.rh_slice.id == RuntimeHintSliceIdLen) { + known_len = casted_value->value->data.rh_slice.len; have_known_len = true; } @@ -22025,16 +24464,16 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct Error err; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (!is_slice(target->value.type)) { + if (!is_slice(target->value->type)) { ir_add_error(ira, instruction->target, - buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *src_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; uint32_t alignment; if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment))) @@ -22046,22 +24485,22 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); if (instr_is_comptime(target)) { - ConstExprValue *target_val = ir_resolve_const(ira, target, UndefBad); + ZigValue *target_val = ir_resolve_const(ira, target, UndefBad); if (target_val == nullptr) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, dest_slice_type); - result->value.data.x_struct.fields = create_const_vals(2); + result->value->data.x_struct.fields = alloc_const_vals_ptrs(2); - ConstExprValue *ptr_val = &result->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *target_ptr_val = &target_val->data.x_struct.fields[slice_ptr_index]; - copy_const_val(ptr_val, target_ptr_val, false); + ZigValue *ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; + ZigValue *target_ptr_val = target_val->data.x_struct.fields[slice_ptr_index]; + copy_const_val(ptr_val, target_ptr_val); ptr_val->type = dest_ptr_type; - ConstExprValue *len_val = &result->value.data.x_struct.fields[slice_len_index]; + ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; len_val->special = ConstValSpecialStatic; len_val->type = ira->codegen->builtin_types.entry_usize; - ConstExprValue *target_len_val = &target_val->data.x_struct.fields[slice_len_index]; + ZigValue *target_len_val = target_val->data.x_struct.fields[slice_len_index]; ZigType *elem_type = src_ptr_type->data.pointer.child_type; BigInt elem_size_bigint; bigint_init_unsigned(&elem_size_bigint, type_size(ira->codegen, elem_type)); @@ -22072,7 +24511,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, dest_slice_type, nullptr, true, false, true); - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } @@ -22099,51 +24538,57 @@ static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInst return ira->codegen->invalid_instruction; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) { + if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, instruction->target, buf_sprintf("expected int type, found '%s'", - buf_ptr(&target->value.type->name))); + buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat); } +static IrInstruction *ir_analyze_float_to_int(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *dest_type, IrInstruction *operand, AstNode *operand_source_node) +{ + if (operand->value->type->id == ZigTypeIdComptimeInt) { + return ir_implicit_cast(ira, operand, dest_type); + } + + if (operand->value->type->id != ZigTypeIdFloat && operand->value->type->id != ZigTypeIdComptimeFloat) { + ir_add_error_node(ira, operand_source_node, buf_sprintf("expected float type, found '%s'", + buf_ptr(&operand->value->type->name))); + return ira->codegen->invalid_instruction; + } + + return ir_resolve_cast(ira, source_instr, operand, dest_type, CastOpFloatToInt); +} + static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + IrInstruction *operand = instruction->target->child; + if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id == ZigTypeIdComptimeInt) { - return ir_implicit_cast(ira, target, dest_type); - } - - if (target->value.type->id != ZigTypeIdFloat && target->value.type->id != ZigTypeIdComptimeFloat) { - ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", - buf_ptr(&target->value.type->name))); - return ira->codegen->invalid_instruction; - } - - return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt); + return ir_analyze_float_to_int(ira, &instruction->base, dest_type, operand, instruction->target->source_node); } static IrInstruction *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) { IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_target; - if (target->value.type->id == ZigTypeIdErrorSet) { + if (target->value->type->id == ZigTypeIdErrorSet) { casted_target = target; } else { casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); - if (type_is_invalid(casted_target->value.type)) + if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_instruction; } @@ -22152,11 +24597,11 @@ static IrInstruction *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) { IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); - if (type_is_invalid(casted_target->value.type)) + if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set); @@ -22164,12 +24609,12 @@ static IrInstruction *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdBool) { + if (target->value->type->id != ZigTypeIdBool) { ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", - buf_ptr(&target->value.type->name))); + buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } @@ -22220,48 +24665,48 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s ir_assert(is_valid_vector_elem_type(scalar_type), source_instr); uint32_t len_mask; - if (mask->value.type->id == ZigTypeIdVector) { - len_mask = mask->value.type->data.vector.len; - } else if (mask->value.type->id == ZigTypeIdArray) { - len_mask = mask->value.type->data.array.len; + if (mask->value->type->id == ZigTypeIdVector) { + len_mask = mask->value->type->data.vector.len; + } else if (mask->value->type->id == ZigTypeIdArray) { + len_mask = mask->value->type->data.array.len; } else { ir_add_error(ira, mask, buf_sprintf("expected vector or array, found '%s'", - buf_ptr(&mask->value.type->name))); + buf_ptr(&mask->value->type->name))); return ira->codegen->invalid_instruction; } mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask, ira->codegen->builtin_types.entry_i32)); - if (type_is_invalid(mask->value.type)) + if (type_is_invalid(mask->value->type)) return ira->codegen->invalid_instruction; uint32_t len_a; - if (a->value.type->id == ZigTypeIdVector) { - len_a = a->value.type->data.vector.len; - } else if (a->value.type->id == ZigTypeIdArray) { - len_a = a->value.type->data.array.len; - } else if (a->value.type->id == ZigTypeIdUndefined) { + if (a->value->type->id == ZigTypeIdVector) { + len_a = a->value->type->data.vector.len; + } else if (a->value->type->id == ZigTypeIdArray) { + len_a = a->value->type->data.array.len; + } else if (a->value->type->id == ZigTypeIdUndefined) { len_a = UINT32_MAX; } else { ir_add_error(ira, a, buf_sprintf("expected vector or array with element type '%s', found '%s'", buf_ptr(&scalar_type->name), - buf_ptr(&a->value.type->name))); + buf_ptr(&a->value->type->name))); return ira->codegen->invalid_instruction; } uint32_t len_b; - if (b->value.type->id == ZigTypeIdVector) { - len_b = b->value.type->data.vector.len; - } else if (b->value.type->id == ZigTypeIdArray) { - len_b = b->value.type->data.array.len; - } else if (b->value.type->id == ZigTypeIdUndefined) { + if (b->value->type->id == ZigTypeIdVector) { + len_b = b->value->type->data.vector.len; + } else if (b->value->type->id == ZigTypeIdArray) { + len_b = b->value->type->data.array.len; + } else if (b->value->type->id == ZigTypeIdUndefined) { len_b = UINT32_MAX; } else { ir_add_error(ira, b, buf_sprintf("expected vector or array with element type '%s', found '%s'", buf_ptr(&scalar_type->name), - buf_ptr(&b->value.type->name))); + buf_ptr(&b->value->type->name))); return ira->codegen->invalid_instruction; } @@ -22274,7 +24719,7 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s a = ir_const_undef(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); } else { a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); - if (type_is_invalid(a->value.type)) + if (type_is_invalid(a->value->type)) return ira->codegen->invalid_instruction; } @@ -22283,18 +24728,18 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s b = ir_const_undef(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); } else { b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); - if (type_is_invalid(b->value.type)) + if (type_is_invalid(b->value->type)) return ira->codegen->invalid_instruction; } - ConstExprValue *mask_val = ir_resolve_const(ira, mask, UndefOk); + ZigValue *mask_val = ir_resolve_const(ira, mask, UndefOk); if (mask_val == nullptr) return ira->codegen->invalid_instruction; expand_undef_array(ira->codegen, mask_val); for (uint32_t i = 0; i < len_mask; i += 1) { - ConstExprValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; + ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; if (mask_elem_val->special == ConstValSpecialUndef) continue; int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint); @@ -22307,12 +24752,12 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s v = (uint32_t)~v_i32; chosen_operand = b; } - if (v >= chosen_operand->value.type->data.vector.len) { + if (v >= chosen_operand->value->type->data.vector.len) { ErrorMsg *msg = ir_add_error(ira, mask, buf_sprintf("mask index '%u' has out-of-bounds selection", i)); add_error_note(ira->codegen, msg, chosen_operand->source_node, buf_sprintf("selected index '%u' out of bounds of %s", v, - buf_ptr(&chosen_operand->value.type->name))); + buf_ptr(&chosen_operand->value->type->name))); if (chosen_operand == a && v < len_a + len_b) { add_error_note(ira->codegen, msg, b->source_node, buf_create_from_str("selections from the second vector are specified with negative numbers")); @@ -22323,11 +24768,11 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type); if (instr_is_comptime(a) && instr_is_comptime(b)) { - ConstExprValue *a_val = ir_resolve_const(ira, a, UndefOk); + ZigValue *a_val = ir_resolve_const(ira, a, UndefOk); if (a_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *b_val = ir_resolve_const(ira, b, UndefOk); + ZigValue *b_val = ir_resolve_const(ira, b, UndefOk); if (b_val == nullptr) return ira->codegen->invalid_instruction; @@ -22335,24 +24780,24 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s expand_undef_array(ira->codegen, b_val); IrInstruction *result = ir_const(ira, source_instr, result_type); - result->value.data.x_array.data.s_none.elements = create_const_vals(len_mask); + result->value->data.x_array.data.s_none.elements = create_const_vals(len_mask); for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) { - ConstExprValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; - ConstExprValue *result_elem_val = &result->value.data.x_array.data.s_none.elements[i]; + ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; + ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; if (mask_elem_val->special == ConstValSpecialUndef) { result_elem_val->special = ConstValSpecialUndef; continue; } int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint); // We've already checked for and emitted compile errors for index out of bounds here. - ConstExprValue *src_elem_val = (v >= 0) ? - &a->value.data.x_array.data.s_none.elements[v] : - &b->value.data.x_array.data.s_none.elements[~v]; - copy_const_val(result_elem_val, src_elem_val, false); + ZigValue *src_elem_val = (v >= 0) ? + &a->value->data.x_array.data.s_none.elements[v] : + &b->value->data.x_array.data.s_none.elements[~v]; + copy_const_val(result_elem_val, src_elem_val); ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr); } - result->value.special = ConstValSpecialStatic; + result->value->special = ConstValSpecialStatic; return result; } @@ -22368,12 +24813,12 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s IrInstruction *expand_mask = ir_const(ira, mask, get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32)); - expand_mask->value.data.x_array.data.s_none.elements = create_const_vals(len_max); + expand_mask->value->data.x_array.data.s_none.elements = create_const_vals(len_max); uint32_t i = 0; for (; i < len_min; i += 1) - bigint_init_unsigned(&expand_mask->value.data.x_array.data.s_none.elements[i].data.x_bigint, i); + bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i); for (; i < len_max; i += 1) - bigint_init_signed(&expand_mask->value.data.x_array.data.s_none.elements[i].data.x_bigint, -1); + bigint_init_signed(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, -1); IrInstruction *undef = ir_const_undef(ira, source_instr, get_vector_type(ira->codegen, len_min, scalar_type)); @@ -22388,7 +24833,7 @@ static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *s IrInstruction *result = ir_build_shuffle_vector(&ira->new_irb, source_instr->scope, source_instr->source_node, nullptr, a, b, mask); - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -22398,15 +24843,15 @@ static IrInstruction *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrIn return ira->codegen->invalid_instruction; IrInstruction *a = instruction->a->child; - if (type_is_invalid(a->value.type)) + if (type_is_invalid(a->value->type)) return ira->codegen->invalid_instruction; IrInstruction *b = instruction->b->child; - if (type_is_invalid(b->value.type)) + if (type_is_invalid(b->value->type)) return ira->codegen->invalid_instruction; IrInstruction *mask = instruction->mask->child; - if (type_is_invalid(mask->value.type)) + if (type_is_invalid(mask->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_shuffle_vector(ira, &instruction->base, scalar_type, a, b, mask); @@ -22416,11 +24861,11 @@ static IrInstruction *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstruction Error err; IrInstruction *len = instruction->len->child; - if (type_is_invalid(len->value.type)) + if (type_is_invalid(len->value->type)) return ira->codegen->invalid_instruction; IrInstruction *scalar = instruction->scalar->child; - if (type_is_invalid(scalar->value.type)) + if (type_is_invalid(scalar->value->type)) return ira->codegen->invalid_instruction; uint64_t len_u64; @@ -22428,22 +24873,22 @@ static IrInstruction *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; uint32_t len_int = len_u64; - if ((err = ir_validate_vector_elem_type(ira, scalar, scalar->value.type))) + if ((err = ir_validate_vector_elem_type(ira, scalar, scalar->value->type))) return ira->codegen->invalid_instruction; - ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value.type); + ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value->type); if (instr_is_comptime(scalar)) { - ConstExprValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk); + ZigValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk); if (scalar_val == nullptr) return ira->codegen->invalid_instruction; if (scalar_val->special == ConstValSpecialUndef) return ir_const_undef(ira, &instruction->base, return_type); IrInstruction *result = ir_const(ira, &instruction->base, return_type); - result->value.data.x_array.data.s_none.elements = create_const_vals(len_int); + result->value->data.x_array.data.s_none.elements = create_const_vals(len_int); for (uint32_t i = 0; i < len_int; i += 1) { - copy_const_val(&result->value.data.x_array.data.s_none.elements[i], scalar_val, false); + copy_const_val(&result->value->data.x_array.data.s_none.elements[i], scalar_val); } return result; } @@ -22453,17 +24898,17 @@ static IrInstruction *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; ZigType *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_value = ir_implicit_cast(ira, value, bool_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_value)) { - ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *value = ir_resolve_const(ira, casted_value, UndefBad); if (value == nullptr) return ira->codegen->invalid_instruction; @@ -22472,7 +24917,7 @@ static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_bool_not(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_value); - result->value.type = bool_type; + result->value->type = bool_type; return result; } @@ -22480,18 +24925,18 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio Error err; IrInstruction *dest_ptr = instruction->dest_ptr->child; - if (type_is_invalid(dest_ptr->value.type)) + if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *byte_value = instruction->byte->child; - if (type_is_invalid(byte_value->value.type)) + if (type_is_invalid(byte_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *count_value = instruction->count->child; - if (type_is_invalid(count_value->value.type)) + if (type_is_invalid(count_value->value->type)) return ira->codegen->invalid_instruction; - ZigType *dest_uncasted_type = dest_ptr->value.type; + ZigType *dest_uncasted_type = dest_ptr->value->type; bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && dest_uncasted_type->data.pointer.is_volatile; @@ -22508,15 +24953,15 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio PtrLenUnknown, dest_align, 0, 0, false); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); - if (type_is_invalid(casted_dest_ptr->value.type)) + if (type_is_invalid(casted_dest_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8); - if (type_is_invalid(casted_byte->value.type)) + if (type_is_invalid(casted_byte->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); - if (type_is_invalid(casted_count->value.type)) + if (type_is_invalid(casted_count->value->type)) return ira->codegen->invalid_instruction; // TODO test this at comptime with u8 and non-u8 types @@ -22524,22 +24969,22 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio instr_is_comptime(casted_byte) && instr_is_comptime(casted_count)) { - ConstExprValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); + ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); if (dest_ptr_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk); + ZigValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk); if (byte_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); + ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); if (count_val == nullptr) return ira->codegen->invalid_instruction; - if (casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr && - casted_dest_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + if (casted_dest_ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + casted_dest_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *dest_elements; + ZigValue *dest_elements; size_t start; size_t bound_end; switch (dest_ptr_val->data.x_ptr.special) { @@ -22553,7 +24998,7 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio break; case ConstPtrSpecialBaseArray: { - ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; + ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.data.s_none.elements; start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; @@ -22584,7 +25029,7 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } for (size_t i = start; i < end; i += 1) { - copy_const_val(&dest_elements[i], byte_val, true); + copy_const_val(&dest_elements[i], byte_val); } return ir_const_void(ira, &instruction->base); @@ -22593,7 +25038,7 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio IrInstruction *result = ir_build_memset(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_dest_ptr, casted_byte, casted_count); - result->value.type = ira->codegen->builtin_types.entry_void; + result->value->type = ira->codegen->builtin_types.entry_void; return result; } @@ -22601,20 +25046,20 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio Error err; IrInstruction *dest_ptr = instruction->dest_ptr->child; - if (type_is_invalid(dest_ptr->value.type)) + if (type_is_invalid(dest_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *src_ptr = instruction->src_ptr->child; - if (type_is_invalid(src_ptr->value.type)) + if (type_is_invalid(src_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *count_value = instruction->count->child; - if (type_is_invalid(count_value->value.type)) + if (type_is_invalid(count_value->value->type)) return ira->codegen->invalid_instruction; ZigType *u8 = ira->codegen->builtin_types.entry_u8; - ZigType *dest_uncasted_type = dest_ptr->value.type; - ZigType *src_uncasted_type = src_ptr->value.type; + ZigType *dest_uncasted_type = dest_ptr->value->type; + ZigType *src_uncasted_type = src_ptr->value->type; bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && dest_uncasted_type->data.pointer.is_volatile; bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) && @@ -22643,15 +25088,15 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio PtrLenUnknown, src_align, 0, 0, false); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); - if (type_is_invalid(casted_dest_ptr->value.type)) + if (type_is_invalid(casted_dest_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const); - if (type_is_invalid(casted_src_ptr->value.type)) + if (type_is_invalid(casted_src_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); - if (type_is_invalid(casted_count->value.type)) + if (type_is_invalid(casted_count->value->type)) return ira->codegen->invalid_instruction; // TODO test this at comptime with u8 and non-u8 types @@ -22660,22 +25105,22 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio instr_is_comptime(casted_src_ptr) && instr_is_comptime(casted_count)) { - ConstExprValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); + ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); if (dest_ptr_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad); + ZigValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad); if (src_ptr_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); + ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); if (count_val == nullptr) return ira->codegen->invalid_instruction; if (dest_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { size_t count = bigint_as_usize(&count_val->data.x_bigint); - ConstExprValue *dest_elements; + ZigValue *dest_elements; size_t dest_start; size_t dest_end; switch (dest_ptr_val->data.x_ptr.special) { @@ -22689,7 +25134,7 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio break; case ConstPtrSpecialBaseArray: { - ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; + ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); dest_elements = array_val->data.x_array.data.s_none.elements; dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; @@ -22717,7 +25162,7 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio return ira->codegen->invalid_instruction; } - ConstExprValue *src_elements; + ZigValue *src_elements; size_t src_start; size_t src_end; @@ -22732,7 +25177,7 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio break; case ConstPtrSpecialBaseArray: { - ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; + ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); src_elements = array_val->data.x_array.data.s_none.elements; src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index; @@ -22763,7 +25208,7 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio // TODO check for noalias violations - this should be generalized to work for any function for (size_t i = 0; i < count; i += 1) { - copy_const_val(&dest_elements[dest_start + i], &src_elements[src_start + i], true); + copy_const_val(&dest_elements[dest_start + i], &src_elements[src_start + i]); } return ir_const_void(ira, &instruction->base); @@ -22772,90 +25217,112 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio IrInstruction *result = ir_build_memcpy(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_dest_ptr, casted_src_ptr, casted_count); - result->value.type = ira->codegen->builtin_types.entry_void; + result->value->type = ira->codegen->builtin_types.entry_void; return result; } static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSliceSrc *instruction) { IrInstruction *ptr_ptr = instruction->ptr->child; - if (type_is_invalid(ptr_ptr->value.type)) + if (type_is_invalid(ptr_ptr->value->type)) return ira->codegen->invalid_instruction; - ZigType *ptr_ptr_type = ptr_ptr->value.type; + ZigType *ptr_ptr_type = ptr_ptr->value->type; assert(ptr_ptr_type->id == ZigTypeIdPointer); ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->child; - if (type_is_invalid(start->value.type)) + if (type_is_invalid(start->value->type)) return ira->codegen->invalid_instruction; ZigType *usize = ira->codegen->builtin_types.entry_usize; IrInstruction *casted_start = ir_implicit_cast(ira, start, usize); - if (type_is_invalid(casted_start->value.type)) + if (type_is_invalid(casted_start->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end; if (instruction->end) { end = instruction->end->child; - if (type_is_invalid(end->value.type)) + if (type_is_invalid(end->value->type)) return ira->codegen->invalid_instruction; end = ir_implicit_cast(ira, end, usize); - if (type_is_invalid(end->value.type)) + if (type_is_invalid(end->value->type)) return ira->codegen->invalid_instruction; } else { end = nullptr; } - ZigType *return_type; + ZigType *non_sentinel_slice_ptr_type; + ZigType *elem_type; if (array_type->id == ZigTypeIdArray) { - bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && - ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, + elem_type = array_type->data.array.child_type; + bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic && + ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst; + non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, ptr_ptr_type->data.pointer.is_const || is_comptime_const, ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); - return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, - main_type->data.pointer.child_type, + elem_type = main_type->data.pointer.child_type; + non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + elem_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0, false); - return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); return ira->codegen->invalid_instruction; } } else { + elem_type = array_type->data.pointer.child_type; if (array_type->data.pointer.ptr_len == PtrLenC) { array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); } - return_type = get_slice_type(ira->codegen, array_type); + ZigType *maybe_sentineled_slice_ptr_type = array_type; + non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->invalid_instruction; } } } else if (is_slice(array_type)) { - ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; - return_type = get_slice_type(ira->codegen, ptr_type); + ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); + elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_instruction; } + ZigType *return_type; + ZigValue *sentinel_val = nullptr; + if (instruction->sentinel) { + IrInstruction *uncasted_sentinel = instruction->sentinel->child; + if (type_is_invalid(uncasted_sentinel->value->type)) + return ira->codegen->invalid_instruction; + IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ira->codegen->invalid_instruction; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ira->codegen->invalid_instruction; + ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + } else { + return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); + } + if (instr_is_comptime(ptr_ptr) && - value_is_comptime(&casted_start->value) && - (!end || value_is_comptime(&end->value))) + value_is_comptime(casted_start->value) && + (!end || value_is_comptime(end->value))) { - ConstExprValue *array_val; - ConstExprValue *parent_ptr; + ZigValue *array_val; + ZigValue *parent_ptr; size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; @@ -22865,18 +25332,26 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (array_type->id == ZigTypeIdPointer) { ZigType *child_array_type = array_type->data.pointer.child_type; assert(child_array_type->id == ZigTypeIdArray); - parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); + parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_instruction; - array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.source_node); - if (array_val == nullptr) - return ira->codegen->invalid_instruction; - rel_end = child_array_type->data.array.len; - abs_offset = 0; + if (parent_ptr->special == ConstValSpecialUndef) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + ptr_is_undef = true; + } else { + array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.source_node); + if (array_val == nullptr) + return ira->codegen->invalid_instruction; + + rel_end = child_array_type->data.array.len; + abs_offset = 0; + } } else { - array_val = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); + array_val = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.source_node); if (array_val == nullptr) return ira->codegen->invalid_instruction; rel_end = array_type->data.array.len; @@ -22885,7 +25360,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len == PtrLenUnknown); - parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); + parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_instruction; @@ -22933,7 +25408,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { - ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); + ZigValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.source_node); if (slice_ptr == nullptr) return ira->codegen->invalid_instruction; @@ -22942,13 +25417,13 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } - parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index]; + parent_ptr = slice_ptr->data.x_struct.fields[slice_ptr_index]; if (parent_ptr->special == ConstValSpecialUndef) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of undefined")); return ira->codegen->invalid_instruction; } - ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = slice_ptr->data.x_struct.fields[slice_len_index]; switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -22986,17 +25461,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); } - uint64_t start_scalar = bigint_as_u64(&casted_start->value.data.x_bigint); + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_instruction; + + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); if (!ptr_is_undef && start_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->invalid_instruction; } - uint64_t end_scalar; + uint64_t end_scalar = rel_end; if (end) { - end_scalar = bigint_as_u64(&end->value.data.x_bigint); - } else { - end_scalar = rel_end; + ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); + if (!end_val) + return ira->codegen->invalid_instruction; + end_scalar = bigint_as_u64(&end_val->data.x_bigint); } if (!ptr_is_undef) { if (end_scalar > rel_end) { @@ -23014,17 +25494,17 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } IrInstruction *result = ir_const(ira, &instruction->base, return_type); - ConstExprValue *out_val = &result->value; - out_val->data.x_struct.fields = create_const_vals(2); + ZigValue *out_val = result->value; + out_val->data.x_struct.fields = alloc_const_vals_ptrs(2); - ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val = out_val->data.x_struct.fields[slice_ptr_index]; if (array_val) { size_t index = abs_offset + start_scalar; bool is_const = slice_is_const(return_type); init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); if (array_type->id == ZigTypeIdArray) { - ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; + ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; } else if (is_slice(array_type)) { ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; } else if (array_type->id == ZigTypeIdPointer) { @@ -23064,7 +25544,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_panic("TODO"); } - ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; + ZigValue *len_val = out_val->data.x_struct.fields[slice_len_index]; init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); return result; @@ -23072,7 +25552,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, return_type, nullptr, true, false, true); - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) { return result_loc; } return ir_build_slice_gen(ira, &instruction->base, return_type, @@ -23082,7 +25562,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) { Error err; IrInstruction *container = instruction->container->child; - if (type_is_invalid(container->value.type)) + if (type_is_invalid(container->value->type)) return ira->codegen->invalid_instruction; ZigType *container_type = ir_resolve_type(ira, container); @@ -23138,7 +25618,7 @@ static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstr member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); return ira->codegen->invalid_instruction; } - TypeStructField *field = &container_type->data.structure.fields[member_index]; + TypeStructField *field = container_type->data.structure.fields[member_index]; return ir_const_type(ira, &instruction->base, field->type_entry); } else if (container_type->id == ZigTypeIdUnion) { @@ -23180,10 +25660,10 @@ static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstr member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); return ira->codegen->invalid_instruction; } - TypeStructField *field = &container_type->data.structure.fields[member_index]; + TypeStructField *field = container_type->data.structure.fields[member_index]; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - init_const_str_lit(ira->codegen, &result->value, field->name); + init_const_str_lit(ira->codegen, result->value, field->name); return result; } else if (container_type->id == ZigTypeIdEnum) { if (member_index >= container_type->data.enumeration.src_field_count) { @@ -23195,7 +25675,7 @@ static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstr TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - init_const_str_lit(ira->codegen, &result->value, field->name); + init_const_str_lit(ira->codegen, result->value, field->name); return result; } else if (container_type->id == ZigTypeIdUnion) { if (member_index >= container_type->data.unionation.src_field_count) { @@ -23207,7 +25687,7 @@ static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstr TypeUnionField *field = &container_type->data.unionation.fields[member_index]; IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - init_const_str_lit(ira->codegen, &result->value, field->name); + init_const_str_lit(ira->codegen, result->value, field->name); return result; } else { ir_add_error(ira, container_type_value, @@ -23247,21 +25727,21 @@ static IrInstruction *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { IrInstruction *result = ir_build_breakpoint(&ira->new_irb, instruction->base.scope, instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_void; + result->value->type = ira->codegen->builtin_types.entry_void; return result; } static IrInstruction *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) { IrInstruction *result = ir_build_return_address(&ira->new_irb, instruction->base.scope, instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_usize; + result->value->type = ira->codegen->builtin_types.entry_usize; return result; } static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) { IrInstruction *result = ir_build_frame_address(&ira->new_irb, instruction->base.scope, instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_usize; + result->value->type = ira->codegen->builtin_types.entry_usize; return result; } @@ -23277,7 +25757,7 @@ static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInst ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node); - result->value.type = ptr_frame_type; + result->value->type = ptr_frame_type; return result; } @@ -23298,12 +25778,12 @@ static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstructionFrameSizeSrc *instruction) { IrInstruction *fn = instruction->fn->child; - if (type_is_invalid(fn->value.type)) + if (type_is_invalid(fn->value->type)) return ira->codegen->invalid_instruction; - if (fn->value.type->id != ZigTypeIdFn) { + if (fn->value->type->id != ZigTypeIdFn) { ir_add_error(ira, fn, - buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value.type->name))); + buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value->type->name))); return ira->codegen->invalid_instruction; } @@ -23311,7 +25791,7 @@ static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstru IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn); - result->value.type = ira->codegen->builtin_types.entry_usize; + result->value->type = ira->codegen->builtin_types.entry_usize; return result; } @@ -23322,11 +25802,11 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct // field: []align(@alignOf(Node)) Node, // }; IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueAlignOf *lazy_align_of = allocate(1); - lazy_align_of->ira = ira; - result->value.data.x_lazy = &lazy_align_of->base; + LazyValueAlignOf *lazy_align_of = allocate(1, "LazyValueAlignOf"); + lazy_align_of->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_align_of->base; lazy_align_of->base.id = LazyValueIdAlignOf; lazy_align_of->target_type = instruction->type_value->child; @@ -23340,7 +25820,7 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr Error err; IrInstruction *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value.type)) + if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_instruction; ZigType *dest_type = ir_resolve_type(ira, type_value); @@ -23354,15 +25834,15 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr } IrInstruction *op1 = instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type); - if (type_is_invalid(casted_op1->value.type)) + if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_op2; @@ -23373,20 +25853,20 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr } else { casted_op2 = ir_implicit_cast(ira, op2, dest_type); } - if (type_is_invalid(casted_op2->value.type)) + if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_instruction; IrInstruction *result_ptr = instruction->result_ptr->child; - if (type_is_invalid(result_ptr->value.type)) + if (type_is_invalid(result_ptr->value->type)) return ira->codegen->invalid_instruction; ZigType *expected_ptr_type; - if (result_ptr->value.type->id == ZigTypeIdPointer) { + if (result_ptr->value->type->id == ZigTypeIdPointer) { uint32_t alignment; - if ((err = resolve_ptr_align(ira, result_ptr->value.type, &alignment))) + if ((err = resolve_ptr_align(ira, result_ptr->value->type, &alignment))) return ira->codegen->invalid_instruction; expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, - false, result_ptr->value.type->data.pointer.is_volatile, + false, result_ptr->value->type->data.pointer.is_volatile, PtrLenSingle, alignment, 0, 0, false); } else { @@ -23394,28 +25874,28 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr } IrInstruction *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type); - if (type_is_invalid(casted_result_ptr->value.type)) + if (type_is_invalid(casted_result_ptr->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2) && instr_is_comptime(casted_result_ptr)) { - ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_instruction; - ConstExprValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad); + ZigValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad); if (result_val == nullptr) return ira->codegen->invalid_instruction; BigInt *op1_bigint = &op1_val->data.x_bigint; BigInt *op2_bigint = &op2_val->data.x_bigint; - ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val, + ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val, casted_result_ptr->source_node); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; @@ -23451,12 +25931,12 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr IrInstruction *result = ir_build_overflow_op(&ira->new_irb, instruction->base.scope, instruction->base.source_node, instruction->op, type_value, casted_op1, casted_op2, casted_result_ptr, dest_type); - result->value.type = ira->codegen->builtin_types.entry_bool; + result->value->type = ira->codegen->builtin_types.entry_bool; return result; } static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type, - ConstExprValue *op1, ConstExprValue *op2, ConstExprValue *op3, ConstExprValue *out_val) { + ZigValue *op1, ZigValue *op2, ZigValue *op3, ZigValue *out_val) { if (float_type->id == ZigTypeIdComptimeFloat) { f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, &op3->data.x_bigfloat.value); @@ -23484,7 +25964,7 @@ static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, Z static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) { IrInstruction *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value.type)) + if (type_is_invalid(type_value->value->type)) return ira->codegen->invalid_instruction; ZigType *expr_type = ir_resolve_type(ira, type_value); @@ -23500,44 +25980,44 @@ static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructi } IrInstruction *op1 = instruction->op1->child; - if (type_is_invalid(op1->value.type)) + if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, expr_type); - if (type_is_invalid(casted_op1->value.type)) + if (type_is_invalid(casted_op1->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op2 = instruction->op2->child; - if (type_is_invalid(op2->value.type)) + if (type_is_invalid(op2->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, expr_type); - if (type_is_invalid(casted_op2->value.type)) + if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_instruction; IrInstruction *op3 = instruction->op3->child; - if (type_is_invalid(op3->value.type)) + if (type_is_invalid(op3->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type); - if (type_is_invalid(casted_op3->value.type)) + if (type_is_invalid(casted_op3->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2) && instr_is_comptime(casted_op3)) { - ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); + ZigValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); if (!op1_const) return ira->codegen->invalid_instruction; - ConstExprValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); + ZigValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); if (!op2_const) return ira->codegen->invalid_instruction; - ConstExprValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); + ZigValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); if (!op3_const) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, expr_type); - ConstExprValue *out_val = &result->value; + ZigValue *out_val = result->value; if (expr_type->id == ZigTypeIdVector) { expand_undef_array(ira->codegen, op1_const); @@ -23547,10 +26027,10 @@ static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructi expand_undef_array(ira->codegen, out_val); size_t len = expr_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { - ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; - ConstExprValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; - ConstExprValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; - ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; + ZigValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(float_operand_op1->type == float_type); assert(float_operand_op2->type == float_type); assert(float_operand_op3->type == float_type); @@ -23570,13 +26050,13 @@ static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_mul_add(&ira->new_irb, instruction->base.scope, instruction->base.source_node, type_value, casted_op1, casted_op2, casted_op3); - result->value.type = expr_type; + result->value->type = expr_type; return result; } static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) { IrInstruction *base_ptr = instruction->base_ptr->child; - if (type_is_invalid(base_ptr->value.type)) + if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *value; @@ -23586,12 +26066,12 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct value = ir_get_deref(ira, &instruction->base, base_ptr, nullptr); } - ZigType *type_entry = value->value.type; + ZigType *type_entry = value->value->type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if (type_entry->id == ZigTypeIdErrorUnion) { if (instr_is_comptime(value)) { - ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad); + ZigValue *err_union_val = ir_resolve_const(ira, value, UndefBad); if (!err_union_val) return ira->codegen->invalid_instruction; @@ -23609,7 +26089,7 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct if (!type_is_global_error_set(err_set_type) && err_set_type->data.error_set.err_count == 0) { - assert(err_set_type->data.error_set.infer_fn == nullptr); + assert(!err_set_type->data.error_set.incomplete); return ir_const_bool(ira, &instruction->base, false); } } @@ -23625,7 +26105,7 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool initializing) { - ZigType *ptr_type = base_ptr->value.type; + ZigType *ptr_type = base_ptr->value->type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); @@ -23646,20 +26126,20 @@ static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction * ptr_type->data.pointer.explicit_alignment, 0, 0, false); if (instr_is_comptime(base_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (initializing && err_union_val->special == ConstValSpecialUndef) { - ConstExprValue *vals = create_const_vals(2); - ConstExprValue *err_set_val = &vals[0]; - ConstExprValue *payload_val = &vals[1]; + ZigValue *vals = create_const_vals(2); + ZigValue *err_set_val = &vals[0]; + ZigValue *payload_val = &vals[1]; err_set_val->special = ConstValSpecialUndef; err_set_val->type = err_set_type; @@ -23681,12 +26161,12 @@ static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction * if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_unwrap_err_code(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr); - result->value.type = result_type; - result->value.special = ConstValSpecialStatic; + result->value->type = result_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } - ConstExprValue *const_val = &result->value; + ZigValue *const_val = result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; @@ -23696,7 +26176,7 @@ static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction * IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr); - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -23704,7 +26184,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { IrInstruction *base_ptr = instruction->err_union_ptr->child; - if (type_is_invalid(base_ptr->value.type)) + if (type_is_invalid(base_ptr->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_unwrap_err_code(ira, &instruction->base, base_ptr, false); } @@ -23712,7 +26192,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on, bool initializing) { - ZigType *ptr_type = base_ptr->value.type; + ZigType *ptr_type = base_ptr->value->type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); @@ -23734,18 +26214,19 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false); + if (instr_is_comptime(base_ptr)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; if (initializing && err_union_val->special == ConstValSpecialUndef) { - ConstExprValue *vals = create_const_vals(2); - ConstExprValue *err_set_val = &vals[0]; - ConstExprValue *payload_val = &vals[1]; + ZigValue *vals = create_const_vals(2); + ZigValue *err_set_val = &vals[0]; + ZigValue *payload_val = &vals[1]; err_set_val->special = ConstValSpecialStatic; err_set_val->type = type_entry->data.error_union.err_set_type; @@ -23771,14 +26252,14 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr, safety_check_on, initializing); - result->value.type = result_type; - result->value.special = ConstValSpecialStatic; + result->value->type = result_type; + result->value->special = ConstValSpecialStatic; } else { result = ir_const(ira, source_instr, result_type); } - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result->value->data.x_ptr.special = ConstPtrSpecialRef; + result->value->data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + result->value->data.x_ptr.mut = ptr_val->data.x_ptr.mut; return result; } } @@ -23786,7 +26267,7 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, base_ptr, safety_check_on, initializing); - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -23795,7 +26276,7 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, { assert(instruction->value->child); IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_unwrap_error_payload(ira, &instruction->base, value, instruction->safety_check_on, false); @@ -23806,11 +26287,11 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct assert(proto_node->type == NodeTypeFnProto); IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValueFnType *lazy_fn_type = allocate(1); - lazy_fn_type->ira = ira; - result->value.data.x_lazy = &lazy_fn_type->base; + LazyValueFnType *lazy_fn_type = allocate(1, "LazyValueFnType"); + lazy_fn_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_fn_type->base; lazy_fn_type->base.id = LazyValueIdFnType; if (proto_node->data.fn_proto.auto_err_set) { @@ -23819,6 +26300,21 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } + lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto); + if (instruction->callconv_value != nullptr) { + ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention"); + + IrInstruction *casted_value = ir_implicit_cast(ira, instruction->callconv_value, cc_enum_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_instruction; + + ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad); + if (const_value == nullptr) + return ira->codegen->invalid_instruction; + + lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag); + } + size_t param_count = proto_node->data.fn_proto.params.length; lazy_fn_type->proto_node = proto_node; lazy_fn_type->param_types = allocate(param_count); @@ -23829,9 +26325,11 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) { - if (proto_node->data.fn_proto.cc == CallingConventionC) { + const CallingConvention cc = lazy_fn_type->cc; + + if (cc == CallingConventionC) { break; - } else if (proto_node->data.fn_proto.cc == CallingConventionUnspecified) { + } else if (cc == CallingConventionUnspecified) { lazy_fn_type->is_generic = true; return result; } else { @@ -23845,7 +26343,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct } IrInstruction *param_type_value = instruction->param_types[param_index]->child; - if (type_is_invalid(param_type_value->value.type)) + if (type_is_invalid(param_type_value->value->type)) return ira->codegen->invalid_instruction; if (ir_resolve_const(ira, param_type_value, LazyOk) == nullptr) return ira->codegen->invalid_instruction; @@ -23867,7 +26365,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; return ir_const_bool(ira, &instruction->base, instr_is_comptime(value)); @@ -23877,7 +26375,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, IrInstructionCheckSwitchProngs *instruction) { IrInstruction *target_value = instruction->target_value->child; - ZigType *switch_type = target_value->value.type; + ZigType *switch_type = target_value->value->type; if (type_is_invalid(switch_type)) return ira->codegen->invalid_instruction; @@ -23889,25 +26387,31 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstruction *start_value_uncasted = range->start->child; - if (type_is_invalid(start_value_uncasted->value.type)) + if (type_is_invalid(start_value_uncasted->value->type)) return ira->codegen->invalid_instruction; IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); - if (type_is_invalid(start_value->value.type)) + if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end_value_uncasted = range->end->child; - if (type_is_invalid(end_value_uncasted->value.type)) + if (type_is_invalid(end_value_uncasted->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); - if (type_is_invalid(end_value->value.type)) + if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_instruction; + assert(start_value->value->type->id == ZigTypeIdEnum); BigInt start_index; - bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); + bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); - assert(end_value->value.type->id == ZigTypeIdEnum); + assert(end_value->value->type->id == ZigTypeIdEnum); BigInt end_index; - bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag); + bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); + + if (bigint_cmp(&start_index, &end_index) == CmpGT) { + ir_add_error(ira, start_value, + buf_sprintf("range start value is greater than the end value")); + } BigInt field_index; bigint_init_bigint(&field_index, &start_index); @@ -23930,6 +26434,10 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, } } if (!instruction->have_else_prong) { + if (switch_type->data.enumeration.layout == ContainerLayoutExtern) { + ir_add_error(ira, &instruction->base, + buf_sprintf("switch on an extern enum must have an else prong")); + } for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; @@ -23946,30 +26454,31 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length); + size_t field_prev_uses_count = ira->codegen->errors_by_index.length; + AstNode **field_prev_uses = allocate(field_prev_uses_count, "AstNode *"); for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstruction *start_value_uncasted = range->start->child; - if (type_is_invalid(start_value_uncasted->value.type)) + if (type_is_invalid(start_value_uncasted->value->type)) return ira->codegen->invalid_instruction; IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); - if (type_is_invalid(start_value->value.type)) + if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end_value_uncasted = range->end->child; - if (type_is_invalid(end_value_uncasted->value.type)) + if (type_is_invalid(end_value_uncasted->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); - if (type_is_invalid(end_value->value.type)) + if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_instruction; - ir_assert(start_value->value.type->id == ZigTypeIdErrorSet, &instruction->base); - uint32_t start_index = start_value->value.data.x_err_set->value; + ir_assert(start_value->value->type->id == ZigTypeIdErrorSet, &instruction->base); + uint32_t start_index = start_value->value->data.x_err_set->value; - ir_assert(end_value->value.type->id == ZigTypeIdErrorSet, &instruction->base); - uint32_t end_index = end_value->value.data.x_err_set->value; + ir_assert(end_value->value->type->id == ZigTypeIdErrorSet, &instruction->base); + uint32_t end_index = end_value->value->data.x_err_set->value; if (start_index != end_index) { ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors")); @@ -24003,36 +26512,42 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, } } - free(field_prev_uses); + deallocate(field_prev_uses, field_prev_uses_count, "AstNode *"); } else if (switch_type->id == ZigTypeIdInt) { RangeSet rs = {0}; for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; IrInstruction *start_value = range->start->child; - if (type_is_invalid(start_value->value.type)) + if (type_is_invalid(start_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); - if (type_is_invalid(casted_start_value->value.type)) + if (type_is_invalid(casted_start_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *end_value = range->end->child; - if (type_is_invalid(end_value->value.type)) + if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); - if (type_is_invalid(casted_end_value->value.type)) + if (type_is_invalid(casted_end_value->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); + ZigValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); if (!start_val) return ira->codegen->invalid_instruction; - ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); + ZigValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); if (!end_val) return ira->codegen->invalid_instruction; assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); + + if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { + ir_add_error(ira, start_value, + buf_sprintf("range start value is greater than the end value")); + } + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { @@ -24060,10 +26575,10 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, IrInstruction *value = range->start->child; IrInstruction *casted_value = ir_implicit_cast(ira, value, switch_type); - if (type_is_invalid(casted_value->value.type)) + if (type_is_invalid(casted_value->value->type)) return ira->codegen->invalid_instruction; - ConstExprValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); + ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); if (!const_expr_val) return ira->codegen->invalid_instruction; @@ -24096,7 +26611,7 @@ static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze * IrInstructionCheckStatementIsVoid *instruction) { IrInstruction *statement_value = instruction->statement_value->child; - ZigType *statement_type = statement_value->value.type; + ZigType *statement_type = statement_value->value->type; if (type_is_invalid(statement_type)) return ira->codegen->invalid_instruction; @@ -24109,7 +26624,7 @@ static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze * static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { IrInstruction *msg = instruction->msg->child; - if (type_is_invalid(msg->value.type)) + if (type_is_invalid(msg->value->type)) return ir_unreach_error(ira); if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) { @@ -24121,7 +26636,7 @@ static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstruction true, false, PtrLenUnknown, 0, 0, 0, false); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); - if (type_is_invalid(casted_msg->value.type)) + if (type_is_invalid(casted_msg->value->type)) return ir_unreach_error(ira); IrInstruction *new_instruction = ir_build_panic(&ira->new_irb, instruction->base.scope, @@ -24132,7 +26647,7 @@ static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstruction static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) { Error err; - ZigType *target_type = target->value.type; + ZigType *target_type = target->value->type; assert(!type_is_invalid(target_type)); ZigType *result_type; @@ -24165,7 +26680,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id); result_type = get_optional_type(ira->codegen, fn_type); } else if (is_slice(target_type)) { - ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes))) return ira->codegen->invalid_instruction; ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes); @@ -24177,7 +26692,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 } if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; @@ -24191,8 +26706,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 } IrInstruction *result = ir_const(ira, target, result_type); - copy_const_val(&result->value, val, true); - result->value.type = result_type; + copy_const_val(result->value, val); + result->value->type = result_type; return result; } @@ -24202,7 +26717,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 } else { result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, result_type, target, CastOpNoop); } - result->value.type = result_type; + result->value->type = result_type; return result; } @@ -24211,9 +26726,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ { Error err; - ZigType *src_type = ptr->value.type; + ZigType *src_type = ptr->value->type; assert(!type_is_invalid(src_type)); + if (src_type == dest_type) { + return ptr; + } + // We have a check for zero bits later so we use get_src_ptr_type to // validate src_type and dest_type. @@ -24242,10 +26761,27 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) return ira->codegen->invalid_instruction; + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + + if (type_has_bits(dest_type) && !type_has_bits(src_type)) { + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("'%s' and '%s' do not have the same in-memory representation", + buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); + add_error_note(ira->codegen, msg, ptr->source_node, + buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); + add_error_note(ira->codegen, msg, dest_type_src->source_node, + buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + if (instr_is_comptime(ptr)) { bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; - ConstExprValue *val = ir_resolve_const(ira, ptr, is_undef_allowed); + ZigValue *val = ir_resolve_const(ira, ptr, is_undef_allowed); if (!val) return ira->codegen->invalid_instruction; @@ -24261,13 +26797,16 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ } IrInstruction *result; - if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; } else { result = ir_const(ira, source_instr, dest_type); } - copy_const_val(&result->value, val, true); - result->value.type = dest_type; + copy_const_val(result->value, val); + result->value->type = dest_type; // Keep the bigger alignment, it can only help- // unless the target is zero bits. @@ -24289,29 +26828,12 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - if (type_has_bits(dest_type) && !type_has_bits(src_type)) { - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("'%s' and '%s' do not have the same in-memory representation", - buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); - add_error_note(ira->codegen, msg, ptr->source_node, - buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); - add_error_note(ira->codegen, msg, dest_type_src->source_node, - buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - } - // Keep the bigger alignment, it can only help- // unless the target is zero bits. IrInstruction *result; if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) { result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->value->type)) return ira->codegen->invalid_instruction; } else { result = casted_ptr; @@ -24326,7 +26848,7 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; IrInstruction *ptr = instruction->ptr->child; - ZigType *src_type = ptr->value.type; + ZigType *src_type = ptr->value->type; if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; @@ -24334,18 +26856,18 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct instruction->safety_check_on); } -static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) { +static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) { size_t buf_i = 0; // TODO optimize the buf case expand_undef_array(codegen, val); for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; + ZigValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; buf_write_value_bytes(codegen, &buf[buf_i], elem); buf_i += type_size(codegen, elem->type); } } -static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { +static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) { if (val->special == ConstValSpecialUndef) { expand_undef_struct(codegen, val); val->special = ConstValSpecialStatic; @@ -24356,7 +26878,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -24403,10 +26924,10 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ContainerLayoutExtern: { size_t src_field_count = val->type->data.structure.src_field_count; for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { - TypeStructField *struct_field = &val->type->data.structure.fields[field_i]; + TypeStructField *struct_field = val->type->data.structure.fields[field_i]; if (struct_field->gen_index == SIZE_MAX) continue; - ConstExprValue *field_val = &val->data.x_struct.fields[field_i]; + ZigValue *field_val = val->data.x_struct.fields[field_i]; size_t offset = struct_field->offset; buf_write_value_bytes(codegen, buf + offset, field_val); } @@ -24432,12 +26953,12 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue bigint_init_unsigned(&big_int, 0); size_t used_bits = 0; while (src_i < src_field_count) { - TypeStructField *field = &val->type->data.structure.fields[src_i]; + TypeStructField *field = val->type->data.structure.fields[src_i]; assert(field->gen_index != SIZE_MAX); if (field->gen_index != gen_i) break; uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); - buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]); + buf_write_value_bytes(codegen, child_buf, val->data.x_struct.fields[src_i]); BigInt child_val; bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, false); @@ -24481,7 +27002,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue } static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, - ConstExprValue *val, ZigType *elem_type, size_t len) + ZigValue *val, ZigType *elem_type, size_t len) { Error err; uint64_t elem_size = type_size(codegen, elem_type); @@ -24490,7 +27011,7 @@ static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNod case ConstArraySpecialNone: val->data.x_array.data.s_none.elements = create_const_vals(len); for (size_t i = 0; i < len; i++) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i]; + ZigValue *elem = &val->data.x_array.data.s_none.elements[i]; elem->special = ConstValSpecialStatic; elem->type = elem_type; if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) @@ -24505,7 +27026,7 @@ static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNod zig_unreachable(); } -static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) { +static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val) { Error err; src_assert(val->special == ConstValSpecialStatic, source_node); switch (val->type->id) { @@ -24513,7 +27034,6 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -24575,11 +27095,11 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } case ContainerLayoutExtern: { size_t src_field_count = val->type->data.structure.src_field_count; - val->data.x_struct.fields = create_const_vals(src_field_count); + val->data.x_struct.fields = alloc_const_vals_ptrs(src_field_count); for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { - ConstExprValue *field_val = &val->data.x_struct.fields[field_i]; + ZigValue *field_val = val->data.x_struct.fields[field_i]; field_val->special = ConstValSpecialStatic; - TypeStructField *struct_field = &val->type->data.structure.fields[field_i]; + TypeStructField *struct_field = val->type->data.structure.fields[field_i]; field_val->type = struct_field->type_entry; if (struct_field->gen_index == SIZE_MAX) continue; @@ -24592,7 +27112,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } case ContainerLayoutPacked: { size_t src_field_count = val->type->data.structure.src_field_count; - val->data.x_struct.fields = create_const_vals(src_field_count); + val->data.x_struct.fields = alloc_const_vals_ptrs(src_field_count); size_t gen_field_count = val->type->data.structure.gen_field_count; size_t gen_i = 0; size_t src_i = 0; @@ -24610,11 +27130,11 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou BigInt big_int; bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false); while (src_i < src_field_count) { - TypeStructField *field = &val->type->data.structure.fields[src_i]; + TypeStructField *field = val->type->data.structure.fields[src_i]; src_assert(field->gen_index != SIZE_MAX, source_node); if (field->gen_index != gen_i) break; - ConstExprValue *field_val = &val->data.x_struct.fields[src_i]; + ZigValue *field_val = val->data.x_struct.fields[src_i]; field_val->special = ConstValSpecialStatic; field_val->type = field->type_entry; uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); @@ -24668,19 +27188,26 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ { Error err; - ZigType *src_type = value->value.type; + ZigType *src_type = value->value->type; ir_assert(get_codegen_ptr_type(src_type) == nullptr, source_instr); ir_assert(type_can_bit_cast(src_type), source_instr); ir_assert(get_codegen_ptr_type(dest_type) == nullptr, source_instr); ir_assert(type_can_bit_cast(dest_type), source_instr); + if (dest_type->id == ZigTypeIdEnum) { + ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("cannot cast a value of type '%s'", buf_ptr(&dest_type->name))); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("use @intToEnum for type coercion")); + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); uint64_t src_size_bytes = type_size(ira->codegen, src_type); if (dest_size_bytes != src_size_bytes) { @@ -24702,14 +27229,14 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ } if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, source_instr, dest_type); uint8_t *buf = allocate_nonzero(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); - if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value))) + if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) return ira->codegen->invalid_instruction; return result; } @@ -24720,15 +27247,17 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *ptr_type) { + Error err; + ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr); ir_assert(type_has_bits(ptr_type), source_instr); IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); - if (type_is_invalid(casted_int->value.type)) + if (type_is_invalid(casted_int->value->type)) return ira->codegen->invalid_instruction; if (instr_is_comptime(casted_int)) { - ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad); + ZigValue *val = ir_resolve_const(ira, casted_int, UndefBad); if (!val) return ira->codegen->invalid_instruction; @@ -24739,16 +27268,27 @@ static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } + uint32_t align_bytes; + if ((err = resolve_ptr_align(ira, ptr_type, &align_bytes))) + return ira->codegen->invalid_instruction; + + if (addr != 0 && addr % align_bytes != 0) { + ir_add_error(ira, source_instr, + buf_sprintf("pointer type '%s' requires aligned address", + buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_const(ira, source_instr, ptr_type); - result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value.data.x_ptr.data.hard_coded_addr.addr = addr; + result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + result->value->data.x_ptr.data.hard_coded_addr.addr = addr; return result; } IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, nullptr, casted_int); - result->value.type = ptr_type; + result->value->type = ptr_type; return result; } @@ -24765,17 +27305,18 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; } - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + bool has_bits; + if ((err = type_has_bits2(ira->codegen, dest_type, &has_bits))) return ira->codegen->invalid_instruction; - if (!type_has_bits(dest_type)) { + + if (!has_bits) { ir_add_error(ira, dest_type_value, buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type); @@ -24785,7 +27326,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { IrInstruction *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base, instruction->tld); - if (type_is_invalid(ref_instruction->value.type)) { + if (type_is_invalid(ref_instruction->value->type)) { return ira->codegen->invalid_instruction; } @@ -24797,52 +27338,63 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, } static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstructionPtrToInt *instruction) { + Error err; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; ZigType *usize = ira->codegen->builtin_types.entry_usize; // We check size explicitly so we can use get_src_ptr_type here. - if (get_src_ptr_type(target->value.type) == nullptr) { + if (get_src_ptr_type(target->value->type) == nullptr) { ir_add_error(ira, target, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name))); return ira->codegen->invalid_instruction; } - if (!type_has_bits(target->value.type)) { + bool has_bits; + if ((err = type_has_bits2(ira->codegen, target->value->type, &has_bits))) + return ira->codegen->invalid_instruction; + + if (!has_bits) { ir_add_error(ira, target, buf_sprintf("pointer to size 0 type has no address")); return ira->codegen->invalid_instruction; } if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_const(ira, &instruction->base, usize); - bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); - result->value.type = usize; + bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); + result->value->type = usize; return result; } } IrInstruction *result = ir_build_ptr_to_int(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); - result->value.type = usize; + result->value->type = usize; return result; } static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); - result->value.special = ConstValSpecialLazy; + result->value->special = ConstValSpecialLazy; - LazyValuePtrType *lazy_ptr_type = allocate(1); - lazy_ptr_type->ira = ira; - result->value.data.x_lazy = &lazy_ptr_type->base; + LazyValuePtrType *lazy_ptr_type = allocate(1, "LazyValuePtrType"); + lazy_ptr_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_ptr_type->base; lazy_ptr_type->base.id = LazyValueIdPtrType; + if (instruction->sentinel != nullptr) { + lazy_ptr_type->sentinel = instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_instruction; + } + lazy_ptr_type->elem_type = instruction->child_type->child; if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; @@ -24865,15 +27417,15 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) { IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; ZigType *elem_type = nullptr; - if (is_slice(target->value.type)) { - ZigType *slice_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + if (is_slice(target->value->type)) { + ZigType *slice_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; elem_type = slice_ptr_type->data.pointer.child_type; - } else if (target->value.type->id == ZigTypeIdPointer) { - elem_type = target->value.type->data.pointer.child_type; + } else if (target->value->type->id == ZigTypeIdPointer) { + elem_type = target->value->type->data.pointer.child_type; } uint32_t align_bytes; @@ -24882,7 +27434,7 @@ static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; IrInstruction *result = ir_align_cast(ira, target, align_bytes, true); - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->value->type)) return ira->codegen->invalid_instruction; return result; @@ -24947,6 +27499,10 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->invalid_instruction; + if (fn_type->id == ZigTypeIdBoundFn) { + fn_type = fn_type->data.bound_fn.fn_type; + arg_index += 1; + } if (fn_type->id != ZigTypeIdFn) { ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); return ira->codegen->invalid_instruction; @@ -24954,6 +27510,10 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { + if (instruction->allow_var) { + // TODO remove this with var args + return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_var); + } ir_add_error(ira, arg_index_inst, buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments", arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); @@ -24965,10 +27525,14 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct // Args are only unresolved if our function is generic. ir_assert(fn_type->data.fn.is_generic, &instruction->base); - ir_add_error(ira, arg_index_inst, - buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", - arg_index, buf_ptr(&fn_type->name))); - return ira->codegen->invalid_instruction; + if (instruction->allow_var) { + return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_var); + } else { + ir_add_error(ira, arg_index_inst, + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); + return ira->codegen->invalid_instruction; + } } return ir_const_type(ira, &instruction->base, result_type); } @@ -25021,9 +27585,37 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } + } else if (operand_type->id == ZigTypeIdEnum) { + ZigType *int_type = operand_type->data.enumeration.tag_int_type; + if (int_type->data.integral.bit_count < 8) { + ir_add_error(ira, op, + buf_sprintf("expected enum tag type 8 bits or larger, found %" PRIu32 "-bit tag type", + int_type->data.integral.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + if (int_type->data.integral.bit_count > max_atomic_bits) { + ir_add_error(ira, op, + buf_sprintf("expected %" PRIu32 "-bit enum tag type or smaller, found %" PRIu32 "-bit tag type", + max_atomic_bits, int_type->data.integral.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } + if (!is_power_of_2(int_type->data.integral.bit_count)) { + ir_add_error(ira, op, + buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (operand_type->id == ZigTypeIdFloat) { + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + if (operand_type->data.floating.bit_count > max_atomic_bits) { + ir_add_error(ira, op, + buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", + max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } } else if (get_codegen_ptr_type(operand_type) == nullptr) { ir_add_error(ira, op, - buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name))); + buf_sprintf("expected integer, float, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } @@ -25036,13 +27628,13 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; IrInstruction *ptr_inst = instruction->ptr->child; - if (type_is_invalid(ptr_inst->value.type)) + if (type_is_invalid(ptr_inst->value->type)) return ira->codegen->invalid_instruction; // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); - if (type_is_invalid(casted_ptr->value.type)) + if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_instruction; AtomicRmwOp op; @@ -25054,12 +27646,22 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru } } + if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { + ir_add_error(ira, instruction->op, + buf_sprintf("@atomicRmw on enum only works with .Xchg")); + return ira->codegen->invalid_instruction; + } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { + ir_add_error(ira, instruction->op, + buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub")); + return ira->codegen->invalid_instruction; + } + IrInstruction *operand = instruction->operand->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type); - if (type_is_invalid(casted_operand->value.type)) + if (type_is_invalid(casted_operand->value->type)) return ira->codegen->invalid_instruction; AtomicOrder ordering; @@ -25075,7 +27677,7 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru } } - if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) + if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { zig_panic("TODO compile-time execution of atomicRmw"); } @@ -25083,7 +27685,7 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru IrInstruction *result = ir_build_atomic_rmw(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, nullptr, casted_operand, nullptr, op, ordering); - result->value.type = operand_type; + result->value->type = operand_type; return result; } @@ -25093,12 +27695,12 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; IrInstruction *ptr_inst = instruction->ptr->child; - if (type_is_invalid(ptr_inst->value.type)) + if (type_is_invalid(ptr_inst->value->type)) return ira->codegen->invalid_instruction; ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); - if (type_is_invalid(casted_ptr->value.type)) + if (type_is_invalid(casted_ptr->value->type)) return ira->codegen->invalid_instruction; AtomicOrder ordering; @@ -25118,30 +27720,80 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr if (instr_is_comptime(casted_ptr)) { IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr, nullptr); - ir_assert(result->value.type != nullptr, &instruction->base); + ir_assert(result->value->type != nullptr, &instruction->base); return result; } IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering); - result->value.type = operand_type; + result->value->type = operand_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstructionAtomicStore *instruction) { + ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *ptr_inst = instruction->ptr->child; + if (type_is_invalid(ptr_inst->value->type)) + return ira->codegen->invalid_instruction; + + ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value->type)) + return ira->codegen->invalid_instruction; + + IrInstruction *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_instruction; + + IrInstruction *casted_value = ir_implicit_cast(ira, value, operand_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_instruction; + + + AtomicOrder ordering; + if (instruction->ordering == nullptr) { + ordering = instruction->resolved_ordering; + } else { + if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) + return ira->codegen->invalid_instruction; + } + + if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { + ir_assert(instruction->ordering != nullptr, &instruction->base); + ir_add_error(ira, instruction->ordering, + buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); + return ira->codegen->invalid_instruction; + } + + if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { + IrInstruction *result = ir_analyze_store_ptr(ira, &instruction->base, casted_ptr, value, false); + result->value->type = ira->codegen->builtin_types.entry_void; + return result; + } + + IrInstruction *result = ir_build_atomic_store(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_ptr, casted_value, nullptr, ordering); + result->value->type = ira->codegen->builtin_types.entry_void; return result; } static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, instruction->base.source_node); - result->value.type = ira->codegen->builtin_types.entry_void; + result->value->type = ira->codegen->builtin_types.entry_void; return result; } -static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type, - ConstExprValue *op, ConstExprValue *out_val) { +static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInstruction *source_instr, BuiltinFnId fop, ZigType *float_type, + ZigValue *op, ZigValue *out_val) +{ assert(ira && source_instr && float_type && out_val && op); assert(float_type->id == ZigTypeIdFloat || float_type->id == ZigTypeIdComptimeFloat); - BuiltinFnId fop = source_instr->op; unsigned bits; switch (float_type->id) { @@ -25162,24 +27814,49 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, out_val->data.x_f16 = f16_sqrt(op->data.x_f16); break; case BuiltinFnIdSin: + out_val->data.x_f16 = zig_double_to_f16(sin(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdCos: + out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdExp: + out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdExp2: - case BuiltinFnIdLn: + out_val->data.x_f16 = zig_double_to_f16(exp2(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdLog: + out_val->data.x_f16 = zig_double_to_f16(log(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdLog10: + out_val->data.x_f16 = zig_double_to_f16(log10(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdLog2: + out_val->data.x_f16 = zig_double_to_f16(log2(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdFabs: + out_val->data.x_f16 = zig_double_to_f16(fabs(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdFloor: + out_val->data.x_f16 = zig_double_to_f16(floor(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdCeil: + out_val->data.x_f16 = zig_double_to_f16(ceil(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdTrunc: + out_val->data.x_f16 = zig_double_to_f16(trunc(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdNearbyInt: + out_val->data.x_f16 = zig_double_to_f16(nearbyint(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdRound: - zig_panic("unimplemented f16 builtin"); + out_val->data.x_f16 = zig_double_to_f16(round(zig_f16_to_double(op->data.x_f16))); + break; default: zig_unreachable(); }; break; - }; + } case 32: { switch (fop) { case BuiltinFnIdSqrt: @@ -25197,7 +27874,7 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, case BuiltinFnIdExp2: out_val->data.x_f32 = exp2f(op->data.x_f32); break; - case BuiltinFnIdLn: + case BuiltinFnIdLog: out_val->data.x_f32 = logf(op->data.x_f32); break; case BuiltinFnIdLog10: @@ -25228,7 +27905,7 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, zig_unreachable(); }; break; - }; + } case 64: { switch (fop) { case BuiltinFnIdSqrt: @@ -25246,7 +27923,7 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, case BuiltinFnIdExp2: out_val->data.x_f64 = exp2(op->data.x_f64); break; - case BuiltinFnIdLn: + case BuiltinFnIdLog: out_val->data.x_f64 = log(op->data.x_f64); break; case BuiltinFnIdLog10: @@ -25277,7 +27954,11 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, zig_unreachable(); } break; - }; + } + case 80: + return ir_add_error(ira, source_instr, + buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", + float_op_to_name(fop), buf_ptr(&float_type->name))); case 128: { float128_t *out, *in; if (float_type->id == ZigTypeIdComptimeFloat) { @@ -25296,7 +27977,7 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, case BuiltinFnIdCos: case BuiltinFnIdExp: case BuiltinFnIdExp2: - case BuiltinFnIdLn: + case BuiltinFnIdLog: case BuiltinFnIdLog10: case BuiltinFnIdLog2: case BuiltinFnIdFabs: @@ -25304,85 +27985,83 @@ static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, case BuiltinFnIdCeil: case BuiltinFnIdTrunc: case BuiltinFnIdRound: - zig_panic("unimplemented f128 builtin"); + return ir_add_error(ira, source_instr, + buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", + float_op_to_name(fop), buf_ptr(&float_type->name))); default: zig_unreachable(); } break; - }; + } default: zig_unreachable(); } + out_val->special = ConstValSpecialStatic; + return nullptr; } static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) { - IrInstruction *type = instruction->type->child; - if (type_is_invalid(type->value.type)) + IrInstruction *operand = instruction->operand->child; + ZigType *operand_type = operand->value->type; + if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; - ZigType *expr_type = ir_resolve_type(ira, type); - if (type_is_invalid(expr_type)) - return ira->codegen->invalid_instruction; + // This instruction accepts floats and vectors of floats. + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; - // Only allow float types, and vectors of floats. - ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; - if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) { - ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name))); + if (scalar_type->id != ZigTypeIdFloat && scalar_type->id != ZigTypeIdComptimeFloat) { + ir_add_error(ira, operand, + buf_sprintf("expected float type, found '%s'", buf_ptr(&scalar_type->name))); return ira->codegen->invalid_instruction; } - IrInstruction *op1 = instruction->op1->child; - if (type_is_invalid(op1->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type); - if (type_is_invalid(casted_op1->value.type)) - return ira->codegen->invalid_instruction; - - if (instr_is_comptime(casted_op1)) { - // Our comptime 16-bit and 128-bit support is quite limited. - if ((float_type->id == ZigTypeIdComptimeFloat || - float_type->data.floating.bit_count == 16 || - float_type->data.floating.bit_count == 128) && - instruction->op != BuiltinFnIdSqrt) { - ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name))); + if (instr_is_comptime(operand)) { + ZigValue *operand_val = ir_resolve_const(ira, operand, UndefOk); + if (operand_val == nullptr) return ira->codegen->invalid_instruction; - } + if (operand_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base, operand_type); - ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); - if (!op1_const) - return ira->codegen->invalid_instruction; + IrInstruction *result = ir_const(ira, &instruction->base, operand_type); + ZigValue *out_val = result->value; - IrInstruction *result = ir_const(ira, &instruction->base, expr_type); - ConstExprValue *out_val = &result->value; - - if (expr_type->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, op1_const); + if (operand_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, operand_val); out_val->special = ConstValSpecialUndef; expand_undef_array(ira->codegen, out_val); - size_t len = expr_type->data.vector.len; + size_t len = operand_type->data.vector.len; for (size_t i = 0; i < len; i += 1) { - ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; - ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; - assert(float_operand_op1->type == float_type); - assert(float_out_val->type == float_type); - ir_eval_float_op(ira, instruction, float_type, - op1_const, float_out_val); - float_out_val->type = float_type; + ZigValue *elem_operand = &operand_val->data.x_array.data.s_none.elements[i]; + ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; + ir_assert(elem_operand->type == scalar_type, &instruction->base); + ir_assert(float_out_val->type == scalar_type, &instruction->base); + ErrorMsg *msg = ir_eval_float_op(ira, &instruction->base, instruction->fn_id, scalar_type, + elem_operand, float_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, instruction->base.source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_instruction; + } + float_out_val->type = scalar_type; } - out_val->type = expr_type; + out_val->type = operand_type; out_val->special = ConstValSpecialStatic; } else { - ir_eval_float_op(ira, instruction, float_type, op1_const, out_val); + if (ir_eval_float_op(ira, &instruction->base, instruction->fn_id, scalar_type, + operand_val, out_val) != nullptr) + { + return ira->codegen->invalid_instruction; + } } return result; } - ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base); + ir_assert(scalar_type->id == ZigTypeIdFloat, &instruction->base); IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, casted_op1, instruction->op); - result->value.type = expr_type; + instruction->base.source_node, operand, instruction->fn_id); + result->value->type = operand_type; return result; } @@ -25394,16 +28073,16 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; IrInstruction *uncasted_op = instruction->op->child; - if (type_is_invalid(uncasted_op->value.type)) + if (type_is_invalid(uncasted_op->value->type)) return ira->codegen->invalid_instruction; uint32_t vector_len; // UINT32_MAX means not a vector - if (uncasted_op->value.type->id == ZigTypeIdArray && - is_valid_vector_elem_type(uncasted_op->value.type->data.array.child_type)) + if (uncasted_op->value->type->id == ZigTypeIdArray && + is_valid_vector_elem_type(uncasted_op->value->type->data.array.child_type)) { - vector_len = uncasted_op->value.type->data.array.len; - } else if (uncasted_op->value.type->id == ZigTypeIdVector) { - vector_len = uncasted_op->value.type->data.vector.len; + vector_len = uncasted_op->value->type->data.array.len; + } else if (uncasted_op->value->type->id == ZigTypeIdVector) { + vector_len = uncasted_op->value->type->data.vector.len; } else { vector_len = UINT32_MAX; } @@ -25412,7 +28091,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type; IrInstruction *op = ir_implicit_cast(ira, uncasted_op, op_type); - if (type_is_invalid(op->value.type)) + if (type_is_invalid(op->value->type)) return ira->codegen->invalid_instruction; if (int_type->data.integral.bit_count == 8 || int_type->data.integral.bit_count == 0) @@ -25426,7 +28105,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction } if (instr_is_comptime(op)) { - ConstExprValue *val = ir_resolve_const(ira, op, UndefOk); + ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_instruction; if (val->special == ConstValSpecialUndef) @@ -25437,28 +28116,28 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction uint8_t *buf = allocate_nonzero(buf_size); if (is_vector) { expand_undef_array(ira->codegen, val); - result->value.data.x_array.data.s_none.elements = create_const_vals(op_type->data.vector.len); + result->value->data.x_array.data.s_none.elements = create_const_vals(op_type->data.vector.len); for (unsigned i = 0; i < op_type->data.vector.len; i += 1) { - ConstExprValue *op_elem_val = &val->data.x_array.data.s_none.elements[i]; + ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i]; if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.source_node, op_elem_val, UndefOk))) { return ira->codegen->invalid_instruction; } - ConstExprValue *result_elem_val = &result->value.data.x_array.data.s_none.elements[i]; + ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; result_elem_val->type = int_type; result_elem_val->special = op_elem_val->special; if (op_elem_val->special == ConstValSpecialUndef) continue; bigint_write_twos_complement(&op_elem_val->data.x_bigint, buf, int_type->data.integral.bit_count, true); - bigint_read_twos_complement(&result->value.data.x_array.data.s_none.elements[i].data.x_bigint, + bigint_read_twos_complement(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, buf, int_type->data.integral.bit_count, false, int_type->data.integral.is_signed); } } else { bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); - bigint_read_twos_complement(&result->value.data.x_bigint, buf, int_type->data.integral.bit_count, false, + bigint_read_twos_complement(&result->value->data.x_bigint, buf, int_type->data.integral.bit_count, false, int_type->data.integral.is_signed); } free(buf); @@ -25467,7 +28146,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, op); - result->value.type = op_type; + result->value->type = op_type; return result; } @@ -25477,17 +28156,17 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value.type)) + if (type_is_invalid(op->value->type)) return ira->codegen->invalid_instruction; if (int_type->data.integral.bit_count == 0) { IrInstruction *result = ir_const(ira, &instruction->base, int_type); - bigint_init_unsigned(&result->value.data.x_bigint, 0); + bigint_init_unsigned(&result->value->data.x_bigint, 0); return result; } if (instr_is_comptime(op)) { - ConstExprValue *val = ir_resolve_const(ira, op, UndefOk); + ZigValue *val = ir_resolve_const(ira, op, UndefOk); if (val == nullptr) return ira->codegen->invalid_instruction; if (val->special == ConstValSpecialUndef) @@ -25511,7 +28190,7 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr } } - bigint_read_twos_complement(&result->value.data.x_bigint, + bigint_read_twos_complement(&result->value->data.x_bigint, result_buf, int_type->data.integral.bit_count, ira->codegen->is_big_endian, @@ -25522,14 +28201,14 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr IrInstruction *result = ir_build_bit_reverse(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, op); - result->value.type = int_type; + result->value->type = int_type; return result; } static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_enum_to_int(ira, &instruction->base, target); @@ -25554,11 +28233,11 @@ static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstr ZigType *tag_type = dest_type->data.enumeration.tag_int_type; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (type_is_invalid(target->value->type)) return ira->codegen->invalid_instruction; IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type); - if (type_is_invalid(casted_target->value.type)) + if (type_is_invalid(casted_target->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type); @@ -25625,33 +28304,33 @@ static IrInstruction *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, Ir static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstructionEndExpr *instruction) { IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + if (type_is_invalid(value->value->type)) return ira->codegen->invalid_instruction; bool was_written = instruction->result_loc->written; IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - value->value.type, value, false, false, true); + value->value->type, value, false, false, true); if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value.type)) + if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_instruction; - if (result_loc->value.type->id == ZigTypeIdUnreachable) + if (result_loc->value->type->id == ZigTypeIdUnreachable) return result_loc; if (!was_written || instruction->result_loc->id == ResultLocIdPeer) { IrInstruction *store_ptr = ir_analyze_store_ptr(ira, &instruction->base, result_loc, value, instruction->result_loc->allow_write_through_const); - if (type_is_invalid(store_ptr->value.type)) { + if (type_is_invalid(store_ptr->value->type)) { return ira->codegen->invalid_instruction; } } - if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer && + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer && instruction->result_loc->id != ResultLocIdPeer) { if (instr_is_comptime(value)) { - result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + result_loc->value->data.x_ptr.mut = ConstPtrMutComptimeConst; } else { - result_loc->value.special = ConstValSpecialRuntime; + result_loc->value->special = ConstValSpecialRuntime; } } } @@ -25659,14 +28338,30 @@ static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstruct return ir_const_void(ira, &instruction->base); } -static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstructionBitCastSrc *instruction) { +static IrInstruction *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstructionImplicitCast *instruction) { IrInstruction *operand = instruction->operand->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return operand; IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, - &instruction->result_loc_bit_cast->base, operand->value.type, operand, false, false, true); - if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) + &instruction->result_loc_cast->base, operand->value->type, operand, false, false, true); + if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) + return result_loc; + + ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + return ir_implicit_cast2(ira, &instruction->base, operand, dest_type); +} + +static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstructionBitCastSrc *instruction) { + IrInstruction *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return operand; + + IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, + &instruction->result_loc_bit_cast->base, operand->value->type, operand, false, false, true); + if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) return result_loc; if (instruction->result_loc_bit_cast->parent->gen_instruction != nullptr) { @@ -25694,11 +28389,11 @@ static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *i return ira->codegen->invalid_instruction; IrInstruction *field_result_loc = instruction->field_result_loc->child; - if (type_is_invalid(field_result_loc->value.type)) + if (type_is_invalid(field_result_loc->value->type)) return ira->codegen->invalid_instruction; IrInstruction *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value.type)) + if (type_is_invalid(result_loc->value->type)) return ira->codegen->invalid_instruction; return ir_analyze_union_init(ira, &instruction->base, instruction->base.source_node, @@ -25715,7 +28410,7 @@ static IrInstruction *ir_analyze_instruction_suspend_finish(IrAnalyze *ira, IrInstructionSuspendFinish *instruction) { IrInstruction *begin_base = instruction->begin->base.child; - if (type_is_invalid(begin_base->value.type)) + if (type_is_invalid(begin_base->value->type)) return ira->codegen->invalid_instruction; ir_assert(begin_base->id == IrInstructionIdSuspendBegin, &instruction->base); IrInstructionSuspendBegin *begin = reinterpret_cast(begin_base); @@ -25733,44 +28428,44 @@ static IrInstruction *ir_analyze_instruction_suspend_finish(IrAnalyze *ira, static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *frame_ptr, ZigFn **target_fn) { - if (type_is_invalid(frame_ptr->value.type)) + if (type_is_invalid(frame_ptr->value->type)) return ira->codegen->invalid_instruction; *target_fn = nullptr; ZigType *result_type; IrInstruction *frame; - if (frame_ptr->value.type->id == ZigTypeIdPointer && - frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && - frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + if (frame_ptr->value->type->id == ZigTypeIdPointer && + frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && + frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { - ZigFn *func = frame_ptr->value.type->data.pointer.child_type->data.frame.fn; + ZigFn *func = frame_ptr->value->type->data.pointer.child_type->data.frame.fn; result_type = func->type_entry->data.fn.fn_type_id.return_type; *target_fn = func; frame = frame_ptr; } else { frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr); - if (frame->value.type->id == ZigTypeIdPointer && - frame->value.type->data.pointer.ptr_len == PtrLenSingle && - frame->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + if (frame->value->type->id == ZigTypeIdPointer && + frame->value->type->data.pointer.ptr_len == PtrLenSingle && + frame->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { - ZigFn *func = frame->value.type->data.pointer.child_type->data.frame.fn; + ZigFn *func = frame->value->type->data.pointer.child_type->data.frame.fn; result_type = func->type_entry->data.fn.fn_type_id.return_type; *target_fn = func; - } else if (frame->value.type->id != ZigTypeIdAnyFrame || - frame->value.type->data.any_frame.result_type == nullptr) + } else if (frame->value->type->id != ZigTypeIdAnyFrame || + frame->value->type->data.any_frame.result_type == nullptr) { ir_add_error(ira, source_instr, - buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value.type->name))); + buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value->type->name))); return ira->codegen->invalid_instruction; } else { - result_type = frame->value.type->data.any_frame.result_type; + result_type = frame->value->type->data.any_frame.result_type; } } ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); - if (type_is_invalid(casted_frame->value.type)) + if (type_is_invalid(casted_frame->value->type)) return ira->codegen->invalid_instruction; return casted_frame; @@ -25778,14 +28473,14 @@ static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwaitSrc *instruction) { IrInstruction *operand = instruction->frame->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_instruction; ZigFn *target_fn; IrInstruction *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base, operand, &target_fn); - if (type_is_invalid(frame->value.type)) + if (type_is_invalid(frame->value->type)) return ira->codegen->invalid_instruction; - ZigType *result_type = frame->value.type->data.any_frame.result_type; + ZigType *result_type = frame->value->type->data.any_frame.result_type; ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); ir_assert(fn_entry != nullptr, &instruction->base); @@ -25805,7 +28500,7 @@ static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstruction if (type_has_bits(result_type)) { result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, result_type, nullptr, true, true, true); - if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) + if (result_loc != nullptr && (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc))) return result_loc; } else { result_loc = nullptr; @@ -25819,13 +28514,13 @@ static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstructionResume *instruction) { IrInstruction *frame_ptr = instruction->frame->child; - if (type_is_invalid(frame_ptr->value.type)) + if (type_is_invalid(frame_ptr->value->type)) return ira->codegen->invalid_instruction; IrInstruction *frame; - if (frame_ptr->value.type->id == ZigTypeIdPointer && - frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && - frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + if (frame_ptr->value->type->id == ZigTypeIdPointer && + frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && + frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) { frame = frame_ptr; } else { @@ -25834,7 +28529,7 @@ static IrInstruction *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstructio ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); - if (type_is_invalid(casted_frame->value.type)) + if (type_is_invalid(casted_frame->value->type)) return ira->codegen->invalid_instruction; return ir_build_resume(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_frame); @@ -25845,10 +28540,10 @@ static IrInstruction *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstr return ir_const_void(ira, &instruction->base); IrInstruction *operand = instruction->operand->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_instruction; - if (!type_has_bits(operand->value.type)) + if (!type_has_bits(operand->value->type)) return ir_const_void(ira, &instruction->base); ir_assert(instruction->spill_id == SpillIdRetErrCode, &instruction->base); @@ -25861,10 +28556,10 @@ static IrInstruction *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstructionSpillEnd *instruction) { IrInstruction *operand = instruction->begin->operand->child; - if (type_is_invalid(operand->value.type)) + if (type_is_invalid(operand->value->type)) return ira->codegen->invalid_instruction; - if (ir_should_inline(ira->new_irb.exec, instruction->base.scope) || !type_has_bits(operand->value.type)) + if (ir_should_inline(ira->new_irb.exec, instruction->base.scope) || !type_has_bits(operand->value->type)) return operand; ir_assert(instruction->begin->base.child->id == IrInstructionIdSpillBegin, &instruction->base); @@ -25872,7 +28567,7 @@ static IrInstruction *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstruc IrInstruction *result = ir_build_spill_end(&ira->new_irb, instruction->base.scope, instruction->base.source_node, begin); - result->value.type = operand->value.type; + result->value->type = operand->value->type; return result; } @@ -25906,6 +28601,9 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction case IrInstructionIdFrameSizeGen: case IrInstructionIdAwaitGen: case IrInstructionIdSplatGen: + case IrInstructionIdVectorExtractElem: + case IrInstructionIdVectorStoreElem: + case IrInstructionIdAsmGen: zig_unreachable(); case IrInstructionIdReturn: @@ -25916,6 +28614,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); + case IrInstructionIdMergeErrSets: + return ir_analyze_instruction_merge_err_sets(ira, (IrInstructionMergeErrSets *)instruction); case IrInstructionIdDeclVarSrc: return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: @@ -25930,6 +28630,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); case IrInstructionIdCallSrc: return ir_analyze_instruction_call(ira, (IrInstructionCallSrc *)instruction); + case IrInstructionIdCallSrcArgs: + return ir_analyze_instruction_call_args(ira, (IrInstructionCallSrcArgs *)instruction); + case IrInstructionIdCallExtra: + return ir_analyze_instruction_call_extra(ira, (IrInstructionCallExtra *)instruction); case IrInstructionIdBr: return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); case IrInstructionIdCondBr: @@ -25950,10 +28654,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_any_frame_type(ira, (IrInstructionAnyFrameType *)instruction); case IrInstructionIdSliceType: return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction); - case IrInstructionIdGlobalAsm: - return ir_analyze_instruction_global_asm(ira, (IrInstructionGlobalAsm *)instruction); - case IrInstructionIdAsm: - return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction); + case IrInstructionIdAsmSrc: + return ir_analyze_instruction_asm(ira, (IrInstructionAsmSrc *)instruction); case IrInstructionIdArrayType: return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction); case IrInstructionIdSizeOf: @@ -26138,6 +28840,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); case IrInstructionIdAtomicLoad: return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); + case IrInstructionIdAtomicStore: + return ir_analyze_instruction_atomic_store(ira, (IrInstructionAtomicStore *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); case IrInstructionIdAddImplicitReturnType: @@ -26192,7 +28896,8 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ assert(old_exec->first_err_trace_msg == nullptr); assert(expected_type == nullptr || !type_is_invalid(expected_type)); - IrAnalyze *ira = allocate(1); + IrAnalyze *ira = allocate(1, "IrAnalyze"); + ira->ref_count = 1; old_exec->analysis = ira; ira->codegen = codegen; @@ -26205,7 +28910,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ ira->new_irb.codegen = codegen; ira->new_irb.exec = new_exec; - ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count); + ZigValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count); ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count); for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) { ira->exec_context.mem_slot_list.items[i] = &vals[i]; @@ -26228,14 +28933,35 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ } if (ira->codegen->verbose_ir) { - fprintf(stderr, "analyze #%zu\n", old_instruction->debug_id); + fprintf(stderr, "~ "); + old_instruction->src(); + fprintf(stderr, "~ "); + ir_print_instruction(codegen, stderr, old_instruction, 0, IrPassSrc); + bool want_break = false; + if (ira->break_debug_id == old_instruction->debug_id) { + want_break = true; + } else if (old_instruction->source_node != nullptr) { + for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) { + if (dbg_ir_breakpoints_buf[i].line == old_instruction->source_node->line + 1 && + buf_ends_with_str(old_instruction->source_node->owner->data.structure.root_struct->path, + dbg_ir_breakpoints_buf[i].src_file)) + { + want_break = true; + } + } + } + if (want_break) BREAKPOINT; } IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction); if (new_instruction != nullptr) { - ir_assert(new_instruction->value.type != nullptr || new_instruction->value.type != nullptr, old_instruction); + ir_assert(new_instruction->value->type != nullptr || new_instruction->value->type != nullptr, old_instruction); old_instruction->child = new_instruction; - if (type_is_invalid(new_instruction->value.type)) { + if (type_is_invalid(new_instruction->value->type)) { + if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> (invalid)"); + } + if (new_exec->first_err_trace_msg != nullptr) { ira->codegen->trace_err = new_exec->first_err_trace_msg; } else { @@ -26249,16 +28975,28 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ old_instruction->source_node, buf_create_from_str("referenced here")); } return ira->codegen->builtin_types.entry_invalid; + } else if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> "); + if (instr_is_unreachable(new_instruction)) { + fprintf(stderr, "(noreturn)\n"); + } else { + ir_print_instruction(codegen, stderr, new_instruction, 0, IrPassGen); + } } // unreachable instructions do their own control flow. - if (new_instruction->value.type->id == ZigTypeIdUnreachable) + if (new_instruction->value->type->id == ZigTypeIdUnreachable) continue; + } else { + if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> (null"); + } } ira->instruction_index += 1; } + ZigType *res_type; if (new_exec->first_err_trace_msg != nullptr) { codegen->trace_err = new_exec->first_err_trace_msg; if (codegen->trace_err != nullptr && new_exec->source_node != nullptr && @@ -26268,13 +29006,18 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ codegen->trace_err = add_error_note(codegen, codegen->trace_err, new_exec->source_node, buf_create_from_str("referenced here")); } - return ira->codegen->builtin_types.entry_invalid; + res_type = ira->codegen->builtin_types.entry_invalid; } else if (ira->src_implicit_return_type_list.length == 0) { - return codegen->builtin_types.entry_unreachable; + res_type = codegen->builtin_types.entry_unreachable; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, + res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } + + // It is now safe to free Pass 1 IR instructions. + ira_deref(ira); + + return res_type; } bool ir_has_side_effects(IrInstruction *instruction) { @@ -26287,7 +29030,10 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdDeclVarSrc: case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: + case IrInstructionIdVectorStoreElem: + case IrInstructionIdCallExtra: case IrInstructionIdCallSrc: + case IrInstructionIdCallSrcArgs: case IrInstructionIdCallGen: case IrInstructionIdReturn: case IrInstructionIdUnreachable: @@ -26317,12 +29063,12 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSaveErrRetAddr: case IrInstructionIdAddImplicitReturnType: case IrInstructionIdAtomicRmw: + case IrInstructionIdAtomicStore: case IrInstructionIdCmpxchgGen: case IrInstructionIdCmpxchgSrc: case IrInstructionIdAssertZero: case IrInstructionIdAssertNonNull: case IrInstructionIdResizeSlice: - case IrInstructionIdGlobalAsm: case IrInstructionIdUndeclaredIdent: case IrInstructionIdEndExpr: case IrInstructionIdPtrOfArrayToSlice: @@ -26341,6 +29087,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: + case IrInstructionIdMergeErrSets: case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdCast: @@ -26438,11 +29185,18 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAllocaSrc: case IrInstructionIdAllocaGen: case IrInstructionIdSpillEnd: + case IrInstructionIdVectorExtractElem: return false; - case IrInstructionIdAsm: + case IrInstructionIdAsmSrc: { - IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; + IrInstructionAsmSrc *asm_instruction = (IrInstructionAsmSrc *)instruction; + return asm_instruction->has_side_effects; + } + + case IrInstructionIdAsmGen: + { + IrInstructionAsmGen *asm_instruction = (IrInstructionAsmGen *)instruction; return asm_instruction->has_side_effects; } case IrInstructionIdUnwrapErrPayload: @@ -26473,7 +29227,7 @@ static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, La AstNode *proto_node = lazy_fn_type->proto_node; FnTypeId fn_type_id = {0}; - init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); + init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->cc, proto_node->data.fn_proto.params.length); for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index); @@ -26518,9 +29272,10 @@ static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, La break; } if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - if ((err = type_resolve(ira->codegen, param_type, ResolveStatusZeroBitsKnown))) + bool has_bits; + if ((err = type_has_bits2(ira->codegen, param_type, &has_bits))) return nullptr; - if (!type_has_bits(param_type)) { + if (!has_bits) { ir_add_error(ira, param_type_inst, buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); @@ -26547,7 +29302,7 @@ static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, La return get_fn_type(ira->codegen, &fn_type_id); } -static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { +static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { Error err; if (val->special != ConstValSpecialLazy) return ErrorNone; @@ -26558,8 +29313,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_align_of->ira; - if (lazy_align_of->target_type->value.special == ConstValSpecialStatic) { - switch (lazy_align_of->target_type->value.data.x_type->id) { + if (lazy_align_of->target_type->value->special == ConstValSpecialStatic) { + switch (lazy_align_of->target_type->value->data.x_type->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: @@ -26570,12 +29325,11 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdVoid: case ZigTypeIdOpaque: ir_add_error(ira, lazy_align_of->target_type, buf_sprintf("no align available for type '%s'", - buf_ptr(&lazy_align_of->target_type->value.data.x_type->name))); + buf_ptr(&lazy_align_of->target_type->value->data.x_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdBool: case ZigTypeIdInt: @@ -26597,8 +29351,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { } uint32_t align_in_bytes; - if ((err = type_val_resolve_abi_align(ira->codegen, &lazy_align_of->target_type->value, - &align_in_bytes))) + if ((err = type_val_resolve_abi_align(ira->codegen, source_node, + lazy_align_of->target_type->value, &align_in_bytes))) { return err; } @@ -26606,25 +29360,26 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSizeOf: { LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_size_of->ira; - if (lazy_size_of->target_type->value.special == ConstValSpecialStatic) { - switch (lazy_size_of->target_type->value.data.x_type->id) { + if (lazy_size_of->target_type->value->special == ConstValSpecialStatic) { + switch (lazy_size_of->target_type->value->data.x_type->id) { case ZigTypeIdInvalid: // handled above zig_unreachable(); case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error(ira, lazy_size_of->target_type, buf_sprintf("no size available for type '%s'", - buf_ptr(&lazy_size_of->target_type->value.data.x_type->name))); + buf_ptr(&lazy_size_of->target_type->value->data.x_type->name))); return ErrorSemanticAnalyzeFail; case ZigTypeIdMetaType: case ZigTypeIdEnumLiteral: @@ -26652,7 +29407,7 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { size_t abi_size; size_t size_in_bits; - if ((err = type_val_resolve_abi_size(ira->codegen, source_node, &lazy_size_of->target_type->value, + if ((err = type_val_resolve_abi_size(ira->codegen, source_node, lazy_size_of->target_type->value, &abi_size, &size_in_bits))) { return err; @@ -26661,6 +29416,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, abi_size); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSliceType: { @@ -26671,6 +29428,20 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; + ZigValue *sentinel_val; + if (lazy_slice_type->sentinel != nullptr) { + if (type_is_invalid(lazy_slice_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + uint32_t align_bytes = 0; if (lazy_slice_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes)) @@ -26683,7 +29454,6 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error(ira, lazy_slice_type->elem_type, buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name))); @@ -26716,12 +29486,17 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; if ((err = type_resolve(ira->codegen, elem_type, needed_status))) return err; - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, - lazy_slice_type->is_const, lazy_slice_type->is_volatile, PtrLenUnknown, align_bytes, - 0, 0, lazy_slice_type->is_allowzero); + ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + lazy_slice_type->is_const, lazy_slice_type->is_volatile, + PtrLenUnknown, + align_bytes, + 0, 0, lazy_slice_type->is_allowzero, + VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdPtrType: { @@ -26732,6 +29507,20 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { if (type_is_invalid(elem_type)) return ErrorSemanticAnalyzeFail; + ZigValue *sentinel_val; + if (lazy_ptr_type->sentinel != nullptr) { + if (type_is_invalid(lazy_ptr_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + uint32_t align_bytes = 0; if (lazy_ptr_type->align_inst != nullptr) { if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes)) @@ -26774,11 +29563,78 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { } bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC; assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_pointer_to_type_extra(ira->codegen, elem_type, + val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes, lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, - allow_zero); + allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_array_type->ira; + + ZigType *elem_type = ir_resolve_type(ira, lazy_array_type->elem_type); + if (type_is_invalid(elem_type)) + return ErrorSemanticAnalyzeFail; + + switch (elem_type->id) { + case ZigTypeIdInvalid: // handled above + zig_unreachable(); + case ZigTypeIdUnreachable: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + ir_add_error(ira, lazy_array_type->elem_type, + buf_sprintf("array of type '%s' not allowed", + buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + break; + } + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + ZigValue *sentinel_val = nullptr; + if (lazy_array_type->sentinel != nullptr) { + if (type_is_invalid(lazy_array_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } + + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdOptType: { @@ -26801,16 +29657,21 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_optional_type(ira->codegen, payload_type); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdFnType: { LazyValueFnType *lazy_fn_type = reinterpret_cast(val->data.x_lazy); - ZigType *fn_type = ir_resolve_lazy_fn_type(lazy_fn_type->ira, source_node, lazy_fn_type); + IrAnalyze *ira = lazy_fn_type->ira; + ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type); if (fn_type == nullptr) return ErrorSemanticAnalyzeFail; val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = fn_type; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdErrUnionType: { @@ -26839,13 +29700,15 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } } zig_unreachable(); } -Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ConstExprValue *val) { +Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val) { Error err; if ((err = ir_resolve_lazy_raw(source_node, val))) { if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { @@ -26860,3 +29723,43 @@ Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ConstExprValue *va } return ErrorNone; } + +void IrInstruction::src() { + IrInstruction *inst = this; + if (inst->source_node != nullptr) { + inst->source_node->src(); + } else { + fprintf(stderr, "(null source node)\n"); + } +} + +void IrInstruction::dump() { + IrInstruction *inst = this; + inst->src(); + IrPass pass = (inst->child == nullptr) ? IrPassGen : IrPassSrc; + if (inst->scope == nullptr) { + fprintf(stderr, "(null scope)\n"); + } else { + ir_print_instruction(inst->scope->codegen, stderr, inst, 0, pass); + if (pass == IrPassSrc) { + fprintf(stderr, "-> "); + ir_print_instruction(inst->scope->codegen, stderr, inst->child, 0, IrPassGen); + } + } +} + +void IrAnalyze::dump() { + ir_print(this->codegen, stderr, this->new_irb.exec, 0, IrPassGen); + if (this->new_irb.current_basic_block != nullptr) { + fprintf(stderr, "Current basic block:\n"); + ir_print_basic_block(this->codegen, stderr, this->new_irb.current_basic_block, 1, IrPassGen); + } +} + +void dbg_ir_break(const char *src_file, uint32_t line) { + dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line}; + dbg_ir_breakpoints_count += 1; +} +void dbg_ir_clear(void) { + dbg_ir_breakpoints_count = 0; +} diff --git a/src/ir.hpp b/src/ir.hpp index d3ec33aef..003bf4897 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -18,12 +18,12 @@ enum IrPass { bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable); bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); -ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef); -Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ConstExprValue *val); +Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val); ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, ZigType *expected_type, AstNode *expected_type_source_node); @@ -31,8 +31,12 @@ ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_ bool ir_has_side_effects(IrInstruction *instruction); struct IrAnalyze; -ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, +ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, AstNode *source_node); -const char *float_op_to_name(BuiltinFnId op, bool llvm_name); +const char *float_op_to_name(BuiltinFnId op); + +// for debugging purposes +void dbg_ir_break(const char *src_file, uint32_t line); +void dbg_ir_clear(void); #endif diff --git a/src/ir_print.cpp b/src/ir_print.cpp index aae65d50a..90ab410e4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -38,8 +38,8 @@ struct IrPrint { static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction); -static const char* ir_instruction_type_str(IrInstruction* instruction) { - switch (instruction->id) { +const char* ir_instruction_type_str(IrInstructionId id) { + switch (id) { case IrInstructionIdInvalid: return "Invalid"; case IrInstructionIdShuffleVector: @@ -70,12 +70,16 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "UnOp"; case IrInstructionIdBinOp: return "BinOp"; + case IrInstructionIdMergeErrSets: + return "MergeErrSets"; case IrInstructionIdLoadPtr: return "LoadPtr"; case IrInstructionIdLoadPtrGen: return "LoadPtrGen"; case IrInstructionIdStorePtr: return "StorePtr"; + case IrInstructionIdVectorStoreElem: + return "VectorStoreElem"; case IrInstructionIdFieldPtr: return "FieldPtr"; case IrInstructionIdStructFieldPtr: @@ -88,8 +92,12 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "VarPtr"; case IrInstructionIdReturnPtr: return "ReturnPtr"; + case IrInstructionIdCallExtra: + return "CallExtra"; case IrInstructionIdCallSrc: return "CallSrc"; + case IrInstructionIdCallSrcArgs: + return "CallSrcArgs"; case IrInstructionIdCallGen: return "CallGen"; case IrInstructionIdConst: @@ -120,10 +128,10 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "AnyFrameType"; case IrInstructionIdSliceType: return "SliceType"; - case IrInstructionIdGlobalAsm: - return "GlobalAsm"; - case IrInstructionIdAsm: - return "Asm"; + case IrInstructionIdAsmSrc: + return "AsmSrc"; + case IrInstructionIdAsmGen: + return "AsmGen"; case IrInstructionIdSizeOf: return "SizeOf"; case IrInstructionIdTestNonNull: @@ -320,6 +328,8 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "AtomicRmw"; case IrInstructionIdAtomicLoad: return "AtomicLoad"; + case IrInstructionIdAtomicStore: + return "AtomicStore"; case IrInstructionIdSaveErrRetAddr: return "SaveErrRetAddr"; case IrInstructionIdAddImplicitReturnType: @@ -368,6 +378,8 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { return "SpillBegin"; case IrInstructionIdSpillEnd: return "SpillEnd"; + case IrInstructionIdVectorExtractElem: + return "VectorExtractElem"; } zig_unreachable(); } @@ -381,14 +393,14 @@ static void ir_print_indent(IrPrint *irp) { static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trailing) { ir_print_indent(irp); const char mark = trailing ? ':' : '#'; - const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)"; + const char *type_name = instruction->value->type ? buf_ptr(&instruction->value->type->name) : "(unknown)"; const char *ref_count = ir_has_side_effects(instruction) ? - "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count)); - fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id, - ir_instruction_type_str(instruction), type_name, ref_count); + "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->ref_count)); + fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->debug_id, + ir_instruction_type_str(instruction->id), type_name, ref_count); } -static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { +static void ir_print_const_value(IrPrint *irp, ZigValue *const_val) { Buf buf = BUF_INIT; buf_resize(&buf, 0); render_const_value(irp->codegen, &buf, const_val); @@ -396,7 +408,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { } static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { - fprintf(irp->f, "#%" ZIG_PRI_usize "", instruction->debug_id); + fprintf(irp->f, "#%" PRIu32 "", instruction->debug_id); if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) { irp->printed.put(instruction, 0); irp->pending.append(instruction); @@ -409,8 +421,8 @@ static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) return; } - if (instruction->value.special != ConstValSpecialRuntime) { - ir_print_const_value(irp, &instruction->value); + if (instruction->value->special != ConstValSpecialRuntime) { + ir_print_const_value(irp, instruction->value); } else { ir_print_var_instruction(irp, instruction); } @@ -430,7 +442,7 @@ static void ir_print_return(IrPrint *irp, IrInstructionReturn *instruction) { } static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { - ir_print_const_value(irp, &const_instruction->base.value); + ir_print_const_value(irp, const_instruction->base.value); } static const char *ir_bin_op_id_str(IrBinOp op_id) { @@ -497,8 +509,6 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) { return "++"; case IrBinOpArrayMult: return "**"; - case IrBinOpMergeErrorSets: - return "||"; } zig_unreachable(); } @@ -535,6 +545,15 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction } } +static void ir_print_merge_err_sets(IrPrint *irp, IrInstructionMergeErrSets *instruction) { + ir_print_other_instruction(irp, instruction->op1); + fprintf(irp->f, " || "); + ir_print_other_instruction(irp, instruction->op2); + if (instruction->type_name != nullptr) { + fprintf(irp->f, " // name=%s", buf_ptr(instruction->type_name)); + } +} + static void ir_print_decl_var_src(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) { const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; const char *name = decl_var_instruction->var->name; @@ -588,6 +607,12 @@ static void ir_print_result_loc_bit_cast(IrPrint *irp, ResultLocBitCast *result_ fprintf(irp->f, ")"); } +static void ir_print_result_loc_cast(IrPrint *irp, ResultLocCast *result_loc_cast) { + fprintf(irp->f, "cast(ty="); + ir_print_other_instruction(irp, result_loc_cast->base.source_instruction); + fprintf(irp->f, ")"); +} + static void ir_print_result_loc(IrPrint *irp, ResultLoc *result_loc) { switch (result_loc->id) { case ResultLocIdInvalid: @@ -606,6 +631,8 @@ static void ir_print_result_loc(IrPrint *irp, ResultLoc *result_loc) { return ir_print_result_loc_peer(irp, (ResultLocPeer *)result_loc); case ResultLocIdBitCast: return ir_print_result_loc_bit_cast(irp, (ResultLocBitCast *)result_loc); + case ResultLocIdCast: + return ir_print_result_loc_cast(irp, (ResultLocCast *)result_loc); case ResultLocIdPeerParent: fprintf(irp->f, "peer_parent"); return; @@ -613,15 +640,57 @@ static void ir_print_result_loc(IrPrint *irp, ResultLoc *result_loc) { zig_unreachable(); } +static void ir_print_call_extra(IrPrint *irp, IrInstructionCallExtra *instruction) { + fprintf(irp->f, "opts="); + ir_print_other_instruction(irp, instruction->options); + fprintf(irp->f, ", fn="); + ir_print_other_instruction(irp, instruction->fn_ref); + fprintf(irp->f, ", args="); + ir_print_other_instruction(irp, instruction->args); + fprintf(irp->f, ", result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_call_src_args(IrPrint *irp, IrInstructionCallSrcArgs *instruction) { + fprintf(irp->f, "opts="); + ir_print_other_instruction(irp, instruction->options); + fprintf(irp->f, ", fn="); + ir_print_other_instruction(irp, instruction->fn_ref); + fprintf(irp->f, ", args=("); + for (size_t i = 0; i < instruction->args_len; i += 1) { + IrInstruction *arg = instruction->args_ptr[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, arg); + } + fprintf(irp->f, "), result="); + ir_print_result_loc(irp, instruction->result_loc); +} + static void ir_print_call_src(IrPrint *irp, IrInstructionCallSrc *call_instruction) { switch (call_instruction->modifier) { case CallModifierNone: break; + case CallModifierNoAsync: + fprintf(irp->f, "noasync "); + break; case CallModifierAsync: fprintf(irp->f, "async "); break; - case CallModifierNoAsync: - fprintf(irp->f, "noasync "); + case CallModifierNeverTail: + fprintf(irp->f, "notail "); + break; + case CallModifierNeverInline: + fprintf(irp->f, "noinline "); + break; + case CallModifierAlwaysTail: + fprintf(irp->f, "tail "); + break; + case CallModifierAlwaysInline: + fprintf(irp->f, "inline "); + break; + case CallModifierCompileTime: + fprintf(irp->f, "comptime "); break; case CallModifierBuiltin: zig_unreachable(); @@ -647,11 +716,26 @@ static void ir_print_call_gen(IrPrint *irp, IrInstructionCallGen *call_instructi switch (call_instruction->modifier) { case CallModifierNone: break; + case CallModifierNoAsync: + fprintf(irp->f, "noasync "); + break; case CallModifierAsync: fprintf(irp->f, "async "); break; - case CallModifierNoAsync: - fprintf(irp->f, "noasync "); + case CallModifierNeverTail: + fprintf(irp->f, "notail "); + break; + case CallModifierNeverInline: + fprintf(irp->f, "noinline "); + break; + case CallModifierAlwaysTail: + fprintf(irp->f, "tail "); + break; + case CallModifierAlwaysInline: + fprintf(irp->f, "inline "); + break; + case CallModifierCompileTime: + fprintf(irp->f, "comptime "); break; case CallModifierBuiltin: zig_unreachable(); @@ -710,7 +794,6 @@ static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) { } static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) { - ir_print_other_instruction(irp, instruction->container_type); fprintf(irp->f, "{"); if (instruction->item_count > 50) { fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count); @@ -722,11 +805,11 @@ static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerIni ir_print_other_instruction(irp, result_loc); } } - fprintf(irp->f, "}"); + fprintf(irp->f, "}result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) { - ir_print_other_instruction(irp, instruction->container_type); fprintf(irp->f, "{"); for (size_t i = 0; i < instruction->field_count; i += 1) { IrInstructionContainerInitFieldsField *field = &instruction->fields[i]; @@ -734,7 +817,8 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name)); ir_print_other_instruction(irp, field->result_loc); } - fprintf(irp->f, "} // container init"); + fprintf(irp->f, "}result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) { @@ -779,8 +863,17 @@ static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) ir_print_other_instruction(irp, instruction->value); } +static void ir_print_vector_store_elem(IrPrint *irp, IrInstructionVectorStoreElem *instruction) { + fprintf(irp->f, "vector_ptr="); + ir_print_var_instruction(irp, instruction->vector_ptr); + fprintf(irp->f, ",index="); + ir_print_var_instruction(irp, instruction->index); + fprintf(irp->f, ",value="); + ir_print_other_instruction(irp, instruction->value); +} + static void ir_print_typeof(IrPrint *irp, IrInstructionTypeOf *instruction) { - fprintf(irp->f, "@typeOf("); + fprintf(irp->f, "@TypeOf("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); } @@ -837,6 +930,10 @@ static void ir_print_set_float_mode(IrPrint *irp, IrInstructionSetFloatMode *ins static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) { fprintf(irp->f, "["); ir_print_other_instruction(irp, instruction->size); + if (instruction->sentinel != nullptr) { + fprintf(irp->f, ":"); + ir_print_other_instruction(irp, instruction->sentinel); + } fprintf(irp->f, "]"); ir_print_other_instruction(irp, instruction->child_type); } @@ -856,11 +953,50 @@ static void ir_print_any_frame_type(IrPrint *irp, IrInstructionAnyFrameType *ins } } -static void ir_print_global_asm(IrPrint *irp, IrInstructionGlobalAsm *instruction) { - fprintf(irp->f, "asm(\"%s\")", buf_ptr(instruction->asm_code)); +static void ir_print_asm_src(IrPrint *irp, IrInstructionAsmSrc *instruction) { + assert(instruction->base.source_node->type == NodeTypeAsmExpr); + AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr; + const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; + fprintf(irp->f, "asm%s (", volatile_kw); + ir_print_other_instruction(irp, instruction->asm_template); + + for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { + AsmOutput *asm_output = asm_expr->output_list.at(i); + if (i != 0) fprintf(irp->f, ", "); + + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_output->asm_symbolic_name), + buf_ptr(asm_output->constraint)); + if (asm_output->return_type) { + fprintf(irp->f, "-> "); + ir_print_other_instruction(irp, instruction->output_types[i]); + } else { + fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name)); + } + fprintf(irp->f, ")"); + } + + fprintf(irp->f, " : "); + for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { + AsmInput *asm_input = asm_expr->input_list.at(i); + + if (i != 0) fprintf(irp->f, ", "); + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_input->asm_symbolic_name), + buf_ptr(asm_input->constraint)); + ir_print_other_instruction(irp, instruction->input_list[i]); + fprintf(irp->f, ")"); + } + fprintf(irp->f, " : "); + for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) { + Buf *reg_name = asm_expr->clobber_list.at(i); + if (i != 0) fprintf(irp->f, ", "); + fprintf(irp->f, "\"%s\"", buf_ptr(reg_name)); + } + fprintf(irp->f, ")"); } -static void ir_print_asm(IrPrint *irp, IrInstructionAsm *instruction) { +static void ir_print_asm_gen(IrPrint *irp, IrInstructionAsmGen *instruction) { assert(instruction->base.source_node->type == NodeTypeAsmExpr); AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr; const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; @@ -1462,6 +1598,13 @@ static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruc fprintf(irp->f, ")"); } +static void ir_print_implicit_cast(IrPrint *irp, IrInstructionImplicitCast *instruction) { + fprintf(irp->f, "@implicitCast("); + ir_print_other_instruction(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_result_loc(irp, &instruction->result_loc_cast->base); +} + static void ir_print_bit_cast_src(IrPrint *irp, IrInstructionBitCastSrc *instruction) { fprintf(irp->f, "@bitCast("); ir_print_other_instruction(irp, instruction->operand); @@ -1717,14 +1860,6 @@ static void ir_print_align_cast(IrPrint *irp, IrInstructionAlignCast *instructio fprintf(irp->f, ")"); } -static void ir_print_implicit_cast(IrPrint *irp, IrInstructionImplicitCast *instruction) { - fprintf(irp->f, "@implicitCast("); - ir_print_other_instruction(irp, instruction->dest_type); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); -} - static void ir_print_resolve_result(IrPrint *irp, IrInstructionResolveResult *instruction) { fprintf(irp->f, "ResolveResult("); ir_print_result_loc(irp, instruction->result_loc); @@ -1762,21 +1897,11 @@ static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionTagType *instructi } static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { - if (instruction->linkage == nullptr) { - fprintf(irp->f, "@export("); - ir_print_other_instruction(irp, instruction->name); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); - } else { - fprintf(irp->f, "@exportWithLinkage("); - ir_print_other_instruction(irp, instruction->name); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->linkage); - fprintf(irp->f, ")"); - } + fprintf(irp->f, "@export("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->options); + fprintf(irp->f, ")"); } static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { @@ -1842,6 +1967,27 @@ static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruct fprintf(irp->f, ")"); } +static void ir_print_atomic_store(IrPrint *irp, IrInstructionAtomicStore *instruction) { + fprintf(irp->f, "@atomicStore("); + if (instruction->operand_type != nullptr) { + ir_print_other_instruction(irp, instruction->operand_type); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ","); + if (instruction->ordering != nullptr) { + ir_print_other_instruction(irp, instruction->ordering); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ")"); +} + + static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) { fprintf(irp->f, "@saveErrRetAddr()"); } @@ -1853,15 +1999,8 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl } static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) { - - fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false)); - if (instruction->type != nullptr) { - ir_print_other_instruction(irp, instruction->type); - } else { - fprintf(irp->f, "null"); - } - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->op1); + fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); + ir_print_other_instruction(irp, instruction->operand); fprintf(irp->f, ")"); } @@ -1960,6 +2099,14 @@ static void ir_print_spill_end(IrPrint *irp, IrInstructionSpillEnd *instruction) fprintf(irp->f, ")"); } +static void ir_print_vector_extract_elem(IrPrint *irp, IrInstructionVectorExtractElem *instruction) { + fprintf(irp->f, "@vectorExtractElem("); + ir_print_other_instruction(irp, instruction->vector); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->index); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool trailing) { ir_print_prefix(irp, instruction, trailing); switch (instruction->id) { @@ -1974,15 +2121,24 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdBinOp: ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); break; + case IrInstructionIdMergeErrSets: + ir_print_merge_err_sets(irp, (IrInstructionMergeErrSets *)instruction); + break; case IrInstructionIdDeclVarSrc: ir_print_decl_var_src(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); break; + case IrInstructionIdCallExtra: + ir_print_call_extra(irp, (IrInstructionCallExtra *)instruction); + break; case IrInstructionIdCallSrc: ir_print_call_src(irp, (IrInstructionCallSrc *)instruction); break; + case IrInstructionIdCallSrcArgs: + ir_print_call_src_args(irp, (IrInstructionCallSrcArgs *)instruction); + break; case IrInstructionIdCallGen: ir_print_call_gen(irp, (IrInstructionCallGen *)instruction); break; @@ -2025,6 +2181,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdStorePtr: ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); break; + case IrInstructionIdVectorStoreElem: + ir_print_vector_store_elem(irp, (IrInstructionVectorStoreElem *)instruction); + break; case IrInstructionIdTypeOf: ir_print_typeof(irp, (IrInstructionTypeOf *)instruction); break; @@ -2055,11 +2214,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdAnyFrameType: ir_print_any_frame_type(irp, (IrInstructionAnyFrameType *)instruction); break; - case IrInstructionIdGlobalAsm: - ir_print_global_asm(irp, (IrInstructionGlobalAsm *)instruction); + case IrInstructionIdAsmSrc: + ir_print_asm_src(irp, (IrInstructionAsmSrc *)instruction); break; - case IrInstructionIdAsm: - ir_print_asm(irp, (IrInstructionAsm *)instruction); + case IrInstructionIdAsmGen: + ir_print_asm_gen(irp, (IrInstructionAsmGen *)instruction); break; case IrInstructionIdSizeOf: ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); @@ -2388,6 +2547,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdAtomicLoad: ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); break; + case IrInstructionIdAtomicStore: + ir_print_atomic_store(irp, (IrInstructionAtomicStore *)instruction); + break; case IrInstructionIdEnumToInt: ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); break; @@ -2454,10 +2616,44 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool case IrInstructionIdSpillEnd: ir_print_spill_end(irp, (IrInstructionSpillEnd *)instruction); break; + case IrInstructionIdVectorExtractElem: + ir_print_vector_extract_elem(irp, (IrInstructionVectorExtractElem *)instruction); + break; } fprintf(irp->f, "\n"); } +static void irp_print_basic_block(IrPrint *irp, IrBasicBlock *current_block) { + fprintf(irp->f, "%s_%" ZIG_PRI_usize ":\n", current_block->name_hint, current_block->debug_id); + for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { + IrInstruction *instruction = current_block->instruction_list.at(instr_i); + if (irp->pass != IrPassSrc) { + irp->printed.put(instruction, 0); + irp->pending.clear(); + } + ir_print_instruction(irp, instruction, false); + for (size_t j = 0; j < irp->pending.length; ++j) + ir_print_instruction(irp, irp->pending.at(j), true); + } +} + +void ir_print_basic_block(CodeGen *codegen, FILE *f, IrBasicBlock *bb, int indent_size, IrPass pass) { + IrPrint ir_print = {}; + ir_print.pass = pass; + ir_print.codegen = codegen; + ir_print.f = f; + ir_print.indent = indent_size; + ir_print.indent_size = indent_size; + ir_print.printed = {}; + ir_print.printed.init(64); + ir_print.pending = {}; + + irp_print_basic_block(&ir_print, bb); + + ir_print.pending.deinit(); + ir_print.printed.deinit(); +} + void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass) { IrPrint ir_print = {}; IrPrint *irp = &ir_print; @@ -2471,18 +2667,7 @@ void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_si irp->pending = {}; for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { - IrBasicBlock *current_block = executable->basic_block_list.at(bb_i); - fprintf(irp->f, "%s_%" ZIG_PRI_usize ":\n", current_block->name_hint, current_block->debug_id); - for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { - IrInstruction *instruction = current_block->instruction_list.at(instr_i); - if (irp->pass != IrPassSrc) { - irp->printed.put(instruction, 0); - irp->pending.clear(); - } - ir_print_instruction(irp, instruction, false); - for (size_t j = 0; j < irp->pending.length; ++j) - ir_print_instruction(irp, irp->pending.at(j), true); - } + irp_print_basic_block(irp, executable->basic_block_list.at(bb_i)); } irp->pending.deinit(); @@ -2503,3 +2688,18 @@ void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, ir_print_instruction(irp, instruction, false); } + +void ir_print_const_expr(CodeGen *codegen, FILE *f, ZigValue *value, int indent_size, IrPass pass) { + IrPrint ir_print = {}; + IrPrint *irp = &ir_print; + irp->pass = pass; + irp->codegen = codegen; + irp->f = f; + irp->indent = indent_size; + irp->indent_size = indent_size; + irp->printed = {}; + irp->printed.init(4); + irp->pending = {}; + + ir_print_const_value(irp, value); +} diff --git a/src/ir_print.hpp b/src/ir_print.hpp index 0960af4e6..1292779ac 100644 --- a/src/ir_print.hpp +++ b/src/ir_print.hpp @@ -14,5 +14,9 @@ void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass); void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass); +void ir_print_const_expr(CodeGen *codegen, FILE *f, ZigValue *value, int indent_size, IrPass pass); +void ir_print_basic_block(CodeGen *codegen, FILE *f, IrBasicBlock *bb, int indent_size, IrPass pass); + +const char* ir_instruction_type_str(IrInstructionId id); #endif diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 135941dc4..2adc1cb69 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -320,7 +320,7 @@ Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirnam #undef CC_EXE -#if defined(ZIG_OS_WINDOWS) || defined(ZIG_OS_LINUX) +#if defined(ZIG_OS_WINDOWS) || defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY) static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool verbose) { return zig_libc_cc_print_file_name("crt1.o", &self->crt_dir, true, verbose); } @@ -389,7 +389,7 @@ static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, Zi } Buf search_path = BUF_INIT; buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); - buf_append_str(&search_path, "\\..\\..\\include"); + buf_append_str(&search_path, "..\\..\\include"); Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path)); bool exists; @@ -413,6 +413,7 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) { "# The directory that contains `stdlib.h`.\n" "# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`\n" "include_dir=%s\n" + "\n" "# The system-specific include directory. May be the same as `include_dir`.\n" "# On Windows it's the directory that includes `vcruntime.h`.\n" "# On POSIX it's the directory that includes `sys/errno.h`.\n" @@ -435,8 +436,7 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) { "# The directory that contains `kernel32.lib`.\n" "# Only needed when targeting MSVC on Windows.\n" "kernel32_lib_dir=%s\n" - "\n" - , + "\n", buf_ptr(&self->include_dir), buf_ptr(&self->sys_include_dir), buf_ptr(&self->crt_dir), @@ -489,7 +489,7 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { return err; #if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) buf_init_from_str(&self->crt_dir, "/usr/lib"); -#elif defined(ZIG_OS_LINUX) +#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY) if ((err = zig_libc_find_native_crt_dir_posix(self, verbose))) return err; #endif diff --git a/src/link.cpp b/src/link.cpp index cf473b8cd..61a5ad566 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -552,28 +552,41 @@ static const char *mingwex_arm64_src[] = { struct MinGWDef { const char *name; - const char *path; bool always_link; }; static const MinGWDef mingw_def_list[] = { - {"msvcrt", "lib-common" OS_SEP "msvcrt.def.in", true}, - {"setupapi", "libarm32" OS_SEP "setupapi.def", false}, - {"setupapi", "libarm64" OS_SEP "setupapi.def", false}, - {"setupapi", "lib32" OS_SEP "setupapi.def", false}, - {"setupapi", "lib64" OS_SEP "setupapi.def", false}, - {"winmm", "lib-common" OS_SEP "winmm.def", false}, - {"gdi32", "lib-common" OS_SEP "gdi32.def", false}, - {"imm32", "lib-common" OS_SEP "imm32.def", false}, - {"version", "lib-common" OS_SEP "version.def", false}, - {"advapi32", "lib-common" OS_SEP "advapi32.def.in", true}, - {"oleaut32", "lib-common" OS_SEP "oleaut32.def.in", false}, - {"ole32", "lib-common" OS_SEP "ole32.def.in", false}, - {"shell32", "lib-common" OS_SEP "shell32.def", true}, - {"user32", "lib-common" OS_SEP "user32.def.in", true}, - {"kernel32", "lib-common" OS_SEP "kernel32.def.in", true}, - {"ntdll", "libarm32" OS_SEP "ntdll.def", true}, - {"ntdll", "lib32" OS_SEP "ntdll.def", true}, - {"ntdll", "lib64" OS_SEP "ntdll.def", true}, + {"advapi32",true}, + {"bcrypt", false}, + {"comctl32",false}, + {"comdlg32",false}, + {"crypt32", false}, + {"cryptnet",false}, + {"gdi32", false}, + {"imm32", false}, + {"kernel32",true}, + {"lz32", false}, + {"mpr", false}, + {"msvcrt", true}, + {"mswsock", false}, + {"ncrypt", false}, + {"netapi32",false}, + {"ntdll", true}, + {"ole32", false}, + {"oleaut32",false}, + {"opengl32",false}, + {"rpcns4", false}, + {"rpcrt4", false}, + {"scarddlg",false}, + {"setupapi",false}, + {"shell32", true}, + {"urlmon", false}, + {"user32", true}, + {"version", false}, + {"winmm", false}, + {"winscard",false}, + {"winspool",false}, + {"wintrust",false}, + {"ws2_32", false}, }; struct LinkJob { @@ -581,11 +594,13 @@ struct LinkJob { ZigList args; bool link_in_crt; HashMap rpath_table; + Stage2ProgressNode *build_dep_prog_node; }; -static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file) { - CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str(name)); +static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file, + Stage2ProgressNode *progress_node) +{ + CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node); ZigList c_source_files = {0}; c_source_files.append(c_file); child_gen->c_source_files = c_source_files; @@ -609,9 +624,8 @@ static const char *path_from_libunwind(CodeGen *g, const char *subpath) { return path_from_zig_lib(g, "libunwind", subpath); } -static const char *build_libunwind(CodeGen *parent) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("unwind")); +static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress_node) { + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "unwind", progress_node); LinkLib *new_link_lib = codegen_add_link_lib(child_gen, buf_create_from_str("c")); new_link_lib->provided_explicitly = false; enum SrcKind { @@ -1004,9 +1018,8 @@ static bool is_musl_arch_name(const char *name) { return false; } -static const char *build_musl(CodeGen *parent) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("c")); +static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node) { + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c", progress_node); // When there is a src//foo.* then it should substitute for src/foo.* // Even a .s file can substitute for a .c file. @@ -1162,7 +1175,7 @@ static void add_mingwex_os_dep(CodeGen *parent, CodeGen *child_gen, const char * child_gen->c_source_files.append(c_file); } -static const char *get_libc_crt_file(CodeGen *parent, const char *file) { +static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2ProgressNode *progress_node) { if (parent->libc == nullptr && parent->zig_target->os == OsWindows) { if (strcmp(file, "crt2.o") == 0) { CFile *c_file = allocate(1); @@ -1175,7 +1188,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { //c_file->args.append("-DUNICODE"); //c_file->args.append("-D_UNICODE"); //c_file->args.append("-DWPRFLAG=1"); - return build_libc_object(parent, "crt2", c_file); + return build_libc_object(parent, "crt2", c_file, progress_node); } else if (strcmp(file, "dllcrt2.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = buf_ptr(buf_sprintf( @@ -1183,10 +1196,9 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { mingw_add_cc_args(parent, c_file); c_file->args.append("-U__CRTDLL__"); c_file->args.append("-D__MSVCRT__"); - return build_libc_object(parent, "dllcrt2", c_file); + return build_libc_object(parent, "dllcrt2", c_file, progress_node); } else if (strcmp(file, "mingw32.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("mingw32")); + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingw32", progress_node); static const char *deps[] = { "mingw" OS_SEP "crt" OS_SEP "crt0_c.c", @@ -1243,8 +1255,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { codegen_build_and_link(child_gen); return buf_ptr(&child_gen->output_file_path); } else if (strcmp(file, "msvcrt-os.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("msvcrt-os")); + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "msvcrt-os", progress_node); for (size_t i = 0; i < array_length(msvcrt_common_src); i += 1) { add_msvcrt_os_dep(parent, child_gen, msvcrt_common_src[i]); @@ -1261,8 +1272,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { codegen_build_and_link(child_gen); return buf_ptr(&child_gen->output_file_path); } else if (strcmp(file, "mingwex.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("mingwex")); + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingwex", progress_node); for (size_t i = 0; i < array_length(mingwex_generic_src); i += 1) { add_mingwex_os_dep(parent, child_gen, mingwex_generic_src[i]); @@ -1305,7 +1315,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DASSEMBLER"); c_file->args.append("-g"); c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crti", c_file); + return build_libc_object(parent, "crti", c_file, progress_node); } else if (strcmp(file, "crtn.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = glibc_start_asm_path(parent, "crtn.S"); @@ -1316,7 +1326,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DASSEMBLER"); c_file->args.append("-g"); c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crtn", c_file); + return build_libc_object(parent, "crtn", c_file, progress_node); } else if (strcmp(file, "start.os") == 0) { CFile *c_file = allocate(1); c_file->source_path = glibc_start_asm_path(parent, "start.S"); @@ -1334,7 +1344,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DASSEMBLER"); c_file->args.append("-g"); c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "start", c_file); + return build_libc_object(parent, "start", c_file, progress_node); } else if (strcmp(file, "abi-note.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "abi-note.S"); @@ -1347,19 +1357,17 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DASSEMBLER"); c_file->args.append("-g"); c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "abi-note", c_file); + return build_libc_object(parent, "abi-note", c_file, progress_node); } else if (strcmp(file, "Scrt1.o") == 0) { - const char *start_os = get_libc_crt_file(parent, "start.os"); - const char *abi_note_o = get_libc_crt_file(parent, "abi-note.o"); - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeObj, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("Scrt1")); + const char *start_os = get_libc_crt_file(parent, "start.os", progress_node); + const char *abi_note_o = get_libc_crt_file(parent, "abi-note.o", progress_node); + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeObj, nullptr, "Scrt1", progress_node); codegen_add_object(child_gen, buf_create_from_str(start_os)); codegen_add_object(child_gen, buf_create_from_str(abi_note_o)); codegen_build_and_link(child_gen); return buf_ptr(&child_gen->output_file_path); } else if (strcmp(file, "libc_nonshared.a") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); - codegen_set_out_name(child_gen, buf_create_from_str("c_nonshared")); + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node); { CFile *c_file = allocate(1); c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c"); @@ -1388,7 +1396,8 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DPIC"); c_file->args.append("-DLIBC_NONSHARED=1"); c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str(build_libc_object(parent, "elf-init", c_file))); + codegen_add_object(child_gen, buf_create_from_str( + build_libc_object(parent, "elf-init", c_file, progress_node))); } static const struct { const char *name; @@ -1432,7 +1441,8 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-DPIC"); c_file->args.append("-DLIBC_NONSHARED=1"); c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str(build_libc_object(parent, deps[i].name, c_file))); + codegen_add_object(child_gen, buf_create_from_str( + build_libc_object(parent, deps[i].name, c_file, progress_node))); } codegen_build_and_link(child_gen); return buf_ptr(&child_gen->output_file_path); @@ -1445,20 +1455,20 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->source_path = musl_start_asm_path(parent, "crti.s"); musl_add_cc_args(parent, c_file, false); c_file->args.append("-Qunused-arguments"); - return build_libc_object(parent, "crti", c_file); + return build_libc_object(parent, "crti", c_file, progress_node); } else if (strcmp(file, "crtn.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = musl_start_asm_path(parent, "crtn.s"); c_file->args.append("-Qunused-arguments"); musl_add_cc_args(parent, c_file, false); - return build_libc_object(parent, "crtn", c_file); + return build_libc_object(parent, "crtn", c_file, progress_node); } else if (strcmp(file, "crt1.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "crt1.c"); musl_add_cc_args(parent, c_file, false); c_file->args.append("-fno-stack-protector"); c_file->args.append("-DCRT"); - return build_libc_object(parent, "crt1", c_file); + return build_libc_object(parent, "crt1", c_file, progress_node); } else if (strcmp(file, "Scrt1.o") == 0) { CFile *c_file = allocate(1); c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "Scrt1.c"); @@ -1466,7 +1476,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { c_file->args.append("-fPIC"); c_file->args.append("-fno-stack-protector"); c_file->args.append("-DCRT"); - return build_libc_object(parent, "Scrt1", c_file); + return build_libc_object(parent, "Scrt1", c_file, progress_node); } else { zig_unreachable(); } @@ -1478,10 +1488,11 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) { } } -static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type) { - CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type, - parent_gen->libc); - codegen_set_out_name(child_gen, buf_create_from_str(aname)); +static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type, + Stage2ProgressNode *progress_node) +{ + CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type, parent_gen->libc, aname, + progress_node); // This is so that compiler_rt and libc.zig libraries know whether they // will eventually be linked with libc. They make different decisions @@ -1498,18 +1509,18 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, return &child_gen->output_file_path; } -static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type) { +static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { Buf *full_path = buf_alloc(); os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path); - return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type); + return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type, progress_node); } -static Buf *build_c(CodeGen *parent_gen, OutType child_out_type) { +static Buf *build_c(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { Buf *full_path = buf_alloc(); os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("c.zig"), full_path); - return build_a_raw(parent_gen, "c", full_path, child_out_type); + return build_a_raw(parent_gen, "c", full_path, child_out_type, progress_node); } static const char *get_darwin_arch_string(const ZigTarget *t) { @@ -1603,7 +1614,7 @@ static void add_glibc_libs(LinkJob *lj) { Buf *artifact_dir; if ((err = glibc_build_dummies_and_maps(lj->codegen, glibc_abi, lj->codegen->zig_target, - &artifact_dir, true))) + &artifact_dir, true, lj->build_dep_prog_node))) { fprintf(stderr, "%s\n", err_str(err)); exit(1); @@ -1636,6 +1647,10 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append("--gc-sections"); } + if (g->link_eh_frame_hdr) { + lj->args.append("--eh-frame-hdr"); + } + lj->args.append("-m"); lj->args.append(getLDMOption(g->zig_target)); @@ -1679,9 +1694,9 @@ static void construct_linker_job_elf(LinkJob *lj) { } else { crt1o = "Scrt1.o"; } - lj->args.append(get_libc_crt_file(g, crt1o)); + lj->args.append(get_libc_crt_file(g, crt1o, lj->build_dep_prog_node)); if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crti.o")); + lj->args.append(get_libc_crt_file(g, "crti.o", lj->build_dep_prog_node)); } } @@ -1746,11 +1761,11 @@ static void construct_linker_job_elf(LinkJob *lj) { if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) { if (g->libc_link_lib == nullptr) { - Buf *libc_a_path = build_c(g, OutTypeLib); + Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(libc_a_path)); } - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib); + Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -1781,28 +1796,12 @@ static void construct_linker_job_elf(LinkJob *lj) { if (g->libc != nullptr) { if (!g->have_dynamic_link) { lj->args.append("--start-group"); - if (!target_is_android(g->zig_target)) { - lj->args.append("-lgcc"); - lj->args.append("-lgcc_eh"); - } lj->args.append("-lc"); lj->args.append("-lm"); lj->args.append("--end-group"); } else { - if (!target_is_android(g->zig_target)) { - lj->args.append("-lgcc"); - lj->args.append("--as-needed"); - lj->args.append("-lgcc_s"); - lj->args.append("--no-as-needed"); - } lj->args.append("-lc"); lj->args.append("-lm"); - if (!target_is_android(g->zig_target)) { - lj->args.append("-lgcc"); - lj->args.append("--as-needed"); - lj->args.append("-lgcc_s"); - lj->args.append("--no-as-needed"); - } } if (g->zig_target->os == OsFreeBSD) { @@ -1810,15 +1809,15 @@ static void construct_linker_job_elf(LinkJob *lj) { } } else if (target_is_glibc(g->zig_target)) { if (target_supports_libunwind(g->zig_target)) { - lj->args.append(build_libunwind(g)); + lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); } add_glibc_libs(lj); - lj->args.append(get_libc_crt_file(g, "libc_nonshared.a")); + lj->args.append(get_libc_crt_file(g, "libc_nonshared.a", lj->build_dep_prog_node)); } else if (target_is_musl(g->zig_target)) { if (target_supports_libunwind(g->zig_target)) { - lj->args.append(build_libunwind(g)); + lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); } - lj->args.append(build_musl(g)); + lj->args.append(build_musl(g, lj->build_dep_prog_node)); } else { zig_unreachable(); } @@ -1827,21 +1826,15 @@ static void construct_linker_job_elf(LinkJob *lj) { // crt end if (lj->link_in_crt) { if (target_is_android(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtend_android.o")); + lj->args.append(get_libc_crt_file(g, "crtend_android.o", lj->build_dep_prog_node)); } else if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtn.o")); + lj->args.append(get_libc_crt_file(g, "crtn.o", lj->build_dep_prog_node)); } } if (!g->zig_target->is_native) { lj->args.append("--allow-shlib-undefined"); } - - if (g->zig_target->os == OsZen) { - lj->args.append("-e"); - lj->args.append("_start"); - lj->args.append("--image-base=0x10000000"); - } } static void construct_linker_job_wasm(LinkJob *lj) { @@ -1874,10 +1867,10 @@ static void construct_linker_job_wasm(LinkJob *lj) { } if (g->out_type != OutTypeObj) { - Buf *libc_o_path = build_c(g, OutTypeObj); + Buf *libc_o_path = build_c(g, OutTypeObj, lj->build_dep_prog_node); lj->args.append(buf_ptr(libc_o_path)); - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj); + Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, lj->build_dep_prog_node); lj->args.append(buf_ptr(compiler_rt_o_path)); } } @@ -1955,7 +1948,7 @@ static void print_zig_cc_cmd(ZigList *args) { fprintf(stderr, "\n"); } -static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_rel_path) { +static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_file) { Error err; Buf *self_exe_path = buf_alloc(); @@ -1969,12 +1962,10 @@ static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_re exit(1); } - Buf *cache_dir = get_stage1_cache_path(); + Buf *cache_dir = get_global_cache_dir(); Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(cache_dir)); Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); - Buf *def_in_file = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), buf_ptr(def_in_rel_path)); Buf *def_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "def-include", buf_ptr(parent->zig_lib_dir)); @@ -2062,18 +2053,12 @@ static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_re lib_final_path = buf_alloc(); os_path_join(artifact_dir, final_lib_basename, lib_final_path); - args.resize(0); - args.append("link"); - coff_append_machine_arg(parent, &args); - - args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_final_path)))); - args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(lib_final_path)))); - - Buf diag = BUF_INIT; - ZigLLVM_ObjectFormatType target_ofmt = target_object_format(parent->zig_target); - if (!zig_lld_link(target_ofmt, args.items, args.length, &diag)) { - fprintf(stderr, "%s\n", buf_ptr(&diag)); - exit(1); + if (ZigLLVMWriteImportLibrary(buf_ptr(def_final_path), + parent->zig_target->arch, + buf_ptr(lib_final_path), + /* kill_at */ true)) + { + zig_panic("link: could not emit %s", buf_ptr(lib_final_path)); } } else { // cache hit @@ -2097,6 +2082,60 @@ static bool is_linking_system_lib(CodeGen *g, const char *name) { return false; } +static Error find_mingw_lib_def(LinkJob *lj, const char *name, Buf *out_path) { + CodeGen *g = lj->codegen; + Buf override_path = BUF_INIT; + Error err; + + char const *lib_path = nullptr; + if (g->zig_target->arch == ZigLLVM_x86) { + lib_path = "lib32"; + } else if (g->zig_target->arch == ZigLLVM_x86_64) { + lib_path = "lib64"; + } else if (target_is_arm(g->zig_target)) { + const bool is_32 = target_arch_pointer_bit_width(g->zig_target->arch) == 32; + lib_path = is_32 ? "libarm32" : "libarm64"; + } else { + zig_unreachable(); + } + + // Try the archtecture-specific path first + buf_resize(&override_path, 0); + buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), lib_path, name); + + bool does_exist; + if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { + return err; + } + + if (!does_exist) { + // Try the generic version + buf_resize(&override_path, 0); + buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), name); + + if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { + return err; + } + } + + if (!does_exist) { + // Try the generic version and preprocess it + buf_resize(&override_path, 0); + buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def.in", buf_ptr(g->zig_lib_dir), name); + + if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { + return err; + } + } + + if (!does_exist) { + return ErrorFileNotFound; + } + + buf_init_from_buf(out_path, &override_path); + return ErrorNone; +} + static void add_mingw_link_args(LinkJob *lj, bool is_library) { CodeGen *g = lj->codegen; @@ -2111,35 +2150,31 @@ static void add_mingw_link_args(LinkJob *lj, bool is_library) { } if (is_dll) { - lj->args.append(get_libc_crt_file(g, "dllcrt2.o")); + lj->args.append(get_libc_crt_file(g, "dllcrt2.o", lj->build_dep_prog_node)); } else { - lj->args.append(get_libc_crt_file(g, "crt2.o")); + lj->args.append(get_libc_crt_file(g, "crt2.o", lj->build_dep_prog_node)); } - lj->args.append(get_libc_crt_file(g, "mingw32.lib")); - lj->args.append(get_libc_crt_file(g, "mingwex.lib")); - lj->args.append(get_libc_crt_file(g, "msvcrt-os.lib")); + lj->args.append(get_libc_crt_file(g, "mingw32.lib", lj->build_dep_prog_node)); + lj->args.append(get_libc_crt_file(g, "mingwex.lib", lj->build_dep_prog_node)); + lj->args.append(get_libc_crt_file(g, "msvcrt-os.lib", lj->build_dep_prog_node)); for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) { const char *name = mingw_def_list[def_i].name; - Buf *path = buf_create_from_str(mingw_def_list[def_i].path); - bool always_link = mingw_def_list[def_i].always_link; - bool is_this_arch = false; - if (buf_starts_with_str(path, "lib-common" OS_SEP)) { - is_this_arch = true; - } else if (target_is_arm(g->zig_target)) { - if (target_arch_pointer_bit_width(g->zig_target->arch) == 32) { - is_this_arch = buf_starts_with_str(path, "libarm32" OS_SEP); - } else { - is_this_arch = buf_starts_with_str(path, "libarm64" OS_SEP); + const bool always_link = mingw_def_list[def_i].always_link; + + if (always_link || is_linking_system_lib(g, name)) { + Buf lib_path = BUF_INIT; + Error err = find_mingw_lib_def(lj, name, &lib_path); + + if (err == ErrorFileNotFound) { + zig_panic("link: could not find .def file to build %s\n", name); + } else if (err != ErrorNone) { + zig_panic("link: unable to check if .def file for %s exists: %s", + name, err_str(err)); } - } else if (g->zig_target->arch == ZigLLVM_x86) { - is_this_arch = buf_starts_with_str(path, "lib32" OS_SEP); - } else if (g->zig_target->arch == ZigLLVM_x86_64) { - is_this_arch = buf_starts_with_str(path, "lib64" OS_SEP); - } - if (is_this_arch && (always_link || is_linking_system_lib(g, name))) { - lj->args.append(get_def_lib(g, name, path)); + + lj->args.append(get_def_lib(g, name, &lib_path)); } } } @@ -2264,17 +2299,15 @@ static void construct_linker_job_coff(LinkJob *lj) { if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) { if (g->libc_link_lib == nullptr && !g->is_dummy_so) { - Buf *libc_a_path = build_c(g, OutTypeLib); + Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(libc_a_path)); } // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib); + Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(compiler_rt_o_path)); } - Buf *def_contents = buf_alloc(); - ZigList gen_lib_args = {0}; for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { LinkLib *link_lib = g->link_libs_list.at(lib_i); if (buf_eql_str(link_lib->name, "c")) { @@ -2297,36 +2330,27 @@ static void construct_linker_job_coff(LinkJob *lj) { continue; } - buf_resize(def_contents, 0); - buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); - for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { - Buf *symbol_name = link_lib->symbols.at(exp_i); - buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); - } - buf_appendf(def_contents, "\n"); + // This library may be a system one and we may have a suitable .lib file - Buf *def_path = buf_alloc(); - os_path_join(g->output_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); - if ((err = os_write_file(def_path, def_contents))) { - zig_panic("error writing def file: %s", err_str(err)); + // Normalize the library name to lower case, the FS may be + // case-sensitive + char *name = strdup(buf_ptr(link_lib->name)); + assert(name != nullptr); + for (char *ch = name; *ch; ++ch) *ch = tolower(*ch); + + Buf lib_path = BUF_INIT; + err = find_mingw_lib_def(lj, name, &lib_path); + + if (err == ErrorFileNotFound) { + zig_panic("link: could not find .def file to build %s\n", name); + } else if (err != ErrorNone) { + zig_panic("link: unable to check if .def file for %s exists: %s", + name, err_str(err)); } - Buf *generated_lib_path = buf_alloc(); - os_path_join(g->output_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); + lj->args.append(get_def_lib(g, name, &lib_path)); - gen_lib_args.resize(0); - gen_lib_args.append("link"); - - coff_append_machine_arg(g, &gen_lib_args); - gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); - gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); - Buf diag = BUF_INIT; - ZigLLVM_ObjectFormatType target_ofmt = target_object_format(g->zig_target); - if (!zig_lld_link(target_ofmt, gen_lib_args.items, gen_lib_args.length, &diag)) { - fprintf(stderr, "%s\n", buf_ptr(&diag)); - exit(1); - } - lj->args.append(buf_ptr(generated_lib_path)); + free(name); } } @@ -2519,7 +2543,7 @@ static void construct_linker_job_macho(LinkJob *lj) { // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce if (g->out_type == OutTypeExe || is_dyn_lib) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib); + Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -2577,16 +2601,23 @@ static void construct_linker_job(LinkJob *lj) { } } -void zig_link_add_compiler_rt(CodeGen *g) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj); +void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node) { + Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, progress_node); g->link_objects.append(compiler_rt_o_path); } void codegen_link(CodeGen *g) { codegen_add_time_event(g, "Build Dependencies"); - LinkJob lj = {0}; + { + const char *progress_name = "Build Dependencies"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + lj.build_dep_prog_node = g->sub_progress_node; + } + + // even though we're calling LLD as a library it thinks the first // argument is its own exe name lj.args.append("lld"); @@ -2612,6 +2643,11 @@ void codegen_link(CodeGen *g) { } ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os); codegen_add_time_event(g, "LLVM Link"); + { + const char *progress_name = "Link"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } if (g->verbose_link) { fprintf(stderr, "ar rcs %s", buf_ptr(&g->output_file_path)); for (size_t i = 0; i < file_names.length; i += 1) { @@ -2642,6 +2678,11 @@ void codegen_link(CodeGen *g) { Buf diag = BUF_INIT; codegen_add_time_event(g, "LLVM Link"); + { + const char *progress_name = "Link"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } if (g->system_linker_hack && g->zig_target->os == OsMacOSX) { Termination term; ZigList args = {}; diff --git a/src/list.hpp b/src/list.hpp index 59782b46a..4b2833843 100644 --- a/src/list.hpp +++ b/src/list.hpp @@ -13,7 +13,7 @@ template struct ZigList { void deinit() { - free(items); + deallocate(items, capacity); } void append(const T& item) { ensure_capacity(length + 1); diff --git a/src/main.cpp b/src/main.cpp index ffaa4e6ca..d89ac352a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,7 +16,7 @@ #include "libc_installation.hpp" #include "userland.h" #include "glibc.hpp" -#include "stack_report.hpp" +#include "dump_analysis.hpp" #include @@ -34,7 +34,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " build-exe [source] create executable from source or object files\n" " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" - " builtin show the source code of that @import(\"builtin\")\n" + " builtin show the source code of @import(\"builtin\")\n" " cc C compiler\n" " fmt parse files and render in canonical zig format\n" " id print the base64-encoded compiler id\n" @@ -43,7 +43,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " libc [paths_file] Display native libc paths file or validate one\n" " run [source] [-- [args]] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" - " translate-c-2 [source] experimental self-hosted translate-c\n" " targets list available compilation targets\n" " test [source] create and run a test build\n" " version print version number and exit\n" @@ -56,14 +55,23 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --color [auto|off|on] enable or disable colored error messages\n" " --disable-gen-h do not generate a C header file (.h)\n" " --disable-valgrind omit valgrind client requests in debug builds\n" + " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n" " --enable-valgrind include valgrind client requests release builds\n" " -fstack-check enable stack probing in unsafe builds\n" " -fno-stack-check disable stack probing in safe builds\n" + " -fsanitize-c enable C undefined behavior detection in unsafe builds\n" + " -fno-sanitize-c disable C undefined behavior detection in safe builds\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " -fPIC enable Position Independent Code\n" " -fno-PIC disable Position Independent Code\n" " -ftime-report print timing diagnostics\n" " -fstack-report print stack size diagnostics\n" +#ifdef ZIG_ENABLE_MEM_PROFILE + " -fmem-report print memory usage diagnostics\n" +#endif + " -fdump-analysis write analysis.json file with type information\n" + " -femit-docs create a docs/ dir with html documentation\n" + " -fno-emit-bin skip emitting machine code\n" " --libc [file] Provide a file which specifies libc paths\n" " --name [name] override output name\n" " --output-dir [dir] override output directory (defaults to cwd)\n" @@ -85,8 +93,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" " --verbose-cimport enable compiler debug output for C imports\n" " --verbose-cc enable compiler debug output for C compilation\n" - " -dirafter [dir] same as -isystem but do it last\n" - " -isystem [dir] add additional search path for other .h files\n" + " -dirafter [dir] add directory to AFTER include search path\n" + " -isystem [dir] add directory to SYSTEM include search path\n" + " -I[dir] add directory to include search path\n" " -mllvm [arg] (unsupported) forward an arg to LLVM's option processing\n" " --override-lib-dir [arg] override path to Zig lib directory\n" " -ffunction-sections places each function in a separate section\n" @@ -234,7 +243,6 @@ enum Cmd { CmdTargets, CmdTest, CmdTranslateC, - CmdTranslateCUserland, CmdVersion, CmdZen, CmdLibC, @@ -302,9 +310,29 @@ static int zig_error_no_build_file(void) { extern "C" int ZigClang_main(int argc, char **argv); +#ifdef ZIG_ENABLE_MEM_PROFILE +bool mem_report = false; +#endif + +int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + } +#ifdef ZIG_ENABLE_MEM_PROFILE + if (mem_report) { + memprof_dump_stats(stderr); + } +#endif + return exit_code; +} + int main(int argc, char **argv) { stage2_attach_segfault_handler(); +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_init(); +#endif + char *arg0 = argv[0]; Error err; @@ -450,6 +478,7 @@ int main(int argc, char **argv) { bool verbose_llvm_ir = false; bool verbose_cimport = false; bool verbose_cc = false; + bool link_eh_frame_hdr = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; Buf *dynamic_linker = nullptr; @@ -479,6 +508,9 @@ int main(int argc, char **argv) { size_t ver_patch = 0; bool timing_info = false; bool stack_report = false; + bool enable_dump_analysis = false; + bool enable_doc_generation = false; + bool disable_bin_generation = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; @@ -494,6 +526,7 @@ int main(int argc, char **argv) { ValgrindSupport valgrind_support = ValgrindSupportAuto; WantPIC want_pic = WantPICAuto; WantStackCheck want_stack_check = WantStackCheckAuto; + WantCSanitize want_sanitize_c = WantCSanitizeAuto; bool function_sections = false; ZigList llvm_argv = {0}; @@ -580,9 +613,10 @@ int main(int argc, char **argv) { Buf *cache_dir_buf = buf_create_from_str(cache_dir); full_cache_dir = os_path_resolve(&cache_dir_buf, 1); } + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0); CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, - BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false); + BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false, root_progress_node); g->valgrind_support = valgrind_support; g->enable_time_report = timing_info; codegen_set_out_name(g, buf_create_from_str("build")); @@ -592,9 +626,13 @@ int main(int argc, char **argv) { ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), buf_ptr(&build_file_basename), "std.special"); - g->root_package->package_table.put(buf_create_from_str("@build"), build_pkg); + g->main_pkg->package_table.put(buf_create_from_str("@build"), build_pkg); g->enable_cache = get_cache_opt(enable_cache, true); codegen_build_and_link(g); + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + root_progress_node = nullptr; + } Termination term; args.items[0] = buf_ptr(&g->output_file_path); @@ -662,10 +700,25 @@ int main(int argc, char **argv) { timing_info = true; } else if (strcmp(arg, "-fstack-report") == 0) { stack_report = true; + } else if (strcmp(arg, "-fmem-report") == 0) { +#ifdef ZIG_ENABLE_MEM_PROFILE + mem_report = true; +#else + fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n"); + return print_error_usage(arg0); +#endif + } else if (strcmp(arg, "-fdump-analysis") == 0) { + enable_dump_analysis = true; + } else if (strcmp(arg, "-femit-docs") == 0) { + enable_doc_generation = true; + } else if (strcmp(arg, "-fno-emit-bin") == 0) { + disable_bin_generation = true; } else if (strcmp(arg, "--enable-valgrind") == 0) { valgrind_support = ValgrindSupportEnabled; } else if (strcmp(arg, "--disable-valgrind") == 0) { valgrind_support = ValgrindSupportDisabled; + } else if (strcmp(arg, "--eh-frame-hdr") == 0) { + link_eh_frame_hdr = true; } else if (strcmp(arg, "-fPIC") == 0) { want_pic = WantPICEnabled; } else if (strcmp(arg, "-fno-PIC") == 0) { @@ -674,6 +727,10 @@ int main(int argc, char **argv) { want_stack_check = WantStackCheckEnabled; } else if (strcmp(arg, "-fno-stack-check") == 0) { want_stack_check = WantStackCheckDisabled; + } else if (strcmp(arg, "-fsanitize-c") == 0) { + want_sanitize_c = WantCSanitizeEnabled; + } else if (strcmp(arg, "-fno-sanitize-c") == 0) { + want_sanitize_c = WantCSanitizeDisabled; } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; } else if (strcmp(arg, "--single-threaded") == 0) { @@ -696,6 +753,9 @@ int main(int argc, char **argv) { if (strcmp(l, "c") == 0) have_libc = true; link_libs.append(l); + } else if (arg[1] == 'I' && arg[2] != 0) { + clang_argv.append("-I"); + clang_argv.append(&arg[2]); } else if (arg[1] == 'F' && arg[2] != 0) { framework_dirs.append(&arg[2]); } else if (strcmp(arg, "--pkg-begin") == 0) { @@ -771,6 +831,9 @@ int main(int argc, char **argv) { } else if (strcmp(arg, "-isystem") == 0) { clang_argv.append("-isystem"); clang_argv.append(argv[i]); + } else if (strcmp(arg, "-I") == 0) { + clang_argv.append("-I"); + clang_argv.append(argv[i]); } else if (strcmp(arg, "-dirafter") == 0) { clang_argv.append("-dirafter"); clang_argv.append(argv[i]); @@ -899,8 +962,6 @@ int main(int argc, char **argv) { cmd = CmdLibC; } else if (strcmp(arg, "translate-c") == 0) { cmd = CmdTranslateC; - } else if (strcmp(arg, "translate-c-2") == 0) { - cmd = CmdTranslateCUserland; } else if (strcmp(arg, "test") == 0) { cmd = CmdTest; out_type = OutTypeExe; @@ -917,7 +978,6 @@ int main(int argc, char **argv) { case CmdBuild: case CmdRun: case CmdTranslateC: - case CmdTranslateCUserland: case CmdTest: case CmdLibC: if (!in_file) { @@ -944,6 +1004,10 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + Stage2Progress *progress = stage2_progress_create(); + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); + if (color == ErrColorOff) stage2_progress_disable_tty(progress); + init_all_targets(); ZigTarget target; @@ -1014,18 +1078,18 @@ int main(int argc, char **argv) { if (in_file) { ZigLibCInstallation libc; if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true))) - return EXIT_FAILURE; - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_FAILURE); + return main_exit(root_progress_node, EXIT_SUCCESS); } ZigLibCInstallation libc; if ((err = zig_libc_find_native(&libc, true))) - return EXIT_FAILURE; + return main_exit(root_progress_node, EXIT_FAILURE); zig_libc_render(&libc, stdout); - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); } case CmdBuiltin: { CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, - out_type, build_mode, override_lib_dir, nullptr, nullptr, false); + out_type, build_mode, override_lib_dir, nullptr, nullptr, false, root_progress_node); codegen_set_strip(g, strip); for (size_t i = 0; i < link_libs.length; i += 1) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); @@ -1035,18 +1099,18 @@ int main(int argc, char **argv) { g->valgrind_support = valgrind_support; g->want_pic = want_pic; g->want_stack_check = want_stack_check; + g->want_sanitize_c = want_sanitize_c; g->want_single_threaded = want_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); - return EXIT_FAILURE; + return main_exit(root_progress_node, EXIT_FAILURE); } - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); } case CmdRun: case CmdBuild: case CmdTranslateC: - case CmdTranslateCUserland: case CmdTest: { if (cmd == CmdBuild && !in_file && objects.length == 0 && @@ -1058,7 +1122,7 @@ int main(int argc, char **argv) { " * --object argument\n" " * --c-source argument\n"); return print_error_usage(arg0); - } else if ((cmd == CmdTranslateC || cmd == CmdTranslateCUserland || + } else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) { fprintf(stderr, "Expected source file argument.\n"); @@ -1070,7 +1134,7 @@ int main(int argc, char **argv) { assert(cmd != CmdBuild || out_type != OutTypeUnknown); - bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC || cmd == CmdTranslateCUserland); + bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC); if (cmd == CmdRun) { out_name = "run"; @@ -1104,8 +1168,7 @@ int main(int argc, char **argv) { return print_error_usage(arg0); } - Buf *zig_root_source_file = (cmd == CmdTranslateC || cmd == CmdTranslateCUserland) ? - nullptr : in_file_buf; + Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; if (cmd == CmdRun && buf_out_name == nullptr) { buf_out_name = buf_create_from_str("run"); @@ -1115,13 +1178,13 @@ int main(int argc, char **argv) { libc = allocate(1); if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) { fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); - return EXIT_FAILURE; + return main_exit(root_progress_node, EXIT_FAILURE); } } Buf *cache_dir_buf; if (cache_dir == nullptr) { if (cmd == CmdRun) { - cache_dir_buf = get_stage1_cache_path(); + cache_dir_buf = get_global_cache_dir(); } else { cache_dir_buf = buf_create_from_str(default_zig_cache_name); } @@ -1129,15 +1192,20 @@ int main(int argc, char **argv) { cache_dir_buf = buf_create_from_str(cache_dir); } CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, - override_lib_dir, libc, cache_dir_buf, cmd == CmdTest); + override_lib_dir, libc, cache_dir_buf, cmd == CmdTest, root_progress_node); if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2); g->valgrind_support = valgrind_support; + g->link_eh_frame_hdr = link_eh_frame_hdr; g->want_pic = want_pic; g->want_stack_check = want_stack_check; + g->want_sanitize_c = want_sanitize_c; g->subsystem = subsystem; g->enable_time_report = timing_info; g->enable_stack_report = stack_report; + g->enable_dump_analysis = enable_dump_analysis; + g->enable_doc_generation = enable_doc_generation; + g->disable_bin_generation = disable_bin_generation; codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); g->want_single_threaded = want_single_threaded; @@ -1189,7 +1257,7 @@ int main(int argc, char **argv) { codegen_set_rdynamic(g, rdynamic); if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); - return EXIT_FAILURE; + return main_exit(root_progress_node, EXIT_FAILURE); } if (mmacosx_version_min) { @@ -1208,7 +1276,7 @@ int main(int argc, char **argv) { codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix)); } - add_package(g, cur_pkg, g->root_package); + add_package(g, cur_pkg, g->main_pkg); if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { g->c_source_files = c_source_files; @@ -1223,12 +1291,20 @@ int main(int argc, char **argv) { g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun); codegen_build_and_link(g); + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + root_progress_node = nullptr; + } if (timing_info) codegen_print_timing_report(g, stdout); if (stack_report) zig_print_stack_report(g, stdout); if (cmd == CmdRun) { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_dump_stats(stderr); +#endif + const char *exec_path = buf_ptr(&g->output_file_path); ZigList args = {0}; @@ -1252,17 +1328,18 @@ int main(int argc, char **argv) { buf_replace(&g->output_file_path, '/', '\\'); #endif if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0) - return EXIT_FAILURE; + return main_exit(root_progress_node, EXIT_FAILURE); } - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); } else { zig_unreachable(); } - } else if (cmd == CmdTranslateC || cmd == CmdTranslateCUserland) { - codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland); + } else if (cmd == CmdTranslateC) { + g->enable_cache = get_cache_opt(enable_cache, false); + codegen_translate_c(g, in_file_buf); if (timing_info) codegen_print_timing_report(g, stderr); - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); } else if (cmd == CmdTest) { codegen_set_emit_file_type(g, emit_file_type); @@ -1271,6 +1348,10 @@ int main(int argc, char **argv) { g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr); codegen_build_and_link(g); + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + root_progress_node = nullptr; + } if (timing_info) { codegen_print_timing_report(g, stdout); @@ -1280,6 +1361,11 @@ int main(int argc, char **argv) { zig_print_stack_report(g, stdout); } + if (g->disable_bin_generation) { + fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); + return main_exit(root_progress_node, EXIT_SUCCESS); + } + Buf *test_exe_path_unresolved = &g->output_file_path; Buf *test_exe_path = buf_alloc(); *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1); @@ -1287,7 +1373,7 @@ int main(int argc, char **argv) { if (emit_file_type != EmitFileTypeBinary) { fprintf(stderr, "Created %s but skipping execution because it is non executable.\n", buf_ptr(test_exe_path)); - return 0; + return main_exit(root_progress_node, EXIT_SUCCESS); } for (size_t i = 0; i < test_exec_args.length; i += 1) { @@ -1299,7 +1385,7 @@ int main(int argc, char **argv) { if (!target_can_exec(&native, &target) && test_exec_args.length == 0) { fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", buf_ptr(test_exe_path)); - return 0; + return main_exit(root_progress_node, EXIT_SUCCESS); } Termination term; @@ -1311,20 +1397,20 @@ int main(int argc, char **argv) { fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); } - return (term.how == TerminationIdClean) ? term.code : -1; + return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1); } else { zig_unreachable(); } } case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); case CmdZen: { const char *ptr; size_t len; stage2_zen(&ptr, &len); fwrite(ptr, len, 1, stdout); - return EXIT_SUCCESS; + return main_exit(root_progress_node, EXIT_SUCCESS); } case CmdTargets: return print_target_list(stdout); diff --git a/src/memory_profiling.cpp b/src/memory_profiling.cpp new file mode 100644 index 000000000..4bd4cea7b --- /dev/null +++ b/src/memory_profiling.cpp @@ -0,0 +1,150 @@ +#include "memory_profiling.hpp" +#include "hash_map.hpp" +#include "list.hpp" +#include "util.hpp" +#include + +#ifdef ZIG_ENABLE_MEM_PROFILE + +MemprofInternCount memprof_intern_count; + +static bool str_eql_str(const char *a, const char *b) { + return strcmp(a, b) == 0; +} + +static uint32_t str_hash(const char *s) { + // FNV 32-bit hash + uint32_t h = 2166136261; + for (; *s; s += 1) { + h = h ^ *s; + h = h * 16777619; + } + return h; +} + +struct CountAndSize { + size_t item_count; + size_t type_size; +}; + +ZigList unknown_names = {}; +HashMap usage_table = {}; +bool table_active = false; + +static const char *get_default_name(const char *name_or_null, size_t type_size) { + if (name_or_null != nullptr) return name_or_null; + if (type_size >= unknown_names.length) { + table_active = false; + while (type_size >= unknown_names.length) { + unknown_names.append(nullptr); + } + table_active = true; + } + if (unknown_names.at(type_size) == nullptr) { + char buf[100]; + sprintf(buf, "Unknown_%zu%c", type_size, 0); + unknown_names.at(type_size) = strdup(buf); + } + return unknown_names.at(type_size); +} + +void memprof_alloc(const char *name, size_t count, size_t type_size) { + if (!table_active) return; + if (count == 0) return; + // temporarily disable during table put + table_active = false; + name = get_default_name(name, type_size); + auto existing_entry = usage_table.put_unique(name, {count, type_size}); + if (existing_entry != nullptr) { + assert(existing_entry->value.type_size == type_size); // allocated name does not match type + existing_entry->value.item_count += count; + } + table_active = true; +} + +void memprof_dealloc(const char *name, size_t count, size_t type_size) { + if (!table_active) return; + if (count == 0) return; + name = get_default_name(name, type_size); + auto existing_entry = usage_table.maybe_get(name); + if (existing_entry == nullptr) { + zig_panic("deallocated name '%s' (size %zu) not found in allocated table; compromised memory usage stats", + name, type_size); + } + if (existing_entry->value.type_size != type_size) { + zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size); + } + existing_entry->value.item_count -= count; +} + +void memprof_init(void) { + usage_table.init(1024); + table_active = true; +} + +struct MemItem { + const char *type_name; + CountAndSize count_and_size; +}; + +static size_t get_bytes(const MemItem *item) { + return item->count_and_size.item_count * item->count_and_size.type_size; +} + +static int compare_bytes_desc(const void *a, const void *b) { + size_t size_a = get_bytes((const MemItem *)(a)); + size_t size_b = get_bytes((const MemItem *)(b)); + if (size_a > size_b) + return -1; + if (size_a < size_b) + return 1; + return 0; +} + +void memprof_dump_stats(FILE *file) { + assert(table_active); + // disable modifications from this function + table_active = false; + + ZigList list = {}; + + auto it = usage_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + list.append({entry->key, entry->value}); + } + + qsort(list.items, list.length, sizeof(MemItem), compare_bytes_desc); + + size_t total_bytes_used = 0; + + for (size_t i = 0; i < list.length; i += 1) { + const MemItem *item = &list.at(i); + fprintf(file, "%s: %zu items, %zu bytes each, total ", item->type_name, + item->count_and_size.item_count, item->count_and_size.type_size); + size_t bytes = get_bytes(item); + zig_pretty_print_bytes(file, bytes); + fprintf(file, "\n"); + + total_bytes_used += bytes; + } + + fprintf(stderr, "Total bytes used: "); + zig_pretty_print_bytes(file, total_bytes_used); + fprintf(file, "\n"); + + list.deinit(); + table_active = true; + + fprintf(stderr, "\n"); + fprintf(stderr, "undefined: interned %zu times\n", memprof_intern_count.x_undefined); + fprintf(stderr, "void: interned %zu times\n", memprof_intern_count.x_void); + fprintf(stderr, "null: interned %zu times\n", memprof_intern_count.x_null); + fprintf(stderr, "unreachable: interned %zu times\n", memprof_intern_count.x_unreachable); + fprintf(stderr, "zero_byte: interned %zu times\n", memprof_intern_count.zero_byte); +} + +#endif diff --git a/src/memory_profiling.hpp b/src/memory_profiling.hpp new file mode 100644 index 000000000..78e77a89f --- /dev/null +++ b/src/memory_profiling.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEMORY_PROFILING_HPP +#define ZIG_MEMORY_PROFILING_HPP + +#include "config.h" + +#include +#include + +struct MemprofInternCount { + size_t x_undefined; + size_t x_void; + size_t x_null; + size_t x_unreachable; + size_t zero_byte; +}; +extern MemprofInternCount memprof_intern_count; + +void memprof_init(void); + +void memprof_alloc(const char *name, size_t item_count, size_t type_size); +void memprof_dealloc(const char *name, size_t item_count, size_t type_size); + +void memprof_dump_stats(FILE *file); +#endif diff --git a/src/os.cpp b/src/os.cpp index 6dc3777f8..c34885138 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -52,7 +52,7 @@ typedef SSIZE_T ssize_t; #endif -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) #include #endif @@ -60,7 +60,7 @@ typedef SSIZE_T ssize_t; #include #endif -#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) +#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) #include #endif @@ -85,7 +85,7 @@ static clock_serv_t macos_monotonic_clock; #if defined(__APPLE__) && !defined(environ) #include #define environ (*_NSGetEnviron()) -#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) +#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) extern char **environ; #endif @@ -1450,7 +1450,7 @@ Error os_self_exe_path(Buf *out_path) { } buf_resize(out_path, amt); return ErrorNone; -#elif defined(ZIG_OS_FREEBSD) +#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) buf_resize(out_path, PATH_MAX); int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t cb = PATH_MAX; @@ -1554,7 +1554,7 @@ void os_stderr_set_color(TermColor color) { Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1586,7 +1586,7 @@ Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Ar Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { return ErrorNone; } @@ -1603,7 +1603,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch #if defined(ZIG_OS_WINDOWS) { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1626,7 +1626,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch } { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); + buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1774,25 +1774,36 @@ Error os_get_app_data_dir(Buf *out_path, const char *appname) { buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); return ErrorNone; #elif defined(ZIG_OS_POSIX) - const char *home_dir = getenv("HOME"); - if (home_dir == nullptr) { - // TODO use /etc/passwd - return ErrorFileNotFound; + const char *cache_dir = getenv("XDG_CACHE_HOME"); + if (cache_dir == nullptr) { + cache_dir = getenv("HOME"); + if (cache_dir == nullptr) { + // TODO use /etc/passwd + return ErrorFileNotFound; + } + if (cache_dir[0] == 0) { + return ErrorFileNotFound; + } + buf_init_from_str(out_path, cache_dir); + if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { + buf_append_char(out_path, '/'); + } + buf_appendf(out_path, ".cache/%s", appname); + } else { + if (cache_dir[0] == 0) { + return ErrorFileNotFound; + } + buf_init_from_str(out_path, cache_dir); + if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { + buf_append_char(out_path, '/'); + } + buf_appendf(out_path, "%s", appname); } - if (home_dir[0] == 0) { - return ErrorFileNotFound; - } - buf_init_from_str(out_path, home_dir); - if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { - buf_append_char(out_path, '/'); - } - buf_appendf(out_path, ".local/share/%s", appname); return ErrorNone; #endif } - -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { ZigList *libs = reinterpret_cast< ZigList *>(data); if (info->dlpi_name[0] == '/') { @@ -1803,7 +1814,7 @@ static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, #endif Error os_self_exe_shared_libs(ZigList &paths) { -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) paths.resize(0); dl_iterate_phdr(self_exe_shared_libs_callback, &paths); return ErrorNone; diff --git a/src/os.hpp b/src/os.hpp index c8135e984..7da846a50 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -27,6 +27,8 @@ #define ZIG_OS_FREEBSD #elif defined(__NetBSD__) #define ZIG_OS_NETBSD +#elif defined(__DragonFly__) +#define ZIG_OS_DRAGONFLY #else #define ZIG_OS_UNKNOWN #endif @@ -47,6 +49,7 @@ extern const char *possible_ld_names[]; #if defined(ZIG_OS_WINDOWS) #define ZIG_PRI_usize "I64u" +#define ZIG_PRI_i64 "I64d" #define ZIG_PRI_u64 "I64u" #define ZIG_PRI_llu "I64u" #define ZIG_PRI_x64 "I64x" @@ -54,6 +57,7 @@ extern const char *possible_ld_names[]; #define ZIG_OS_SEP_CHAR '\\' #else #define ZIG_PRI_usize "zu" +#define ZIG_PRI_i64 PRId64 #define ZIG_PRI_u64 PRIu64 #define ZIG_PRI_llu "llu" #define ZIG_PRI_x64 PRIx64 diff --git a/src/parser.cpp b/src/parser.cpp index 96071daa0..0054c0a0c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -37,7 +37,7 @@ static AstNode *ast_parse_root(ParseContext *pc); static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc); static AstNode *ast_parse_test_decl(ParseContext *pc); static AstNode *ast_parse_top_level_comptime(ParseContext *pc); -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod); +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments); static AstNode *ast_parse_fn_proto(ParseContext *pc); static AstNode *ast_parse_var_decl(ParseContext *pc); static AstNode *ast_parse_container_field(ParseContext *pc); @@ -81,7 +81,7 @@ static AstNode *ast_parse_for_type_expr(ParseContext *pc); static AstNode *ast_parse_while_type_expr(ParseContext *pc); static AstNode *ast_parse_switch_expr(ParseContext *pc); static AstNode *ast_parse_asm_expr(ParseContext *pc); -static AstNode *ast_parse_enum_lit(ParseContext *pc); +static AstNode *ast_parse_anon_lit(ParseContext *pc); static AstNode *ast_parse_asm_output(ParseContext *pc); static AsmOutput *ast_parse_asm_output_item(ParseContext *pc); static AstNode *ast_parse_asm_input(ParseContext *pc); @@ -92,6 +92,7 @@ static Token *ast_parse_block_label(ParseContext *pc); static AstNode *ast_parse_field_init(ParseContext *pc); static AstNode *ast_parse_while_continue_expr(ParseContext *pc); static AstNode *ast_parse_link_section(ParseContext *pc); +static AstNode *ast_parse_callconv(ParseContext *pc); static Optional ast_parse_fn_cc(ParseContext *pc); static AstNode *ast_parse_param_decl(ParseContext *pc); static AstNode *ast_parse_param_type(ParseContext *pc); @@ -140,16 +141,9 @@ static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { exit(EXIT_FAILURE); } -static Buf ast_token_str(Buf *input, Token *token) { - Buf str = BUF_INIT; - buf_init_from_mem(&str, buf_ptr(input) + token->start_pos, token->end_pos - token->start_pos); - return str; -} - ATTRIBUTE_NORETURN static void ast_invalid_token_error(ParseContext *pc, Token *token) { - Buf token_value = ast_token_str(pc->buf, token); - ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error(pc, token, "invalid token: '%s'", token_name(token->id)); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { @@ -212,7 +206,7 @@ static void put_back_token(ParseContext *pc) { static Buf *token_buf(Token *token) { if (token == nullptr) return nullptr; - assert(token->id == TokenIdStringLiteral || token->id == TokenIdSymbol); + assert(token->id == TokenIdStringLiteral || token->id == TokenIdMultilineStringLiteral || token->id == TokenIdSymbol); return &token->data.str_lit.str; } @@ -493,20 +487,59 @@ static AstNode *ast_parse_root(ParseContext *pc) { node->data.container_decl.layout = ContainerLayoutAuto; node->data.container_decl.kind = ContainerKindStruct; node->data.container_decl.is_root = true; + if (buf_len(&members.doc_comments) != 0) { + node->data.container_decl.doc_comments = members.doc_comments; + } return node; } +static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) { + Token *first_doc_token = nullptr; + Token *doc_token = nullptr; + while ((doc_token = eat_token_if(pc, TokenIdDocComment))) { + if (first_doc_token == nullptr) { + first_doc_token = doc_token; + } + if (buf->list.length == 0) { + buf_resize(buf, 0); + } + // chops off '///' but leaves '\n' + buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, + doc_token->end_pos - doc_token->start_pos - 3); + } + return first_doc_token; +} + +static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) { + if (buf_len(buf) != 0 && peek_token(pc)->id == TokenIdContainerDocComment) { + buf_append_char(buf, '\n'); + } + Token *doc_token = nullptr; + while ((doc_token = eat_token_if(pc, TokenIdContainerDocComment))) { + if (buf->list.length == 0) { + buf_resize(buf, 0); + } + // chops off '//!' but leaves '\n' + buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, + doc_token->end_pos - doc_token->start_pos - 3); + } +} + // ContainerMembers // <- TestDecl ContainerMembers // / TopLevelComptime ContainerMembers // / KEYWORD_pub? TopLevelDecl ContainerMembers -// / KEYWORD_pub? ContainerField COMMA ContainerMembers -// / KEYWORD_pub? ContainerField +// / KEYWORD_comptime? ContainerField COMMA ContainerMembers +// / KEYWORD_comptime? ContainerField // / static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { AstNodeContainerDecl res = {}; + Buf tld_doc_comment_buf = BUF_INIT; + buf_resize(&tld_doc_comment_buf, 0); for (;;) { + ast_parse_container_doc_comments(pc, &tld_doc_comment_buf); + AstNode *test_decl = ast_parse_test_decl(pc); if (test_decl != nullptr) { res.decls.append(test_decl); @@ -519,19 +552,29 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { continue; } + Buf doc_comment_buf = BUF_INIT; + ast_parse_doc_comments(pc, &doc_comment_buf); + Token *visib_token = eat_token_if(pc, TokenIdKeywordPub); VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate; - AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod); + AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf); if (top_level_decl != nullptr) { res.decls.append(top_level_decl); continue; } + if (visib_token != nullptr) { + ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); + } + + Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime); + AstNode *container_field = ast_parse_container_field(pc); if (container_field != nullptr) { assert(container_field->type == NodeTypeStructField); - container_field->data.struct_field.visib_mod = visib_mod; + container_field->data.struct_field.doc_comments = doc_comment_buf; + container_field->data.struct_field.comptime_token = comptime_token; res.fields.append(container_field); if (eat_token_if(pc, TokenIdComma) != nullptr) { continue; @@ -540,18 +583,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { } } - // We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet. - // It is therefore safe to return and let the caller continue parsing. - if (visib_token == nullptr) - break; - - ast_invalid_token_error(pc, peek_token(pc)); + break; } - + res.doc_comments = tld_doc_comment_buf; return res; } -// TestDecl <- KEYWORD_test STRINGLITERAL Block +// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block static AstNode *ast_parse_test_decl(ParseContext *pc) { Token *test = eat_token_if(pc, TokenIdKeywordTest); if (test == nullptr) @@ -571,6 +609,13 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { if (comptime == nullptr) return nullptr; + // 1 token lookahead because it could be a comptime struct field + Token *lbrace = peek_token(pc); + if (lbrace->id != TokenIdLBrace) { + put_back_token(pc); + return nullptr; + } + AstNode *block = ast_expect(pc, ast_parse_block_expr); AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); res->data.comptime_expr.expr = block; @@ -578,10 +623,10 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { } // TopLevelDecl -// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) -// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl +// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) +// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl // / KEYWORD_use Expr SEMICOLON -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) { Token *first = eat_token_if(pc, TokenIdKeywordExport); if (first == nullptr) first = eat_token_if(pc, TokenIdKeywordExtern); @@ -603,6 +648,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { var_decl->column = first->start_column; var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; var_decl->data.variable_declaration.visib_mod = visib_mod; + var_decl->data.variable_declaration.doc_comments = *doc_comments; var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern; var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport; var_decl->data.variable_declaration.lib_name = token_buf(lib_name); @@ -623,7 +669,10 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { fn_proto->line = first->start_line; fn_proto->column = first->start_column; fn_proto->data.fn_proto.visib_mod = visib_mod; - fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern; + fn_proto->data.fn_proto.doc_comments = *doc_comments; + // ast_parse_fn_cc may set it + if (!fn_proto->data.fn_proto.is_extern) + fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern; fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport; switch (first->id) { case TokenIdKeywordInline: @@ -657,6 +706,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { if (var_decl != nullptr) { assert(var_decl->type == NodeTypeVariableDeclaration); var_decl->data.variable_declaration.visib_mod = visib_mod; + var_decl->data.variable_declaration.doc_comments = *doc_comments; var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; return var_decl; } @@ -672,6 +722,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { assert(fn_proto->type == NodeTypeFnProto); fn_proto->data.fn_proto.visib_mod = visib_mod; + fn_proto->data.fn_proto.doc_comments = *doc_comments; AstNode *res = fn_proto; if (body != nullptr) { res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); @@ -706,7 +757,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { // The extern keyword for fn CC is also used for container decls. // We therefore put it back, as allow container decl to consume it // later. - if (fn_cc.cc == CallingConventionC) { + if (fn_cc.is_extern) { fn = eat_token_if(pc, TokenIdKeywordFn); if (fn == nullptr) { put_back_token(pc); @@ -729,6 +780,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { AstNode *align_expr = ast_parse_byte_align(pc); AstNode *section_expr = ast_parse_link_section(pc); + AstNode *callconv_expr = ast_parse_callconv(pc); Token *var = eat_token_if(pc, TokenIdKeywordVar); Token *exmark = nullptr; AstNode *return_type = nullptr; @@ -743,16 +795,11 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { res->data.fn_proto.params = params; res->data.fn_proto.align_expr = align_expr; res->data.fn_proto.section_expr = section_expr; + res->data.fn_proto.callconv_expr = callconv_expr; res->data.fn_proto.return_var_token = var; res->data.fn_proto.auto_err_set = exmark != nullptr; res->data.fn_proto.return_type = return_type; - // It seems that the Zig compiler expects varargs to be the - // last parameter in the decl list. This is not encoded in - // the grammar, which allows varargs anywhere in the decl. - // Since varargs is gonna be removed at some point, I'm not - // gonna encode this "varargs is always last" rule in the - // grammar, and just enforce it here, until varargs is removed. for (size_t i = 0; i < params.length; i++) { AstNode *param_decl = params.at(i); assert(param_decl->type == NodeTypeParamDecl); @@ -803,7 +850,12 @@ static AstNode *ast_parse_container_field(ParseContext *pc) { AstNode *type_expr = nullptr; if (eat_token_if(pc, TokenIdColon) != nullptr) { - type_expr = ast_expect(pc, ast_parse_type_expr); + Token *var_tok = eat_token_if(pc, TokenIdKeywordVar); + if (var_tok != nullptr) { + type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok); + } else { + type_expr = ast_expect(pc, ast_parse_type_expr); + } } AstNode *align_expr = ast_parse_byte_align(pc); AstNode *expr = nullptr; @@ -1577,9 +1629,9 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { if (container_decl != nullptr) return container_decl; - AstNode *enum_lit = ast_parse_enum_lit(pc); - if (enum_lit != nullptr) - return enum_lit; + AstNode *anon_lit = ast_parse_anon_lit(pc); + if (anon_lit != nullptr) + return anon_lit; AstNode *error_set_decl = ast_parse_error_set_decl(pc); if (error_set_decl != nullptr) @@ -1670,10 +1722,11 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { return ast_create_node(pc, NodeTypeUnreachable, unreachable); Token *string_lit = eat_token_if(pc, TokenIdStringLiteral); + if (string_lit == nullptr) + string_lit = eat_token_if(pc, TokenIdMultilineStringLiteral); if (string_lit != nullptr) { AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit); res->data.string_literal.buf = token_buf(string_lit); - res->data.string_literal.c = string_lit->data.str_lit.is_c_str; return res; } @@ -1719,11 +1772,20 @@ static AstNode *ast_parse_error_set_decl(ParseContext *pc) { } ZigList decls = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { + Buf doc_comment_buf = BUF_INIT; + Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf); Token *ident = eat_token_if(context, TokenIdSymbol); if (ident == nullptr) return (AstNode*)nullptr; - return token_symbol(context, ident); + AstNode *symbol_node = token_symbol(context, ident); + if (doc_token == nullptr) + return symbol_node; + + AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token); + field_node->data.err_set_field.field_name = symbol_node; + field_node->data.err_set_field.doc_comments = doc_comment_buf; + return field_node; }); expect_token(pc, TokenIdRBrace); @@ -1780,8 +1842,10 @@ static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { return loop; } - if (label != nullptr) - ast_invalid_token_error(pc, peek_token(pc)); + if (label != nullptr) { + put_back_token(pc); + put_back_token(pc); + } return nullptr; } @@ -1831,7 +1895,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) { Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile); expect_token(pc, TokenIdLParen); - Token *asm_template = expect_token(pc, TokenIdStringLiteral); + AstNode *asm_template = ast_expect(pc, ast_parse_expr); AstNode *res = ast_parse_asm_output(pc); if (res == nullptr) res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); @@ -1844,16 +1908,26 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) { return res; } -static AstNode *ast_parse_enum_lit(ParseContext *pc) { +static AstNode *ast_parse_anon_lit(ParseContext *pc) { Token *period = eat_token_if(pc, TokenIdDot); if (period == nullptr) return nullptr; - Token *identifier = expect_token(pc, TokenIdSymbol); - AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period); - res->data.enum_literal.period = period; - res->data.enum_literal.identifier = identifier; - return res; + // anon enum literal + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period); + res->data.enum_literal.period = period; + res->data.enum_literal.identifier = identifier; + return res; + } + + // anon container literal + AstNode *res = ast_parse_init_list(pc); + if (res != nullptr) + return res; + put_back_token(pc); + return nullptr; } // AsmOutput <- COLON AsmOutputList AsmInput? @@ -1872,17 +1946,15 @@ static AstNode *ast_parse_asm_output(ParseContext *pc) { // AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { - Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (sym_name == nullptr) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) { - return nullptr; - } else { - sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - } - } + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; - Token *str = expect_token(pc, TokenIdStringLiteral); + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + + Token *str = eat_token_if(pc, TokenIdMultilineStringLiteral); + if (str == nullptr) + str = expect_token(pc, TokenIdStringLiteral); expect_token(pc, TokenIdLParen); Token *var_name = eat_token_if(pc, TokenIdSymbol); @@ -1895,7 +1967,7 @@ static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { expect_token(pc, TokenIdRParen); AsmOutput *res = allocate(1); - res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : token_buf(sym_name); + res->asm_symbolic_name = token_buf(sym_name); res->constraint = token_buf(str); res->variable_name = token_buf(var_name); res->return_type = return_type; @@ -1918,23 +1990,21 @@ static AstNode *ast_parse_asm_input(ParseContext *pc) { // AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { - Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (sym_name == nullptr) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) { - return nullptr; - } else { - sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - } - } + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; - Token *constraint = expect_token(pc, TokenIdStringLiteral); + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + + Token *constraint = eat_token_if(pc, TokenIdMultilineStringLiteral); + if (constraint == nullptr) + constraint = expect_token(pc, TokenIdStringLiteral); expect_token(pc, TokenIdLParen); AstNode *expr = ast_expect(pc, ast_parse_expr); expect_token(pc, TokenIdRParen); AsmInput *res = allocate(1); - res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : token_buf(sym_name); + res->asm_symbolic_name = token_buf(sym_name); res->constraint = token_buf(constraint); res->expr = expr; return res; @@ -1947,6 +2017,8 @@ static AstNode *ast_parse_asm_clobbers(ParseContext *pc) { ZigList clobber_list = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { Token *str = eat_token_if(context, TokenIdStringLiteral); + if (str == nullptr) + str = eat_token_if(context, TokenIdMultilineStringLiteral); if (str != nullptr) return token_buf(str); return (Buf*)nullptr; @@ -1987,7 +2059,12 @@ static AstNode *ast_parse_field_init(ParseContext *pc) { if (first == nullptr) return nullptr; - Token *name = expect_token(pc, TokenIdSymbol); + Token *name = eat_token_if(pc, TokenIdSymbol); + if (name == nullptr) { + // Because of anon literals ".{" is also valid. + put_back_token(pc); + return nullptr; + } if (eat_token_if(pc, TokenIdEq) == nullptr) { // Because ".Name" can also be intepreted as an enum literal, we should put back // those two tokens again so that the parser can try to parse them as the enum @@ -2028,27 +2105,29 @@ static AstNode *ast_parse_link_section(ParseContext *pc) { return res; } +// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN +static AstNode *ast_parse_callconv(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordCallconv); + if (first == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; +} + // FnCC -// <- KEYWORD_nakedcc -// / KEYWORD_stdcallcc -// / KEYWORD_extern +// <- KEYWORD_extern // / KEYWORD_async static Optional ast_parse_fn_cc(ParseContext *pc) { AstNodeFnProto res = {}; - if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) { - res.cc = CallingConventionNaked; - return Optional::some(res); - } - if (eat_token_if(pc, TokenIdKeywordStdcallCC) != nullptr) { - res.cc = CallingConventionStdcall; + if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) { + res.is_async = true; return Optional::some(res); } if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) { - res.cc = CallingConventionC; - return Optional::some(res); - } - if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) { - res.cc = CallingConventionAsync; + res.is_extern = true; return Optional::some(res); } @@ -2057,6 +2136,9 @@ static Optional ast_parse_fn_cc(ParseContext *pc) { // ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType static AstNode *ast_parse_param_decl(ParseContext *pc) { + Buf doc_comments = BUF_INIT; + ast_parse_doc_comments(pc, &doc_comments); + Token *first = eat_token_if(pc, TokenIdKeywordNoAlias); if (first == nullptr) first = eat_token_if(pc, TokenIdKeywordCompTime); @@ -2089,6 +2171,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) { res->line = first->start_line; res->column = first->start_column; res->data.param_decl.name = token_buf(name); + res->data.param_decl.doc_comments = doc_comments; res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias; res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime; return res; @@ -2545,37 +2628,28 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { put_back_token(pc); } - AstNode *array = ast_parse_array_type_start(pc); - if (array != nullptr) { - assert(array->type == NodeTypeArrayType); - while (true) { - Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); - if (allowzero_token != nullptr) { - array->data.array_type.allow_zero_token = allowzero_token; - continue; + Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket); + if (arr_init_lbracket != nullptr) { + Token *underscore = eat_token_if(pc, TokenIdSymbol); + if (underscore == nullptr) { + put_back_token(pc); + } else if (!buf_eql_str(token_buf(underscore), "_")) { + put_back_token(pc); + put_back_token(pc); + } else { + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); } - - AstNode *align_expr = ast_parse_byte_align(pc); - if (align_expr != nullptr) { - array->data.array_type.align_expr = align_expr; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { - array->data.array_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { - array->data.array_type.is_volatile = true; - continue; - } - break; + expect_token(pc, TokenIdRBracket); + AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket); + node->data.inferred_array_type.sentinel = sentinel; + return node; } - - return array; } + AstNode *ptr = ast_parse_ptr_type_start(pc); if (ptr != nullptr) { assert(ptr->type == NodeTypePointerType); @@ -2621,9 +2695,35 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { return ptr; } - Token *arr_init = eat_token_if(pc, TokenIdBracketUnderscoreBracket); - if (arr_init != nullptr) { - return ast_create_node(pc, NodeTypeInferredArrayType, arr_init); + AstNode *array = ast_parse_array_type_start(pc); + if (array != nullptr) { + assert(array->type == NodeTypeArrayType); + while (true) { + Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); + if (allowzero_token != nullptr) { + array->data.array_type.allow_zero_token = allowzero_token; + continue; + } + + AstNode *align_expr = ast_parse_byte_align(pc); + if (align_expr != nullptr) { + array->data.array_type.align_expr = align_expr; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + array->data.array_type.is_const = true; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + array->data.array_type.is_volatile = true; + continue; + } + break; + } + + return array; } @@ -2631,7 +2731,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { } // SuffixOp -// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET +// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET // / DOT IDENTIFIER // / DOTASTERISK // / DOTQUESTIONMARK @@ -2641,12 +2741,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) { AstNode *start = ast_expect(pc, ast_parse_expr); AstNode *end = nullptr; if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) { + AstNode *sentinel = nullptr; end = ast_parse_expr(pc); + if (eat_token_if(pc, TokenIdColon) != nullptr) { + sentinel = ast_parse_expr(pc); + } expect_token(pc, TokenIdRBracket); AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); res->data.slice_expr.start = start; res->data.slice_expr.end = end; + res->data.slice_expr.sentinel = sentinel; return res; } @@ -2657,10 +2762,12 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) { return res; } + Token *dot_asterisk = eat_token_if(pc, TokenIdDotStar); + if (dot_asterisk != nullptr) + return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk); + Token *dot = eat_token_if(pc, TokenIdDot); if (dot != nullptr) { - if (eat_token_if(pc, TokenIdStar) != nullptr) - return ast_create_node(pc, NodeTypePtrDeref, dot); if (eat_token_if(pc, TokenIdQuestion) != nullptr) return ast_create_node(pc, NodeTypeUnwrapOptional, dot); @@ -2695,9 +2802,15 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { return nullptr; AstNode *size = ast_parse_expr(pc); + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } expect_token(pc, TokenIdRBracket); AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); res->data.array_type.size = size; + res->data.array_type.sentinel = sentinel; return res; } @@ -2707,35 +2820,63 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // / PTRUNKNOWN // / PTRC static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { + AstNode *sentinel = nullptr; + Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); res->data.pointer_type.star_token = asterisk; + res->data.pointer_type.sentinel = sentinel; return res; } Token *asterisk2 = eat_token_if(pc, TokenIdStarStar); if (asterisk2 != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); res->data.pointer_type.star_token = asterisk2; res2->data.pointer_type.star_token = asterisk2; + res2->data.pointer_type.sentinel = sentinel; res->data.pointer_type.op_expr = res2; return res; } - Token *multptr = eat_token_if(pc, TokenIdBracketStarBracket); - if (multptr != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypePointerType, multptr); - res->data.pointer_type.star_token = multptr; - return res; - } + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket != nullptr) { + Token *star = eat_token_if(pc, TokenIdStar); + if (star == nullptr) { + put_back_token(pc); + } else { + Token *c_tok = eat_token_if(pc, TokenIdSymbol); + if (c_tok != nullptr) { + if (!buf_eql_str(token_buf(c_tok), "c")) { + put_back_token(pc); // c symbol + } else { + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = c_tok; + return res; + } + } - Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket); - if (cptr != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr); - res->data.pointer_type.star_token = cptr; - return res; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = lbracket; + res->data.pointer_type.sentinel = sentinel; + return res; + } } return nullptr; @@ -2753,6 +2894,9 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { res->data.container_decl.fields = members.fields; res->data.container_decl.decls = members.decls; + if (buf_len(&members.doc_comments) != 0) { + res->data.container_decl.doc_comments = members.doc_comments; + } return res; } @@ -2910,6 +3054,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.slice_expr.array_ref_expr, visit, context); visit_field(&node->data.slice_expr.start, visit, context); visit_field(&node->data.slice_expr.end, visit, context); + visit_field(&node->data.slice_expr.sentinel, visit, context); break; case NodeTypeFieldAccessExpr: visit_field(&node->data.field_access_expr.struct_expr, visit, context); @@ -3010,10 +3155,12 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeArrayType: visit_field(&node->data.array_type.size, visit, context); + visit_field(&node->data.array_type.sentinel, visit, context); visit_field(&node->data.array_type.child_type, visit, context); visit_field(&node->data.array_type.align_expr, visit, context); break; case NodeTypeInferredArrayType: + visit_field(&node->data.array_type.sentinel, visit, context); visit_field(&node->data.array_type.child_type, visit, context); break; case NodeTypeAnyFrameType: @@ -3023,12 +3170,16 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont // none break; case NodeTypePointerType: + visit_field(&node->data.pointer_type.sentinel, visit, context); visit_field(&node->data.pointer_type.align_expr, visit, context); visit_field(&node->data.pointer_type.op_expr, visit, context); break; case NodeTypeErrorSetDecl: visit_node_list(&node->data.err_set_decl.decls, visit, context); break; + case NodeTypeErrorSetField: + visit_field(&node->data.err_set_field.field_name, visit, context); + break; case NodeTypeResume: visit_field(&node->data.resume_expr.expr, visit, context); break; @@ -3039,6 +3190,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.suspend.block, visit, context); break; case NodeTypeEnumLiteral: + case NodeTypeVarFieldType: break; } } diff --git a/src/range_set.cpp b/src/range_set.cpp index fd0076191..9e621d2f1 100644 --- a/src/range_set.cpp +++ b/src/range_set.cpp @@ -40,6 +40,9 @@ void rangeset_sort(RangeSet *rs) { } bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) { + if (rs->src_range_list.length == 0) + return false; + rangeset_sort(rs); const Range *first_range = &rs->src_range_list.at(0).range; diff --git a/src/stack_report.cpp b/src/stack_report.cpp deleted file mode 100644 index bd5a43631..000000000 --- a/src/stack_report.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "stack_report.hpp" - -static void tree_print(FILE *f, ZigType *ty, size_t indent); - -static void pretty_print_bytes(FILE *f, double n) { - if (n > 1024.0 * 1024.0 * 1024.0) { - fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); - return; - } - if (n > 1024.0 * 1024.0) { - fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); - return; - } - if (n > 1024.0) { - fprintf(f, "%.02f KiB", n / 1024.0); - return; - } - fprintf(f, "%.02f bytes", n ); - return; -} - -static int compare_type_abi_sizes_desc(const void *a, const void *b) { - uint64_t size_a = (*(ZigType * const*)(a))->abi_size; - uint64_t size_b = (*(ZigType * const*)(b))->abi_size; - if (size_a > size_b) - return -1; - if (size_a < size_b) - return 1; - return 0; -} - -static void start_child(FILE *f, size_t indent) { - fprintf(f, "\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void start_peer(FILE *f, size_t indent) { - fprintf(f, ",\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { - ZigList children = {}; - uint64_t sum_from_fields = 0; - for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; - children.append(field->type_entry); - sum_from_fields += field->type_entry->abi_size; - } - qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); - - start_peer(f, indent); - fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); - - start_peer(f, indent); - fprintf(f, "\"fields\": ["); - - for (size_t i = 0; i < children.length; i += 1) { - if (i == 0) { - start_child(f, indent + 1); - } else { - start_peer(f, indent + 1); - } - fprintf(f, "{"); - - ZigType *child_type = children.at(i); - tree_print(f, child_type, indent + 2); - - start_child(f, indent + 1); - fprintf(f, "}"); - } - - start_child(f, indent); - fprintf(f, "]"); -} - -static void tree_print(FILE *f, ZigType *ty, size_t indent) { - start_child(f, indent); - fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); - - start_peer(f, indent); - fprintf(f, "\"sizef\": \""); - pretty_print_bytes(f, ty->abi_size); - fprintf(f, "\""); - - start_peer(f, indent); - fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); - - switch (ty->id) { - case ZigTypeIdFnFrame: - return tree_print_struct(f, ty->data.frame.locals_struct, indent); - case ZigTypeIdStruct: - return tree_print_struct(f, ty, indent); - default: - start_child(f, indent); - return; - } -} - -void zig_print_stack_report(CodeGen *g, FILE *f) { - if (g->largest_frame_fn == nullptr) { - fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); - return; - } - fprintf(f, "{"); - tree_print(f, g->largest_frame_fn->frame_type, 1); - - start_child(f, 0); - fprintf(f, "}\n"); -} diff --git a/src/target.cpp b/src/target.cpp index 1cab9253a..82d5467e2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -179,8 +179,8 @@ static const Os os_list[] = { OsHurd, OsWASI, OsEmscripten, - OsZen, OsUefi, + OsOther, }; // Coordinate with zig_llvm.h @@ -269,7 +269,7 @@ Os target_os_enum(size_t index) { ZigLLVM_OSType get_llvm_os_type(Os os_type) { switch (os_type) { case OsFreestanding: - case OsZen: + case OsOther: return ZigLLVM_UnknownOS; case OsAnanas: return ZigLLVM_Ananas; @@ -425,10 +425,10 @@ const char *target_os_name(Os os_type) { switch (os_type) { case OsFreestanding: return "freestanding"; - case OsZen: - return "zen"; case OsUefi: return "uefi"; + case OsOther: + return "other"; case OsAnanas: case OsCloudABI: case OsDragonFly: @@ -1009,6 +1009,7 @@ uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch) { uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { switch (target->os) { case OsFreestanding: + case OsOther: switch (target->arch) { case ZigLLVM_msp430: switch (id) { @@ -1047,9 +1048,9 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { } case OsLinux: case OsMacOSX: - case OsZen: case OsFreeBSD: case OsNetBSD: + case OsDragonFly: case OsOpenBSD: case OsWASI: case OsEmscripten: @@ -1104,7 +1105,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { } case OsAnanas: case OsCloudABI: - case OsDragonFly: case OsKFreeBSD: case OsLv2: case OsSolaris: @@ -1139,7 +1139,8 @@ bool target_allows_addr_zero(const ZigTarget *target) { const char *target_o_file_ext(const ZigTarget *target) { if (target->abi == ZigLLVM_MSVC || (target->os == OsWindows && !target_abi_is_gnu(target->abi)) || - target->os == OsUefi) { + target->os == OsUefi) + { return ".obj"; } else { return ".o"; @@ -1267,6 +1268,8 @@ const char *target_dynamic_linker(const ZigTarget *target) { return "/libexec/ld-elf.so.1"; case OsNetBSD: return "/libexec/ld.elf_so"; + case OsDragonFly: + return "/libexec/ld-elf.so.2"; case OsLinux: { const ZigLLVM_EnvironmentType abi = target->abi; switch (target->arch) { @@ -1379,11 +1382,11 @@ const char *target_dynamic_linker(const ZigTarget *target) { case OsUefi: case OsWindows: case OsEmscripten: + case OsOther: return nullptr; case OsAnanas: case OsCloudABI: - case OsDragonFly: case OsFuchsia: case OsKFreeBSD: case OsLv2: @@ -1403,7 +1406,6 @@ const char *target_dynamic_linker(const ZigTarget *target) { case OsMesa3D: case OsContiki: case OsAMDPAL: - case OsZen: case OsHermitCore: case OsHurd: case OsWASI: @@ -1457,6 +1459,10 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_mipsel: return "sp"; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + return nullptr; // known to be not available + case ZigLLVM_amdgcn: case ZigLLVM_amdil: case ZigLLVM_amdil64: @@ -1490,8 +1496,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_systemz: case ZigLLVM_tce: case ZigLLVM_tcele: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: case ZigLLVM_ppc: case ZigLLVM_ppc64: @@ -1579,19 +1583,33 @@ bool target_os_requires_libc(Os os) { // On Darwin, we always link libSystem which contains libc. // Similarly on FreeBSD and NetBSD we always link system libc // since this is the stable syscall interface. - return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD); + return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD || os == OsDragonFly); } bool target_supports_fpic(const ZigTarget *target) { - // This is not whether the target supports Position Independent Code, but whether the -fPIC - // C compiler argument is valid. - return target->os != OsWindows; + // This is not whether the target supports Position Independent Code, but whether the -fPIC + // C compiler argument is valid. + return target->os != OsWindows; +} + +bool target_supports_clang_march_native(const ZigTarget *target) { + // Whether clang supports -march=native on this target. + // Arguably it should always work, but in reality it gives: + // error: the clang compiler does not support '-march=native' + // If we move CPU detection logic into Zig itelf, we will not need this, + // instead we will always pass target features and CPU configuration explicitly. + return target->arch != ZigLLVM_aarch64 && + target->arch != ZigLLVM_aarch64_be; } bool target_supports_stack_probing(const ZigTarget *target) { return target->os != OsWindows && target->os != OsUefi && (target->arch == ZigLLVM_x86 || target->arch == ZigLLVM_x86_64); } +bool target_supports_sanitize_c(const ZigTarget *target) { + return true; +} + bool target_requires_pic(const ZigTarget *target, bool linking_libc) { // This function returns whether non-pic code is completely invalid on the given target. return target_is_android(target) || target->os == OsWindows || target->os == OsUefi || target_os_requires_libc(target->os) || @@ -1626,7 +1644,6 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsFreestanding: case OsAnanas: case OsCloudABI: - case OsDragonFly: case OsLv2: case OsSolaris: case OsHaiku: @@ -1643,8 +1660,8 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsMesa3D: case OsContiki: case OsAMDPAL: - case OsZen: case OsHermitCore: + case OsOther: return ZigLLVM_EABI; case OsOpenBSD: case OsMacOSX: @@ -1655,6 +1672,7 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsFuchsia: case OsKFreeBSD: case OsNetBSD: + case OsDragonFly: case OsHurd: return ZigLLVM_GNU; case OsUefi: diff --git a/src/target.hpp b/src/target.hpp index f4c638f19..50611bc85 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -49,8 +49,8 @@ enum Os { OsHurd, OsWASI, OsEmscripten, - OsZen, OsUefi, + OsOther, }; // Synchronize with target.cpp::subarch_list_list @@ -182,6 +182,7 @@ bool target_can_build_libc(const ZigTarget *target); const char *target_libc_generic_name(const ZigTarget *target); bool target_is_libc_lib_name(const ZigTarget *target, const char *name); bool target_supports_fpic(const ZigTarget *target); +bool target_supports_clang_march_native(const ZigTarget *target); bool target_requires_pic(const ZigTarget *target, bool linking_libc); bool target_requires_pie(const ZigTarget *target); bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi); @@ -193,6 +194,7 @@ bool target_is_riscv(const ZigTarget *target); bool target_is_android(const ZigTarget *target); bool target_is_single_threaded(const ZigTarget *target); bool target_supports_stack_probing(const ZigTarget *target); +bool target_supports_sanitize_c(const ZigTarget *target); bool target_has_debug_info(const ZigTarget *target); const char *target_arch_musl_name(ZigLLVM_ArchType arch); bool target_supports_libunwind(const ZigTarget *target); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 71a24fe72..2aae048cd 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -33,10 +33,10 @@ '0': \ case DIGIT_NON_ZERO -#define ALPHA_EXCEPT_C \ +#define ALPHA \ 'a': \ case 'b': \ - /*case 'c':*/ \ + case 'c': \ case 'd': \ case 'e': \ case 'f': \ @@ -87,10 +87,6 @@ case 'Y': \ case 'Z' -#define ALPHA \ - ALPHA_EXCEPT_C: \ - case 'c' - #define SYMBOL_CHAR \ ALPHA: \ case DIGIT: \ @@ -114,6 +110,7 @@ static const struct ZigKeyword zig_keywords[] = { {"async", TokenIdKeywordAsync}, {"await", TokenIdKeywordAwait}, {"break", TokenIdKeywordBreak}, + {"callconv", TokenIdKeywordCallconv}, {"catch", TokenIdKeywordCatch}, {"comptime", TokenIdKeywordCompTime}, {"const", TokenIdKeywordConst}, @@ -130,7 +127,6 @@ static const struct ZigKeyword zig_keywords[] = { {"for", TokenIdKeywordFor}, {"if", TokenIdKeywordIf}, {"inline", TokenIdKeywordInline}, - {"nakedcc", TokenIdKeywordNakedCC}, {"noalias", TokenIdKeywordNoAlias}, {"noasync", TokenIdKeywordNoAsync}, {"noinline", TokenIdKeywordNoInline}, @@ -142,7 +138,6 @@ static const struct ZigKeyword zig_keywords[] = { {"resume", TokenIdKeywordResume}, {"return", TokenIdKeywordReturn}, {"linksection", TokenIdKeywordLinkSection}, - {"stdcallcc", TokenIdKeywordStdcallCC}, {"struct", TokenIdKeywordStruct}, {"suspend", TokenIdKeywordSuspend}, {"switch", TokenIdKeywordSwitch}, @@ -153,7 +148,6 @@ static const struct ZigKeyword zig_keywords[] = { {"undefined", TokenIdKeywordUndefined}, {"union", TokenIdKeywordUnion}, {"unreachable", TokenIdKeywordUnreachable}, - {"use", TokenIdKeywordUsingNamespace}, {"usingnamespace", TokenIdKeywordUsingNamespace}, {"var", TokenIdKeywordVar}, {"volatile", TokenIdKeywordVolatile}, @@ -181,7 +175,6 @@ static bool is_symbol_char(uint8_t c) { enum TokenizeState { TokenizeStateStart, TokenizeStateSymbol, - TokenizeStateSymbolFirstC, TokenizeStateZero, // "0", which might lead to "0x" TokenizeStateNumber, // "123", "0x123" TokenizeStateNumberDot, @@ -193,9 +186,13 @@ enum TokenizeState { TokenizeStateStringEscapeUnicodeStart, TokenizeStateCharLiteral, TokenizeStateCharLiteralEnd, + TokenizeStateCharLiteralUnicode, TokenizeStateSawStar, TokenizeStateSawStarPercent, TokenizeStateSawSlash, + TokenizeStateSawSlash2, + TokenizeStateSawSlash3, + TokenizeStateSawSlashBang, TokenizeStateSawBackslash, TokenizeStateSawPercent, TokenizeStateSawPlus, @@ -206,11 +203,12 @@ enum TokenizeState { TokenizeStateSawCaret, TokenizeStateSawBar, TokenizeStateSawBarBar, + TokenizeStateDocComment, + TokenizeStateContainerDocComment, TokenizeStateLineComment, TokenizeStateLineString, TokenizeStateLineStringEnd, TokenizeStateLineStringContinue, - TokenizeStateLineStringContinueC, TokenizeStateSawEq, TokenizeStateSawBang, TokenizeStateSawLessThan, @@ -222,10 +220,6 @@ enum TokenizeState { TokenizeStateSawAtSign, TokenizeStateCharCode, TokenizeStateError, - TokenizeStateLBracket, - TokenizeStateLBracketStar, - TokenizeStateLBracketStarC, - TokenizeStateLBracketUnderscore, }; @@ -247,6 +241,7 @@ struct Tokenize { int exponent_in_bin_or_dec; BigInt specified_exponent; BigInt significand; + size_t remaining_code_units; }; ATTRIBUTE_PRINTF(2, 3) @@ -270,10 +265,9 @@ static void set_token_id(Tokenize *t, Token *token, TokenId id) { } else if (id == TokenIdFloatLiteral) { bigfloat_init_32(&token->data.float_lit.bigfloat, 0.0f); token->data.float_lit.overflow = false; - } else if (id == TokenIdStringLiteral || id == TokenIdSymbol) { + } else if (id == TokenIdStringLiteral || id == TokenIdMultilineStringLiteral || id == TokenIdSymbol) { memset(&token->data.str_lit.str, 0, sizeof(Buf)); buf_resize(&token->data.str_lit.str, 0); - token->data.str_lit.is_c_str = false; } } @@ -423,12 +417,7 @@ void tokenize(Buf *buf, Tokenization *out) { switch (c) { case WHITESPACE: break; - case 'c': - t.state = TokenizeStateSymbolFirstC; - begin_token(&t, TokenIdSymbol); - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - case ALPHA_EXCEPT_C: + case ALPHA: case '_': t.state = TokenizeStateSymbol; begin_token(&t, TokenIdSymbol); @@ -485,8 +474,8 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); break; case '[': - t.state = TokenizeStateLBracket; begin_token(&t, TokenIdLBracket); + end_token(&t); break; case ']': begin_token(&t, TokenIdRBracket); @@ -513,7 +502,7 @@ void tokenize(Buf *buf, Tokenization *out) { t.state = TokenizeStateSawSlash; break; case '\\': - begin_token(&t, TokenIdStringLiteral); + begin_token(&t, TokenIdMultilineStringLiteral); t.state = TokenizeStateSawBackslash; break; case '%': @@ -578,6 +567,11 @@ void tokenize(Buf *buf, Tokenization *out) { t.state = TokenizeStateSawDotDot; set_token_id(&t, t.cur_tok, TokenIdEllipsis2); break; + case '*': + t.state = TokenizeStateStart; + set_token_id(&t, t.cur_tok, TokenIdDotStar); + end_token(&t); + break; default: t.pos -= 1; end_token(&t); @@ -775,62 +769,6 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateLBracket: - switch (c) { - case '*': - t.state = TokenizeStateLBracketStar; - break; - case '_': - t.state = TokenizeStateLBracketUnderscore; - break; - default: - // reinterpret as just an lbracket - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLBracketUnderscore: - switch (c) { - case ']': - set_token_id(&t, t.cur_tok, TokenIdBracketUnderscoreBracket); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - // reinterpret as just an lbracket - t.pos -= 2; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLBracketStar: - switch (c) { - case 'c': - t.state = TokenizeStateLBracketStarC; - set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket); - break; - case ']': - set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateLBracketStarC: - switch (c) { - case ']': - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - invalid_char_error(&t, c); - } - break; case TokenizeStateSawPlusPercent: switch (c) { case '=': @@ -910,8 +848,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawSlash: switch (c) { case '/': - cancel_token(&t); - t.state = TokenizeStateLineComment; + t.state = TokenizeStateSawSlash2; break; case '=': set_token_id(&t, t.cur_tok, TokenIdDivEq); @@ -925,6 +862,54 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateSawSlash2: + switch (c) { + case '/': + t.state = TokenizeStateSawSlash3; + break; + case '!': + t.state = TokenizeStateSawSlashBang; + break; + case '\n': + cancel_token(&t); + t.state = TokenizeStateStart; + break; + default: + cancel_token(&t); + t.state = TokenizeStateLineComment; + break; + } + break; + case TokenizeStateSawSlash3: + switch (c) { + case '/': + cancel_token(&t); + t.state = TokenizeStateLineComment; + break; + case '\n': + set_token_id(&t, t.cur_tok, TokenIdDocComment); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + set_token_id(&t, t.cur_tok, TokenIdDocComment); + t.state = TokenizeStateDocComment; + break; + } + break; + case TokenizeStateSawSlashBang: + switch (c) { + case '\n': + set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); + t.state = TokenizeStateContainerDocComment; + break; + } + break; case TokenizeStateSawBackslash: switch (c) { case '\\': @@ -949,30 +934,6 @@ void tokenize(Buf *buf, Tokenization *out) { switch (c) { case WHITESPACE: break; - case 'c': - if (!t.cur_tok->data.str_lit.is_c_str) { - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - break; - } - t.state = TokenizeStateLineStringContinueC; - break; - case '\\': - if (t.cur_tok->data.str_lit.is_c_str) { - invalid_char_error(&t, c); - } - t.state = TokenizeStateLineStringContinue; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLineStringContinueC: - switch (c) { case '\\': t.state = TokenizeStateLineStringContinue; break; @@ -1004,27 +965,26 @@ void tokenize(Buf *buf, Tokenization *out) { break; } break; - case TokenizeStateSymbolFirstC: + case TokenizeStateDocComment: switch (c) { - case '"': - set_token_id(&t, t.cur_tok, TokenIdStringLiteral); - t.cur_tok->data.str_lit.is_c_str = true; - t.state = TokenizeStateString; - break; - case '\\': - set_token_id(&t, t.cur_tok, TokenIdStringLiteral); - t.cur_tok->data.str_lit.is_c_str = true; - t.state = TokenizeStateSawBackslash; - break; - case SYMBOL_CHAR: - t.state = TokenizeStateSymbol; - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - default: - t.pos -= 1; + case '\n': end_token(&t); t.state = TokenizeStateStart; - continue; + break; + default: + // do nothing + break; + } + break; + case TokenizeStateContainerDocComment: + switch (c) { + case '\n': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + // do nothing + break; } break; case TokenizeStateSawAtSign: @@ -1176,17 +1136,32 @@ void tokenize(Buf *buf, Tokenization *out) { } break; case TokenizeStateCharLiteral: - switch (c) { - case '\'': - tokenize_error(&t, "expected character"); - break; - case '\\': - t.state = TokenizeStateStringEscape; - break; - default: - t.cur_tok->data.char_lit.c = c; - t.state = TokenizeStateCharLiteralEnd; - break; + if (c == '\'') { + tokenize_error(&t, "expected character"); + } else if (c == '\\') { + t.state = TokenizeStateStringEscape; + } else if ((c >= 0x80 && c <= 0xbf) || c >= 0xf8) { + // 10xxxxxx + // 11111xxx + invalid_char_error(&t, c); + } else if (c >= 0xc0 && c <= 0xdf) { + // 110xxxxx + t.cur_tok->data.char_lit.c = c & 0x1f; + t.remaining_code_units = 1; + t.state = TokenizeStateCharLiteralUnicode; + } else if (c >= 0xe0 && c <= 0xef) { + // 1110xxxx + t.cur_tok->data.char_lit.c = c & 0x0f; + t.remaining_code_units = 2; + t.state = TokenizeStateCharLiteralUnicode; + } else if (c >= 0xf0 && c <= 0xf7) { + // 11110xxx + t.cur_tok->data.char_lit.c = c & 0x07; + t.remaining_code_units = 3; + t.state = TokenizeStateCharLiteralUnicode; + } else { + t.cur_tok->data.char_lit.c = c; + t.state = TokenizeStateCharLiteralEnd; } break; case TokenizeStateCharLiteralEnd: @@ -1199,6 +1174,17 @@ void tokenize(Buf *buf, Tokenization *out) { invalid_char_error(&t, c); } break; + case TokenizeStateCharLiteralUnicode: + if (c <= 0x7f || c >= 0xc0) { + invalid_char_error(&t, c); + } + t.cur_tok->data.char_lit.c <<= 6; + t.cur_tok->data.char_lit.c += c & 0x3f; + t.remaining_code_units--; + if (t.remaining_code_units == 0) { + t.state = TokenizeStateCharLiteralEnd; + } + break; case TokenizeStateZero: switch (c) { case 'b': @@ -1434,10 +1420,10 @@ void tokenize(Buf *buf, Tokenization *out) { break; case TokenizeStateCharLiteral: case TokenizeStateCharLiteralEnd: + case TokenizeStateCharLiteralUnicode: tokenize_error(&t, "unterminated character literal"); break; case TokenizeStateSymbol: - case TokenizeStateSymbolFirstC: case TokenizeStateZero: case TokenizeStateNumber: case TokenizeStateFloatFraction: @@ -1465,19 +1451,19 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineString: case TokenizeStateLineStringEnd: case TokenizeStateSawBarBar: - case TokenizeStateLBracket: + case TokenizeStateDocComment: + case TokenizeStateContainerDocComment: end_token(&t); break; case TokenizeStateSawDotDot: case TokenizeStateSawBackslash: case TokenizeStateLineStringContinue: - case TokenizeStateLineStringContinueC: - case TokenizeStateLBracketStar: - case TokenizeStateLBracketStarC: - case TokenizeStateLBracketUnderscore: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: + case TokenizeStateSawSlash2: + case TokenizeStateSawSlash3: + case TokenizeStateSawSlashBang: break; } if (t.state != TokenizeStateError) { @@ -1511,8 +1497,6 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; - case TokenIdBracketStarBracket: return "[*]"; - case TokenIdBracketStarCBracket: return "[*c]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; @@ -1524,7 +1508,10 @@ const char * token_name(TokenId id) { case TokenIdComma: return ","; case TokenIdDash: return "-"; case TokenIdDivEq: return "/="; + case TokenIdDocComment: return "DocComment"; + case TokenIdContainerDocComment: return "ContainerDocComment"; case TokenIdDot: return "."; + case TokenIdDotStar: return ".*"; case TokenIdEllipsis2: return ".."; case TokenIdEllipsis3: return "..."; case TokenIdEof: return "EOF"; @@ -1543,6 +1530,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordAsm: return "asm"; case TokenIdKeywordBreak: return "break"; case TokenIdKeywordCatch: return "catch"; + case TokenIdKeywordCallconv: return "callconv"; case TokenIdKeywordCompTime: return "comptime"; case TokenIdKeywordConst: return "const"; case TokenIdKeywordContinue: return "continue"; @@ -1558,7 +1546,6 @@ const char * token_name(TokenId id) { case TokenIdKeywordFor: return "for"; case TokenIdKeywordIf: return "if"; case TokenIdKeywordInline: return "inline"; - case TokenIdKeywordNakedCC: return "nakedcc"; case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordNoAsync: return "noasync"; case TokenIdKeywordNoInline: return "noinline"; @@ -1569,7 +1556,6 @@ const char * token_name(TokenId id) { case TokenIdKeywordPub: return "pub"; case TokenIdKeywordReturn: return "return"; case TokenIdKeywordLinkSection: return "linksection"; - case TokenIdKeywordStdcallCC: return "stdcallcc"; case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordTest: return "test"; @@ -1607,13 +1593,13 @@ const char * token_name(TokenId id) { case TokenIdStar: return "*"; case TokenIdStarStar: return "**"; case TokenIdStringLiteral: return "StringLiteral"; + case TokenIdMultilineStringLiteral: return "MultilineStringLiteral"; case TokenIdSymbol: return "Symbol"; case TokenIdTilde: return "~"; case TokenIdTimesEq: return "*="; case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercentEq: return "*%="; case TokenIdBarBarEq: return "||="; - case TokenIdBracketUnderscoreBracket: return "[_]"; case TokenIdCount: zig_unreachable(); } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index a3d1a6000..3f025ca74 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -28,9 +28,6 @@ enum TokenId { TokenIdBitShiftRight, TokenIdBitShiftRightEq, TokenIdBitXorEq, - TokenIdBracketStarBracket, - TokenIdBracketStarCBracket, - TokenIdBracketUnderscoreBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, @@ -42,7 +39,10 @@ enum TokenId { TokenIdComma, TokenIdDash, TokenIdDivEq, + TokenIdDocComment, + TokenIdContainerDocComment, TokenIdDot, + TokenIdDotStar, TokenIdEllipsis2, TokenIdEllipsis3, TokenIdEof, @@ -59,6 +59,7 @@ enum TokenId { TokenIdKeywordAwait, TokenIdKeywordBreak, TokenIdKeywordCatch, + TokenIdKeywordCallconv, TokenIdKeywordCompTime, TokenIdKeywordConst, TokenIdKeywordContinue, @@ -76,7 +77,6 @@ enum TokenId { TokenIdKeywordInline, TokenIdKeywordNoInline, TokenIdKeywordLinkSection, - TokenIdKeywordNakedCC, TokenIdKeywordNoAlias, TokenIdKeywordNoAsync, TokenIdKeywordNull, @@ -86,7 +86,6 @@ enum TokenId { TokenIdKeywordPub, TokenIdKeywordResume, TokenIdKeywordReturn, - TokenIdKeywordStdcallCC, TokenIdKeywordStruct, TokenIdKeywordSuspend, TokenIdKeywordSwitch, @@ -125,6 +124,7 @@ enum TokenId { TokenIdStar, TokenIdStarStar, TokenIdStringLiteral, + TokenIdMultilineStringLiteral, TokenIdSymbol, TokenIdTilde, TokenIdTimesEq, @@ -146,7 +146,6 @@ struct TokenIntLit { struct TokenStrLit { Buf str; - bool is_c_str; }; struct TokenCharLit { @@ -167,7 +166,7 @@ struct Token { // TokenIdFloatLiteral TokenFloatLit float_lit; - // TokenIdStringLiteral or TokenIdSymbol + // TokenIdStringLiteral, TokenIdMultilineStringLiteral or TokenIdSymbol TokenStrLit str_lit; // TokenIdCharLiteral diff --git a/src/translate_c.cpp b/src/translate_c.cpp deleted file mode 100644 index bd99134f0..000000000 --- a/src/translate_c.cpp +++ /dev/null @@ -1,5227 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ -#include "all_types.hpp" -#include "analyze.hpp" -#include "c_tokenizer.hpp" -#include "error.hpp" -#include "ir.hpp" -#include "os.hpp" -#include "translate_c.hpp" -#include "parser.hpp" -#include "zig_clang.h" - -#if __GNUC__ >= 8 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif - -#include -#include -#include - -#if __GNUC__ >= 8 -#pragma GCC diagnostic pop -#endif - -#include - -struct Alias { - Buf *new_name; - Buf *canon_name; -}; - -enum TransScopeId { - TransScopeIdSwitch, - TransScopeIdVar, - TransScopeIdBlock, - TransScopeIdRoot, - TransScopeIdWhile, -}; - -struct TransScope { - TransScopeId id; - TransScope *parent; -}; - -struct TransScopeSwitch { - TransScope base; - AstNode *switch_node; - uint32_t case_index; - bool found_default; - Buf *end_label_name; -}; - -struct TransScopeVar { - TransScope base; - Buf *c_name; - Buf *zig_name; -}; - -struct TransScopeBlock { - TransScope base; - AstNode *node; -}; - -struct TransScopeRoot { - TransScope base; -}; - -struct TransScopeWhile { - TransScope base; - AstNode *node; -}; - -struct Context { - AstNode *root; - VisibMod visib_mod; - bool want_export; - HashMap decl_table; - HashMap macro_table; - HashMap global_table; - ZigClangSourceManager *source_manager; - ZigList aliases; - bool warnings_on; - - CodeGen *codegen; - ZigClangASTContext *ctx; - - TransScopeRoot *global_scope; - HashMap ptr_params; -}; - -enum ResultUsed { - ResultUsedNo, - ResultUsedYes, -}; - -enum TransLRValue { - TransLValue, - TransRValue, -}; - -static TransScopeRoot *trans_scope_root_create(Context *c); -static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope); -static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope); -static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name); -static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope); - -static TransScopeBlock *trans_scope_block_find(TransScope *scope); - -static AstNode *resolve_record_decl(Context *c, const ZigClangRecordDecl *record_decl); -static AstNode *resolve_enum_decl(Context *c, const ZigClangEnumDecl *enum_decl); -static AstNode *resolve_typedef_decl(Context *c, const ZigClangTypedefNameDecl *typedef_decl); - -static int trans_stmt_extra(Context *c, TransScope *scope, const ZigClangStmt *stmt, - ResultUsed result_used, TransLRValue lrval, - AstNode **out_node, TransScope **out_child_scope, - TransScope **out_node_scope); -static TransScope *trans_stmt(Context *c, TransScope *scope, const ZigClangStmt *stmt, AstNode **out_node); -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const ZigClangExpr *expr, TransLRValue lrval); -static AstNode *trans_type(Context *c, const ZigClangType *ty, ZigClangSourceLocation source_loc); -static AstNode *trans_qual_type(Context *c, ZigClangQualType qt, ZigClangSourceLocation source_loc); -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangExpr *expr, TransLRValue lrval); -static AstNode *trans_ap_value(Context *c, const ZigClangAPValue *ap_value, ZigClangQualType qt, - ZigClangSourceLocation source_loc); -static bool c_is_unsigned_integer(Context *c, ZigClangQualType qt); - -static const ZigClangAPSInt *bitcast(const llvm::APSInt *src) { - return reinterpret_cast(src); -} - -static const ZigClangAPValue *bitcast(const clang::APValue *src) { - return reinterpret_cast(src); -} - -//static const ZigClangStmt *bitcast(const clang::Stmt *src) { -// return reinterpret_cast(src); -//} - -static const ZigClangExpr *bitcast(const clang::Expr *src) { - return reinterpret_cast(src); -} - -static ZigClangSourceLocation bitcast(clang::SourceLocation src) { - ZigClangSourceLocation dest; - memcpy(&dest, static_cast(&src), sizeof(ZigClangSourceLocation)); - return dest; -} -static ZigClangQualType bitcast(clang::QualType src) { - ZigClangQualType dest; - memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); - return dest; -} -//static clang::QualType bitcast(ZigClangQualType src) { -// clang::QualType dest; -// memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); -// return dest; -//} - -ATTRIBUTE_PRINTF(3, 4) -static void emit_warning(Context *c, ZigClangSourceLocation sl, const char *format, ...) { - if (!c->warnings_on) { - return; - } - - va_list ap; - va_start(ap, format); - Buf *msg = buf_vprintf(format, ap); - va_end(ap); - - const char *filename_bytes = ZigClangSourceManager_getFilename(c->source_manager, - ZigClangSourceManager_getSpellingLoc(c->source_manager, sl)); - Buf *path; - if (filename_bytes) { - path = buf_create_from_str(filename_bytes); - } else { - path = buf_sprintf("(no file)"); - } - unsigned line = ZigClangSourceManager_getSpellingLineNumber(c->source_manager, sl); - unsigned column = ZigClangSourceManager_getSpellingColumnNumber(c->source_manager, sl); - fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg)); -} - -static void add_global_weak_alias(Context *c, Buf *new_name, Buf *canon_name) { - Alias *alias = c->aliases.add_one(); - alias->new_name = new_name; - alias->canon_name = canon_name; -} - -static Buf *trans_lookup_zig_symbol(Context *c, TransScope *scope, Buf *c_symbol_name) { - while (scope != nullptr) { - if (scope->id == TransScopeIdVar) { - TransScopeVar *var_scope = (TransScopeVar *)scope; - if (buf_eql_buf(var_scope->c_name, c_symbol_name)) { - return var_scope->zig_name; - } - } - scope = scope->parent; - } - return c_symbol_name; -} - -static AstNode * trans_create_node(Context *c, NodeType id) { - AstNode *node = allocate(1); - node->type = id; - // TODO line/column. mapping to C file?? - return node; -} - -static AstNode *trans_create_node_break(Context *c, Buf *label_name, AstNode *value_node) { - AstNode *node = trans_create_node(c, NodeTypeBreak); - node->data.break_expr.name = label_name; - node->data.break_expr.expr = value_node; - return node; -} - -static AstNode *trans_create_node_return(Context *c, AstNode *value_node) { - AstNode *node = trans_create_node(c, NodeTypeReturnExpr); - node->data.return_expr.kind = ReturnKindUnconditional; - node->data.return_expr.expr = value_node; - return node; -} - -static AstNode *trans_create_node_if(Context *c, AstNode *cond_node, AstNode *then_node, AstNode *else_node) { - AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr); - node->data.if_bool_expr.condition = cond_node; - node->data.if_bool_expr.then_block = then_node; - node->data.if_bool_expr.else_node = else_node; - return node; -} - -static AstNode *trans_create_node_float_lit(Context *c, double value) { - AstNode *node = trans_create_node(c, NodeTypeFloatLiteral); - node->data.float_literal.bigfloat = allocate(1); - bigfloat_init_64(node->data.float_literal.bigfloat, value); - return node; -} - -static AstNode *trans_create_node_symbol(Context *c, Buf *name) { - AstNode *node = trans_create_node(c, NodeTypeSymbol); - node->data.symbol_expr.symbol = name; - return node; -} - -static AstNode *trans_create_node_symbol_str(Context *c, const char *name) { - return trans_create_node_symbol(c, buf_create_from_str(name)); -} - -static AstNode *trans_create_node_builtin_fn_call(Context *c, Buf *name) { - AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - node->data.fn_call_expr.fn_ref_expr = trans_create_node_symbol(c, name); - node->data.fn_call_expr.modifier = CallModifierBuiltin; - return node; -} - -static AstNode *trans_create_node_builtin_fn_call_str(Context *c, const char *name) { - return trans_create_node_builtin_fn_call(c, buf_create_from_str(name)); -} - -static AstNode *trans_create_node_opaque(Context *c) { - return trans_create_node_builtin_fn_call_str(c, "OpaqueType"); -} - -static AstNode *trans_create_node_fn_call_1(Context *c, AstNode *fn_ref_expr, AstNode *arg1) { - AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - node->data.fn_call_expr.fn_ref_expr = fn_ref_expr; - node->data.fn_call_expr.params.append(arg1); - return node; -} - -static AstNode *trans_create_node_field_access(Context *c, AstNode *container, Buf *field_name) { - AstNode *node = trans_create_node(c, NodeTypeFieldAccessExpr); - if (container->type == NodeTypeSymbol) { - assert(container->data.symbol_expr.symbol != nullptr); - } - node->data.field_access_expr.struct_expr = container; - node->data.field_access_expr.field_name = field_name; - return node; -} - -static AstNode *trans_create_node_field_access_str(Context *c, AstNode *container, const char *field_name) { - return trans_create_node_field_access(c, container, buf_create_from_str(field_name)); -} - -static AstNode *trans_create_node_ptr_deref(Context *c, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypePtrDeref); - node->data.ptr_deref_expr.target = child_node; - return node; -} - -static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); - node->data.prefix_op_expr.prefix_op = op; - node->data.prefix_op_expr.primary_expr = child_node; - return node; -} - -static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional); - node->data.unwrap_optional.expr = child_node; - return node; -} - -static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) { - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.op1 = lhs_node; - node->data.bin_op_expr.bin_op = op; - node->data.bin_op_expr.op2 = rhs_node; - return node; -} - -static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNode *node) { - if (result_used == ResultUsedYes) return node; - return trans_create_node_bin_op(c, - trans_create_node_symbol_str(c, "_"), - BinOpTypeAssign, - node); -} - -static TokenId ptr_len_to_token_id(PtrLen ptr_len) { - switch (ptr_len) { - case PtrLenSingle: - return TokenIdStar; - case PtrLenUnknown: - return TokenIdBracketStarBracket; - case PtrLenC: - return TokenIdBracketStarCBracket; - } - zig_unreachable(); -} - -static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) { - AstNode *node = trans_create_node(c, NodeTypePointerType); - node->data.pointer_type.star_token = allocate(1); - node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len); - node->data.pointer_type.is_const = is_const; - node->data.pointer_type.is_volatile = is_volatile; - node->data.pointer_type.op_expr = child_node; - return node; -} - -static AstNode *trans_create_node_addr_of(Context *c, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); - node->data.prefix_op_expr.prefix_op = PrefixOpAddrOf; - node->data.prefix_op_expr.primary_expr = child_node; - return node; -} - -static AstNode *trans_create_node_bool(Context *c, bool value) { - AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral); - bool_node->data.bool_literal.value = value; - return bool_node; -} - -static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) { - AstNode *node = trans_create_node(c, NodeTypeStringLiteral); - node->data.string_literal.buf = buf; - node->data.string_literal.c = true; - return node; -} - -static AstNode *trans_create_node_str_lit_non_c(Context *c, Buf *buf) { - AstNode *node = trans_create_node(c, NodeTypeStringLiteral); - node->data.string_literal.buf = buf; - node->data.string_literal.c = false; - return node; -} - -static AstNode *trans_create_node_unsigned_negative(Context *c, uint64_t x, bool is_negative) { - AstNode *node = trans_create_node(c, NodeTypeIntLiteral); - node->data.int_literal.bigint = allocate(1); - bigint_init_data(node->data.int_literal.bigint, &x, 1, is_negative); - return node; -} - -static AstNode *trans_create_node_unsigned(Context *c, uint64_t x) { - return trans_create_node_unsigned_negative(c, x, false); -} - -static AstNode *trans_create_node_cast(Context *c, AstNode *dest, AstNode *src) { - AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - node->data.fn_call_expr.fn_ref_expr = dest; - node->data.fn_call_expr.params.resize(1); - node->data.fn_call_expr.params.items[0] = src; - return node; -} - -static AstNode *trans_create_node_unsigned_negative_type(Context *c, uint64_t x, bool is_negative, - const char *type_name) -{ - AstNode *lit_node = trans_create_node_unsigned_negative(c, x, is_negative); - return trans_create_node_cast(c, trans_create_node_symbol_str(c, type_name), lit_node); -} - -static AstNode *trans_create_node_array_type(Context *c, AstNode *size_node, AstNode *child_type_node) { - AstNode *node = trans_create_node(c, NodeTypeArrayType); - node->data.array_type.size = size_node; - node->data.array_type.child_type = child_type_node; - return node; -} - -static AstNode *trans_create_node_var_decl(Context *c, VisibMod visib_mod, bool is_const, Buf *var_name, - AstNode *type_node, AstNode *init_node) -{ - AstNode *node = trans_create_node(c, NodeTypeVariableDeclaration); - node->data.variable_declaration.visib_mod = visib_mod; - node->data.variable_declaration.symbol = var_name; - node->data.variable_declaration.is_const = is_const; - node->data.variable_declaration.type = type_node; - node->data.variable_declaration.expr = init_node; - return node; -} - -static AstNode *trans_create_node_var_decl_global(Context *c, bool is_const, Buf *var_name, AstNode *type_node, - AstNode *init_node) -{ - return trans_create_node_var_decl(c, c->visib_mod, is_const, var_name, type_node, init_node); -} - -static AstNode *trans_create_node_var_decl_local(Context *c, bool is_const, Buf *var_name, AstNode *type_node, - AstNode *init_node) -{ - return trans_create_node_var_decl(c, VisibModPrivate, is_const, var_name, type_node, init_node); -} - -static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *ref_node, AstNode *src_proto_node) { - AstNode *fn_def = trans_create_node(c, NodeTypeFnDef); - AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto); - fn_proto->data.fn_proto.visib_mod = c->visib_mod; - fn_proto->data.fn_proto.name = fn_name; - fn_proto->data.fn_proto.fn_inline = FnInlineAlways; - fn_proto->data.fn_proto.return_type = src_proto_node->data.fn_proto.return_type; // TODO ok for these to alias? - - fn_def->data.fn_def.fn_proto = fn_proto; - fn_proto->data.fn_proto.fn_def_node = fn_def; - - AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node); - AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); - fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; - - for (size_t i = 0; i < src_proto_node->data.fn_proto.params.length; i += 1) { - AstNode *src_param_node = src_proto_node->data.fn_proto.params.at(i); - Buf *param_name = src_param_node->data.param_decl.name; - if (!param_name) param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); - - AstNode *dest_param_node = trans_create_node(c, NodeTypeParamDecl); - dest_param_node->data.param_decl.name = param_name; - dest_param_node->data.param_decl.type = src_param_node->data.param_decl.type; - dest_param_node->data.param_decl.is_noalias = src_param_node->data.param_decl.is_noalias; - fn_proto->data.fn_proto.params.append(dest_param_node); - - fn_call_node->data.fn_call_expr.params.append(trans_create_node_symbol(c, param_name)); - - } - - AstNode *block = trans_create_node(c, NodeTypeBlock); - block->data.block.statements.resize(1); - block->data.block.statements.items[0] = trans_create_node_return(c, fn_call_node); - - fn_def->data.fn_def.body = block; - return fn_def; -} - -static AstNode *trans_create_node_grouped_expr(Context *c, AstNode *child) { - AstNode *node = trans_create_node(c, NodeTypeGroupedExpr); - node->data.grouped_expr = child; - return node; -} - -static AstNode *get_global(Context *c, Buf *name) { - { - auto entry = c->global_table.maybe_get(name); - if (entry) { - return entry->value; - } - } - { - auto entry = c->macro_table.maybe_get(name); - if (entry) - return entry->value; - } - ZigType *type; - if (get_primitive_type(c->codegen, name, &type) != ErrorPrimitiveTypeNotFound) { - return trans_create_node_symbol(c, name); - } - return nullptr; -} - -static void add_top_level_decl(Context *c, Buf *name, AstNode *node) { - c->global_table.put(name, node); - c->root->data.container_decl.decls.append(node); -} - -static AstNode *add_global_var(Context *c, Buf *var_name, AstNode *value_node) { - bool is_const = true; - AstNode *type_node = nullptr; - AstNode *node = trans_create_node_var_decl_global(c, is_const, var_name, type_node, value_node); - add_top_level_decl(c, var_name, node); - return node; -} - -static AstNode *trans_create_node_apint(Context *c, const ZigClangAPSInt *aps_int) { - AstNode *node = trans_create_node(c, NodeTypeIntLiteral); - node->data.int_literal.bigint = allocate(1); - bool is_negative = ZigClangAPSInt_isSigned(aps_int) && ZigClangAPSInt_isNegative(aps_int); - if (!is_negative) { - bigint_init_data(node->data.int_literal.bigint, - ZigClangAPSInt_getRawData(aps_int), - ZigClangAPSInt_getNumWords(aps_int), - false); - return node; - } - const ZigClangAPSInt *negated = ZigClangAPSInt_negate(aps_int); - bigint_init_data(node->data.int_literal.bigint, ZigClangAPSInt_getRawData(negated), - ZigClangAPSInt_getNumWords(negated), true); - ZigClangAPSInt_free(negated); - return node; -} - -static AstNode *trans_create_node_apfloat(Context *c, const llvm::APFloat &ap_float) { - uint8_t buf[128]; - size_t written = ap_float.convertToHexString((char *)buf, 0, false, - llvm::APFloat::rmNearestTiesToEven); - AstNode *node = trans_create_node(c, NodeTypeFloatLiteral); - node->data.float_literal.bigfloat = allocate(1); - if (bigfloat_init_buf(node->data.float_literal.bigfloat, buf, written)) { - node->data.float_literal.overflow = true; - } - return node; -} - -static const ZigClangType *qual_type_canon(ZigClangQualType qt) { - ZigClangQualType canon = ZigClangQualType_getCanonicalType(qt); - return ZigClangQualType_getTypePtr(canon); -} - -static ZigClangQualType get_expr_qual_type(Context *c, const ZigClangExpr *expr) { - // String literals in C are `char *` but they should really be `const char *`. - if (ZigClangExpr_getStmtClass(expr) == ZigClangStmt_ImplicitCastExprClass) { - const ZigClangImplicitCastExpr *cast_expr = reinterpret_cast(expr); - if (ZigClangImplicitCastExpr_getCastKind(cast_expr) == ZigClangCK_ArrayToPointerDecay) { - const ZigClangExpr *sub_expr = ZigClangImplicitCastExpr_getSubExpr(cast_expr); - if (ZigClangExpr_getStmtClass(sub_expr) == ZigClangStmt_StringLiteralClass) { - ZigClangQualType array_qt = ZigClangExpr_getType(sub_expr); - const ZigClangArrayType *array_type = reinterpret_cast( - ZigClangQualType_getTypePtr(array_qt)); - ZigClangQualType pointee_qt = ZigClangArrayType_getElementType(array_type); - ZigClangQualType_addConst(&pointee_qt); - return ZigClangASTContext_getPointerType(c->ctx, pointee_qt); - } - } - } - return ZigClangExpr_getType(expr); -} - -static ZigClangQualType get_expr_qual_type_before_implicit_cast(Context *c, const ZigClangExpr *expr) { - if (ZigClangExpr_getStmtClass(expr) == ZigClangStmt_ImplicitCastExprClass) { - const ZigClangImplicitCastExpr *cast_expr = reinterpret_cast(expr); - return get_expr_qual_type(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr)); - } - return ZigClangExpr_getType(expr); -} - -static AstNode *get_expr_type(Context *c, const ZigClangExpr *expr) { - return trans_qual_type(c, get_expr_qual_type(c, expr), ZigClangExpr_getBeginLoc(expr)); -} - -static bool is_c_void_type(AstNode *node) { - return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void")); -} - -static bool qual_type_is_ptr(ZigClangQualType qt) { - const ZigClangType *ty = qual_type_canon(qt); - return ZigClangType_getTypeClass(ty) == ZigClangType_Pointer; -} - -static const ZigClangFunctionProtoType *qual_type_get_fn_proto(ZigClangQualType qt, bool *is_ptr) { - const ZigClangType *ty = qual_type_canon(qt); - *is_ptr = false; - - if (ZigClangType_getTypeClass(ty) == ZigClangType_Pointer) { - *is_ptr = true; - ZigClangQualType child_qt = ZigClangType_getPointeeType(ty); - ty = ZigClangQualType_getTypePtr(child_qt); - } - - if (ZigClangType_getTypeClass(ty) == ZigClangType_FunctionProto) { - return reinterpret_cast(ty); - } - - return nullptr; -} - -static bool qual_type_is_fn_ptr(ZigClangQualType qt) { - bool is_ptr; - if (qual_type_get_fn_proto(qt, &is_ptr)) { - return is_ptr; - } - - return false; -} - -static uint32_t qual_type_int_bit_width(Context *c, const ZigClangQualType qt, ZigClangSourceLocation source_loc) { - const ZigClangType *ty = ZigClangQualType_getTypePtr(qt); - switch (ZigClangType_getTypeClass(ty)) { - case ZigClangType_Builtin: - { - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(ty); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeChar_U: - case ZigClangBuiltinTypeUChar: - case ZigClangBuiltinTypeChar_S: - case ZigClangBuiltinTypeSChar: - return 8; - case ZigClangBuiltinTypeUInt128: - case ZigClangBuiltinTypeInt128: - return 128; - default: - return 0; - } - zig_unreachable(); - } - case ZigClangType_Typedef: - { - const ZigClangTypedefType *typedef_ty = reinterpret_cast(ty); - const ZigClangTypedefNameDecl *typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - const char *type_name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)typedef_decl); - if (strcmp(type_name, "uint8_t") == 0 || strcmp(type_name, "int8_t") == 0) { - return 8; - } else if (strcmp(type_name, "uint16_t") == 0 || strcmp(type_name, "int16_t") == 0) { - return 16; - } else if (strcmp(type_name, "uint32_t") == 0 || strcmp(type_name, "int32_t") == 0) { - return 32; - } else if (strcmp(type_name, "uint64_t") == 0 || strcmp(type_name, "int64_t") == 0) { - return 64; - } else { - return 0; - } - } - default: - return 0; - } - zig_unreachable(); -} - - -static AstNode *qual_type_to_log2_int_ref(Context *c, const ZigClangQualType qt, - ZigClangSourceLocation source_loc) -{ - uint32_t int_bit_width = qual_type_int_bit_width(c, qt, source_loc); - if (int_bit_width != 0) { - // we can perform the log2 now. - uint64_t cast_bit_width = log2_u64(int_bit_width); - return trans_create_node_symbol(c, buf_sprintf("u%" ZIG_PRI_u64, cast_bit_width)); - } - - AstNode *zig_type_node = trans_qual_type(c, qt, source_loc); - -// @import("std").math.Log2Int(c_long); -// -// FnCall -// FieldAccess -// FieldAccess -// FnCall (.builtin = true) -// Symbol "import" -// ZigClangStringLiteral "std" -// Symbol "math" -// Symbol "Log2Int" -// zig_type_node - - AstNode *import_fn_call = trans_create_node_builtin_fn_call_str(c, "import"); - import_fn_call->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("std"))); - AstNode *inner_field_access = trans_create_node_field_access_str(c, import_fn_call, "math"); - AstNode *outer_field_access = trans_create_node_field_access_str(c, inner_field_access, "Log2Int"); - AstNode *log2int_fn_call = trans_create_node_fn_call_1(c, outer_field_access, zig_type_node); - - return log2int_fn_call; -} - -static bool qual_type_child_is_fn_proto(ZigClangQualType qt) { - const ZigClangType *ty = ZigClangQualType_getTypePtr(qt); - if (ZigClangType_getTypeClass(ty) == ZigClangType_Paren) { - const ZigClangParenType *paren_type = reinterpret_cast(ty); - ZigClangQualType inner_type = ZigClangParenType_getInnerType(paren_type); - if (ZigClangQualType_getTypeClass(inner_type) == ZigClangType_FunctionProto) { - return true; - } - } else if (ZigClangType_getTypeClass(ty) == ZigClangType_Attributed) { - const ZigClangAttributedType *attr_type = reinterpret_cast(ty); - return qual_type_child_is_fn_proto(ZigClangAttributedType_getEquivalentType(attr_type)); - } - return false; -} - -static AstNode* trans_c_ptr_cast(Context *c, ZigClangSourceLocation source_location, ZigClangQualType dest_type, - ZigClangQualType src_type, AstNode *expr) -{ - const ZigClangType *ty = ZigClangQualType_getTypePtr(dest_type); - const ZigClangQualType child_type = ZigClangType_getPointeeType(ty); - - AstNode *dest_type_node = trans_type(c, ty, source_location); - AstNode *child_type_node = trans_qual_type(c, child_type, source_location); - - // Implicit downcasting from higher to lower alignment values is forbidden, - // use @alignCast to side-step this problem - AstNode *ptrcast_node = trans_create_node_builtin_fn_call_str(c, "ptrCast"); - ptrcast_node->data.fn_call_expr.params.append(dest_type_node); - - if (ZigClangType_isVoidType(qual_type_canon(child_type))) { - // void has 1-byte alignment - ptrcast_node->data.fn_call_expr.params.append(expr); - } else { - AstNode *alignof_node = trans_create_node_builtin_fn_call_str(c, "alignOf"); - alignof_node->data.fn_call_expr.params.append(child_type_node); - AstNode *aligncast_node = trans_create_node_builtin_fn_call_str(c, "alignCast"); - aligncast_node->data.fn_call_expr.params.append(alignof_node); - aligncast_node->data.fn_call_expr.params.append(expr); - - ptrcast_node->data.fn_call_expr.params.append(aligncast_node); - } - - return ptrcast_node; -} - -static AstNode* trans_c_cast(Context *c, ZigClangSourceLocation source_location, ZigClangQualType dest_type, - ZigClangQualType src_type, AstNode *expr) -{ - // The only way void pointer casts are valid C code, is if - // the value of the expression is ignored. We therefore just - // return the expr, and let the system that ignores values - // translate this correctly. - if (ZigClangType_isVoidType(qual_type_canon(dest_type))) { - return expr; - } - if (ZigClangQualType_eq(dest_type, src_type)) { - return expr; - } - if (qual_type_is_ptr(dest_type) && qual_type_is_ptr(src_type)) { - return trans_c_ptr_cast(c, source_location, dest_type, src_type, expr); - } - if (c_is_unsigned_integer(c, dest_type) && qual_type_is_ptr(src_type)) { - AstNode *addr_node = trans_create_node_builtin_fn_call_str(c, "ptrToInt"); - addr_node->data.fn_call_expr.params.append(expr); - return trans_create_node_fn_call_1(c, trans_qual_type(c, dest_type, source_location), addr_node); - } - if (c_is_unsigned_integer(c, src_type) && qual_type_is_ptr(dest_type)) { - AstNode *ptr_node = trans_create_node_builtin_fn_call_str(c, "intToPtr"); - ptr_node->data.fn_call_expr.params.append(trans_qual_type(c, dest_type, source_location)); - ptr_node->data.fn_call_expr.params.append(expr); - return ptr_node; - } - // TODO: maybe widen to increase size - // TODO: maybe bitcast to change sign - // TODO: maybe truncate to reduce size - return trans_create_node_fn_call_1(c, trans_qual_type(c, dest_type, source_location), expr); -} - -static bool c_is_signed_integer(Context *c, ZigClangQualType qt) { - const ZigClangType *c_type = qual_type_canon(qt); - if (ZigClangType_getTypeClass(c_type) != ZigClangType_Builtin) - return false; - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(c_type); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeSChar: - case ZigClangBuiltinTypeShort: - case ZigClangBuiltinTypeInt: - case ZigClangBuiltinTypeLong: - case ZigClangBuiltinTypeLongLong: - case ZigClangBuiltinTypeInt128: - case ZigClangBuiltinTypeWChar_S: - return true; - default: - return false; - } -} - -static bool c_is_unsigned_integer(Context *c, ZigClangQualType qt) { - const ZigClangType *c_type = qual_type_canon(qt); - if (ZigClangType_getTypeClass(c_type) != ZigClangType_Builtin) - return false; - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(c_type); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeChar_U: - case ZigClangBuiltinTypeUChar: - case ZigClangBuiltinTypeChar_S: - case ZigClangBuiltinTypeUShort: - case ZigClangBuiltinTypeUInt: - case ZigClangBuiltinTypeULong: - case ZigClangBuiltinTypeULongLong: - case ZigClangBuiltinTypeUInt128: - case ZigClangBuiltinTypeWChar_U: - return true; - default: - return false; - } -} - -static bool c_is_builtin_type(Context *c, ZigClangQualType qt, ZigClangBuiltinTypeKind kind) { - const ZigClangType *c_type = qual_type_canon(qt); - if (ZigClangType_getTypeClass(c_type) != ZigClangType_Builtin) - return false; - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(c_type); - return ZigClangBuiltinType_getKind(builtin_ty) == kind; -} - -static bool c_is_float(Context *c, ZigClangQualType qt) { - const ZigClangType *c_type = ZigClangQualType_getTypePtr(qt); - if (ZigClangType_getTypeClass(c_type) != ZigClangType_Builtin) - return false; - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(c_type); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeHalf: - case ZigClangBuiltinTypeFloat: - case ZigClangBuiltinTypeDouble: - case ZigClangBuiltinTypeFloat128: - case ZigClangBuiltinTypeLongDouble: - return true; - default: - return false; - } -} - -static bool qual_type_has_wrapping_overflow(Context *c, ZigClangQualType qt) { - if (c_is_signed_integer(c, qt) || c_is_float(c, qt)) { - // float and signed integer overflow is undefined behavior. - return false; - } else { - // unsigned integer overflow wraps around. - return true; - } -} - -static bool type_is_function(Context *c, const ZigClangType *ty, ZigClangSourceLocation source_loc) { - switch (ZigClangType_getTypeClass(ty)) { - case ZigClangType_FunctionProto: - case ZigClangType_FunctionNoProto: - return true; - case ZigClangType_Elaborated: { - const ZigClangElaboratedType *elaborated_ty = reinterpret_cast(ty); - ZigClangQualType qt = ZigClangElaboratedType_getNamedType(elaborated_ty); - return type_is_function(c, ZigClangQualType_getTypePtr(qt), source_loc); - } - case ZigClangType_Typedef: { - const ZigClangTypedefType *typedef_ty = reinterpret_cast(ty); - const ZigClangTypedefNameDecl *typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - ZigClangQualType underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); - return type_is_function(c, ZigClangQualType_getTypePtr(underlying_type), source_loc); - } - default: - return false; - } -} - -static bool type_is_opaque(Context *c, const ZigClangType *ty, ZigClangSourceLocation source_loc) { - switch (ZigClangType_getTypeClass(ty)) { - case ZigClangType_Builtin: { - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(ty); - return ZigClangBuiltinType_getKind(builtin_ty) == ZigClangBuiltinTypeVoid; - } - case ZigClangType_Record: { - const ZigClangRecordType *record_ty = reinterpret_cast(ty); - const ZigClangRecordDecl *record_decl = ZigClangRecordType_getDecl(record_ty); - const ZigClangRecordDecl *record_def = ZigClangRecordDecl_getDefinition(record_decl); - if (record_def == nullptr) { - return true; - } - for (auto it = reinterpret_cast(record_def)->field_begin(), - it_end = reinterpret_cast(record_def)->field_end(); - it != it_end; ++it) - { - const clang::FieldDecl *field_decl = *it; - - if (field_decl->isBitField()) { - return true; - } - } - return false; - } - case ZigClangType_Elaborated: { - const ZigClangElaboratedType *elaborated_ty = reinterpret_cast(ty); - ZigClangQualType qt = ZigClangElaboratedType_getNamedType(elaborated_ty); - return type_is_opaque(c, ZigClangQualType_getTypePtr(qt), source_loc); - } - case ZigClangType_Typedef: { - const ZigClangTypedefType *typedef_ty = reinterpret_cast(ty); - const ZigClangTypedefNameDecl *typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - ZigClangQualType underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); - return type_is_opaque(c, ZigClangQualType_getTypePtr(underlying_type), source_loc); - } - default: - return false; - } -} - -static AstNode *trans_type(Context *c, const ZigClangType *ty, ZigClangSourceLocation source_loc) { - switch (ZigClangType_getTypeClass(ty)) { - case ZigClangType_Builtin: - { - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(ty); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeVoid: - return trans_create_node_symbol_str(c, "c_void"); - case ZigClangBuiltinTypeBool: - return trans_create_node_symbol_str(c, "bool"); - case ZigClangBuiltinTypeChar_U: - case ZigClangBuiltinTypeUChar: - case ZigClangBuiltinTypeChar_S: - case ZigClangBuiltinTypeChar8: - return trans_create_node_symbol_str(c, "u8"); - case ZigClangBuiltinTypeSChar: - return trans_create_node_symbol_str(c, "i8"); - case ZigClangBuiltinTypeUShort: - return trans_create_node_symbol_str(c, "c_ushort"); - case ZigClangBuiltinTypeUInt: - return trans_create_node_symbol_str(c, "c_uint"); - case ZigClangBuiltinTypeULong: - return trans_create_node_symbol_str(c, "c_ulong"); - case ZigClangBuiltinTypeULongLong: - return trans_create_node_symbol_str(c, "c_ulonglong"); - case ZigClangBuiltinTypeShort: - return trans_create_node_symbol_str(c, "c_short"); - case ZigClangBuiltinTypeInt: - return trans_create_node_symbol_str(c, "c_int"); - case ZigClangBuiltinTypeLong: - return trans_create_node_symbol_str(c, "c_long"); - case ZigClangBuiltinTypeLongLong: - return trans_create_node_symbol_str(c, "c_longlong"); - case ZigClangBuiltinTypeUInt128: - return trans_create_node_symbol_str(c, "u128"); - case ZigClangBuiltinTypeInt128: - return trans_create_node_symbol_str(c, "i128"); - case ZigClangBuiltinTypeFloat: - return trans_create_node_symbol_str(c, "f32"); - case ZigClangBuiltinTypeDouble: - return trans_create_node_symbol_str(c, "f64"); - case ZigClangBuiltinTypeFloat128: - return trans_create_node_symbol_str(c, "f128"); - case ZigClangBuiltinTypeFloat16: - return trans_create_node_symbol_str(c, "f16"); - case ZigClangBuiltinTypeLongDouble: - return trans_create_node_symbol_str(c, "c_longdouble"); - case ZigClangBuiltinTypeWChar_U: - case ZigClangBuiltinTypeChar16: - case ZigClangBuiltinTypeChar32: - case ZigClangBuiltinTypeWChar_S: - case ZigClangBuiltinTypeHalf: - case ZigClangBuiltinTypeNullPtr: - case ZigClangBuiltinTypeObjCId: - case ZigClangBuiltinTypeObjCClass: - case ZigClangBuiltinTypeObjCSel: - case ZigClangBuiltinTypeOMPArraySection: - case ZigClangBuiltinTypeDependent: - case ZigClangBuiltinTypeOverload: - case ZigClangBuiltinTypeBoundMember: - case ZigClangBuiltinTypePseudoObject: - case ZigClangBuiltinTypeUnknownAny: - case ZigClangBuiltinTypeBuiltinFn: - case ZigClangBuiltinTypeARCUnbridgedCast: - case ZigClangBuiltinTypeShortAccum: - case ZigClangBuiltinTypeAccum: - case ZigClangBuiltinTypeLongAccum: - case ZigClangBuiltinTypeUShortAccum: - case ZigClangBuiltinTypeUAccum: - case ZigClangBuiltinTypeULongAccum: - - case ZigClangBuiltinTypeOCLImage1dRO: - case ZigClangBuiltinTypeOCLImage1dArrayRO: - case ZigClangBuiltinTypeOCLImage1dBufferRO: - case ZigClangBuiltinTypeOCLImage2dRO: - case ZigClangBuiltinTypeOCLImage2dArrayRO: - case ZigClangBuiltinTypeOCLImage2dDepthRO: - case ZigClangBuiltinTypeOCLImage2dArrayDepthRO: - case ZigClangBuiltinTypeOCLImage2dMSAARO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAARO: - case ZigClangBuiltinTypeOCLImage2dMSAADepthRO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRO: - case ZigClangBuiltinTypeOCLImage3dRO: - case ZigClangBuiltinTypeOCLImage1dWO: - case ZigClangBuiltinTypeOCLImage1dArrayWO: - case ZigClangBuiltinTypeOCLImage1dBufferWO: - case ZigClangBuiltinTypeOCLImage2dWO: - case ZigClangBuiltinTypeOCLImage2dArrayWO: - case ZigClangBuiltinTypeOCLImage2dDepthWO: - case ZigClangBuiltinTypeOCLImage2dArrayDepthWO: - case ZigClangBuiltinTypeOCLImage2dMSAAWO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAAWO: - case ZigClangBuiltinTypeOCLImage2dMSAADepthWO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthWO: - case ZigClangBuiltinTypeOCLImage3dWO: - case ZigClangBuiltinTypeOCLImage1dRW: - case ZigClangBuiltinTypeOCLImage1dArrayRW: - case ZigClangBuiltinTypeOCLImage1dBufferRW: - case ZigClangBuiltinTypeOCLImage2dRW: - case ZigClangBuiltinTypeOCLImage2dArrayRW: - case ZigClangBuiltinTypeOCLImage2dDepthRW: - case ZigClangBuiltinTypeOCLImage2dArrayDepthRW: - case ZigClangBuiltinTypeOCLImage2dMSAARW: - case ZigClangBuiltinTypeOCLImage2dArrayMSAARW: - case ZigClangBuiltinTypeOCLImage2dMSAADepthRW: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRW: - case ZigClangBuiltinTypeOCLImage3dRW: - case ZigClangBuiltinTypeOCLSampler: - case ZigClangBuiltinTypeOCLEvent: - case ZigClangBuiltinTypeOCLClkEvent: - case ZigClangBuiltinTypeOCLQueue: - case ZigClangBuiltinTypeOCLReserveID: - case ZigClangBuiltinTypeShortFract: - case ZigClangBuiltinTypeFract: - case ZigClangBuiltinTypeLongFract: - case ZigClangBuiltinTypeUShortFract: - case ZigClangBuiltinTypeUFract: - case ZigClangBuiltinTypeULongFract: - case ZigClangBuiltinTypeSatShortAccum: - case ZigClangBuiltinTypeSatAccum: - case ZigClangBuiltinTypeSatLongAccum: - case ZigClangBuiltinTypeSatUShortAccum: - case ZigClangBuiltinTypeSatUAccum: - case ZigClangBuiltinTypeSatULongAccum: - case ZigClangBuiltinTypeSatShortFract: - case ZigClangBuiltinTypeSatFract: - case ZigClangBuiltinTypeSatLongFract: - case ZigClangBuiltinTypeSatUShortFract: - case ZigClangBuiltinTypeSatUFract: - case ZigClangBuiltinTypeSatULongFract: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCMcePayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImePayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCRefPayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCSicPayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCMceResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCRefResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCSicResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultSingleRefStreamout: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultDualRefStreamout: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeSingleRefStreamin: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeDualRefStreamin: - case ZigClangBuiltinTypeSveInt8: - case ZigClangBuiltinTypeSveInt16: - case ZigClangBuiltinTypeSveInt32: - case ZigClangBuiltinTypeSveInt64: - case ZigClangBuiltinTypeSveUint8: - case ZigClangBuiltinTypeSveUint16: - case ZigClangBuiltinTypeSveUint32: - case ZigClangBuiltinTypeSveUint64: - case ZigClangBuiltinTypeSveFloat16: - case ZigClangBuiltinTypeSveFloat32: - case ZigClangBuiltinTypeSveFloat64: - case ZigClangBuiltinTypeSveBool: - emit_warning(c, source_loc, "unsupported builtin type"); - return nullptr; - } - break; - } - case ZigClangType_Pointer: - { - ZigClangQualType child_qt = ZigClangType_getPointeeType(ty); - AstNode *child_node = trans_qual_type(c, child_qt, source_loc); - if (child_node == nullptr) { - emit_warning(c, source_loc, "pointer to unsupported type"); - return nullptr; - } - - if (qual_type_child_is_fn_proto(child_qt)) { - return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); - } - - if (type_is_function(c, ZigClangQualType_getTypePtr(child_qt), source_loc)) { - return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); - } else if (type_is_opaque(c, ZigClangQualType_getTypePtr(child_qt), source_loc)) { - AstNode *pointer_node = trans_create_node_ptr_type(c, - ZigClangQualType_isConstQualified(child_qt), - ZigClangQualType_isVolatileQualified(child_qt), - child_node, PtrLenSingle); - return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); - } else { - return trans_create_node_ptr_type(c, - ZigClangQualType_isConstQualified(child_qt), - ZigClangQualType_isVolatileQualified(child_qt), - child_node, PtrLenC); - } - } - case ZigClangType_Typedef: - { - const ZigClangTypedefType *typedef_ty = reinterpret_cast(ty); - const ZigClangTypedefNameDecl *typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - return resolve_typedef_decl(c, typedef_decl); - } - case ZigClangType_Elaborated: - { - const ZigClangElaboratedType *elaborated_ty = reinterpret_cast(ty); - switch (ZigClangElaboratedType_getKeyword(elaborated_ty)) { - case ZigClangETK_Struct: - case ZigClangETK_Enum: - case ZigClangETK_Union: - return trans_qual_type(c, ZigClangElaboratedType_getNamedType(elaborated_ty), source_loc); - case ZigClangETK_Interface: - case ZigClangETK_Class: - case ZigClangETK_Typename: - case ZigClangETK_None: - emit_warning(c, source_loc, "unsupported elaborated type"); - return nullptr; - } - } - case ZigClangType_FunctionProto: - case ZigClangType_FunctionNoProto: - { - const ZigClangFunctionType *fn_ty = reinterpret_cast(ty); - - AstNode *proto_node = trans_create_node(c, NodeTypeFnProto); - switch (ZigClangFunctionType_getCallConv(fn_ty)) { - case ZigClangCallingConv_C: // __attribute__((cdecl)) - proto_node->data.fn_proto.cc = CallingConventionC; - proto_node->data.fn_proto.is_extern = true; - break; - case ZigClangCallingConv_X86StdCall: // __attribute__((stdcall)) - proto_node->data.fn_proto.cc = CallingConventionStdcall; - break; - case ZigClangCallingConv_X86FastCall: // __attribute__((fastcall)) - emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall"); - return nullptr; - case ZigClangCallingConv_X86ThisCall: // __attribute__((thiscall)) - emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall"); - return nullptr; - case ZigClangCallingConv_X86VectorCall: // __attribute__((vectorcall)) - emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall"); - return nullptr; - case ZigClangCallingConv_X86Pascal: // __attribute__((pascal)) - emit_warning(c, source_loc, "unsupported calling convention: x86 pascal"); - return nullptr; - case ZigClangCallingConv_Win64: // __attribute__((ms_abi)) - emit_warning(c, source_loc, "unsupported calling convention: win64"); - return nullptr; - case ZigClangCallingConv_X86_64SysV: // __attribute__((sysv_abi)) - emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv"); - return nullptr; - case ZigClangCallingConv_X86RegCall: - emit_warning(c, source_loc, "unsupported calling convention: x86 reg"); - return nullptr; - case ZigClangCallingConv_AAPCS: // __attribute__((pcs("aapcs"))) - emit_warning(c, source_loc, "unsupported calling convention: aapcs"); - return nullptr; - case ZigClangCallingConv_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) - emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp"); - return nullptr; - case ZigClangCallingConv_IntelOclBicc: // __attribute__((intel_ocl_bicc)) - emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc"); - return nullptr; - case ZigClangCallingConv_SpirFunction: // default for OpenCL functions on SPIR target - emit_warning(c, source_loc, "unsupported calling convention: SPIR function"); - return nullptr; - case ZigClangCallingConv_OpenCLKernel: - emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel"); - return nullptr; - case ZigClangCallingConv_Swift: - emit_warning(c, source_loc, "unsupported calling convention: Swift"); - return nullptr; - case ZigClangCallingConv_PreserveMost: - emit_warning(c, source_loc, "unsupported calling convention: PreserveMost"); - return nullptr; - case ZigClangCallingConv_PreserveAll: - emit_warning(c, source_loc, "unsupported calling convention: PreserveAll"); - return nullptr; - case ZigClangCallingConv_AArch64VectorCall: - emit_warning(c, source_loc, "unsupported calling convention: AArch64VectorCall"); - return nullptr; - } - - if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) { - proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "noreturn"); - } else { - proto_node->data.fn_proto.return_type = trans_qual_type(c, - ZigClangFunctionType_getReturnType(fn_ty), source_loc); - if (proto_node->data.fn_proto.return_type == nullptr) { - emit_warning(c, source_loc, "unsupported function proto return type"); - return nullptr; - } - // convert c_void to actual void (only for return type) - // we do want to look at the AstNode instead of ZigClangQualType, because - // if they do something like: - // typedef Foo void; - // void foo(void) -> Foo; - // we want to keep the return type AST node. - if (is_c_void_type(proto_node->data.fn_proto.return_type)) { - proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "void"); - } - } - - //emit_warning(c, source_loc, "TODO figure out fn prototype fn name"); - const char *fn_name = nullptr; - if (fn_name != nullptr) { - proto_node->data.fn_proto.name = buf_create_from_str(fn_name); - } - - if (ZigClangType_getTypeClass(ty) == ZigClangType_FunctionNoProto) { - return proto_node; - } - - const ZigClangFunctionProtoType *fn_proto_ty = reinterpret_cast(ty); - - proto_node->data.fn_proto.is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty); - size_t param_count = ZigClangFunctionProtoType_getNumParams(fn_proto_ty); - - for (size_t i = 0; i < param_count; i += 1) { - ZigClangQualType qt = ZigClangFunctionProtoType_getParamType(fn_proto_ty, i); - AstNode *param_type_node = trans_qual_type(c, qt, source_loc); - - if (param_type_node == nullptr) { - emit_warning(c, source_loc, "unresolved function proto parameter type"); - return nullptr; - } - - AstNode *param_node = trans_create_node(c, NodeTypeParamDecl); - //emit_warning(c, source_loc, "TODO figure out fn prototype param name"); - const char *param_name = nullptr; - if (param_name != nullptr) { - param_node->data.param_decl.name = buf_create_from_str(param_name); - } - param_node->data.param_decl.is_noalias = ZigClangQualType_isRestrictQualified(qt); - param_node->data.param_decl.type = param_type_node; - proto_node->data.fn_proto.params.append(param_node); - } - // TODO check for always_inline attribute - // TODO check for align attribute - - return proto_node; - } - case ZigClangType_Record: - { - const ZigClangRecordType *record_ty = reinterpret_cast(ty); - return resolve_record_decl(c, ZigClangRecordType_getDecl(record_ty)); - } - case ZigClangType_Enum: - { - const ZigClangEnumType *enum_ty = reinterpret_cast(ty); - return resolve_enum_decl(c, ZigClangEnumType_getDecl(enum_ty)); - } - case ZigClangType_ConstantArray: - { - const ZigClangConstantArrayType *const_arr_ty = reinterpret_cast(ty); - AstNode *child_type_node = trans_qual_type(c, - ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc); - if (child_type_node == nullptr) { - emit_warning(c, source_loc, "unresolved array element type"); - return nullptr; - } - const ZigClangAPInt *size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty); - uint64_t size = ZigClangAPInt_getLimitedValue(size_ap_int, UINT64_MAX); - AstNode *size_node = trans_create_node_unsigned(c, size); - return trans_create_node_array_type(c, size_node, child_type_node); - } - case ZigClangType_Paren: - { - const ZigClangParenType *paren_ty = reinterpret_cast(ty); - return trans_qual_type(c, ZigClangParenType_getInnerType(paren_ty), source_loc); - } - case ZigClangType_Decayed: - { - const ZigClangDecayedType *decayed_ty = reinterpret_cast(ty); - return trans_qual_type(c, ZigClangDecayedType_getDecayedType(decayed_ty), source_loc); - } - case ZigClangType_Attributed: - { - const ZigClangAttributedType *attributed_ty = reinterpret_cast(ty); - return trans_qual_type(c, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc); - } - case ZigClangType_IncompleteArray: - { - const ZigClangIncompleteArrayType *incomplete_array_ty = reinterpret_cast(ty); - ZigClangQualType child_qt = ZigClangIncompleteArrayType_getElementType(incomplete_array_ty); - AstNode *child_type_node = trans_qual_type(c, child_qt, source_loc); - if (child_type_node == nullptr) { - emit_warning(c, source_loc, "unresolved array element type"); - return nullptr; - } - AstNode *pointer_node = trans_create_node_ptr_type(c, - ZigClangQualType_isConstQualified(child_qt), - ZigClangQualType_isVolatileQualified(child_qt), - child_type_node, PtrLenC); - return pointer_node; - } - case ZigClangType_BlockPointer: - case ZigClangType_LValueReference: - case ZigClangType_RValueReference: - case ZigClangType_MemberPointer: - case ZigClangType_VariableArray: - case ZigClangType_DependentSizedArray: - case ZigClangType_DependentSizedExtVector: - case ZigClangType_Vector: - case ZigClangType_ExtVector: - case ZigClangType_UnresolvedUsing: - case ZigClangType_Adjusted: - case ZigClangType_TypeOfExpr: - case ZigClangType_TypeOf: - case ZigClangType_Decltype: - case ZigClangType_UnaryTransform: - case ZigClangType_TemplateTypeParm: - case ZigClangType_SubstTemplateTypeParm: - case ZigClangType_SubstTemplateTypeParmPack: - case ZigClangType_TemplateSpecialization: - case ZigClangType_Auto: - case ZigClangType_InjectedClassName: - case ZigClangType_DependentName: - case ZigClangType_DependentTemplateSpecialization: - case ZigClangType_PackExpansion: - case ZigClangType_ObjCObject: - case ZigClangType_ObjCInterface: - case ZigClangType_Complex: - case ZigClangType_ObjCObjectPointer: - case ZigClangType_Atomic: - case ZigClangType_Pipe: - case ZigClangType_ObjCTypeParam: - case ZigClangType_DeducedTemplateSpecialization: - case ZigClangType_DependentAddressSpace: - case ZigClangType_DependentVector: - case ZigClangType_MacroQualified: - emit_warning(c, source_loc, "unsupported type: '%s'", ZigClangType_getTypeClassName(ty)); - return nullptr; - } - zig_unreachable(); -} - -static AstNode *trans_qual_type(Context *c, ZigClangQualType qt, ZigClangSourceLocation source_loc) { - return trans_type(c, ZigClangQualType_getTypePtr(qt), source_loc); -} - -static int trans_compound_stmt_inline(Context *c, TransScope *scope, const ZigClangCompoundStmt *stmt, - AstNode *block_node, TransScope **out_node_scope) -{ - assert(block_node->type == NodeTypeBlock); - for (ZigClangCompoundStmt_const_body_iterator it = ZigClangCompoundStmt_body_begin(stmt), - end_it = ZigClangCompoundStmt_body_end(stmt); it != end_it; ++it) - { - AstNode *child_node; - scope = trans_stmt(c, scope, *it, &child_node); - if (scope == nullptr) - return ErrorUnexpected; - if (child_node != nullptr) - block_node->data.block.statements.append(child_node); - } - if (out_node_scope != nullptr) { - *out_node_scope = scope; - } - return ErrorNone; -} - -static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const ZigClangCompoundStmt *stmt, - TransScope **out_node_scope) -{ - TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope); - if (trans_compound_stmt_inline(c, &child_scope_block->base, stmt, child_scope_block->node, out_node_scope)) - return nullptr; - return child_scope_block->node; -} - -static AstNode *trans_stmt_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangStmtExpr *stmt, TransScope **out_node_scope) -{ - AstNode *block = trans_compound_stmt(c, scope, ZigClangStmtExpr_getSubStmt(stmt), out_node_scope); - if (block == nullptr) - return block; - assert(block->type == NodeTypeBlock); - if (block->data.block.statements.length == 0) - return block; - - Buf *label = buf_create_from_str("x"); - block->data.block.name = label; - AstNode *return_expr = block->data.block.statements.pop(); - if (return_expr->type == NodeTypeBinOpExpr && - return_expr->data.bin_op_expr.bin_op == BinOpTypeAssign && - return_expr->data.bin_op_expr.op1->type == NodeTypeSymbol) - { - Buf *symbol_buf = return_expr->data.bin_op_expr.op1->data.symbol_expr.symbol; - if (strcmp("_", buf_ptr(symbol_buf)) == 0) - return_expr = return_expr->data.bin_op_expr.op2; - } - block->data.block.statements.append(trans_create_node_break(c, label, return_expr)); - return maybe_suppress_result(c, result_used, block); -} - -static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ZigClangReturnStmt *stmt) { - const ZigClangExpr *value_expr = ZigClangReturnStmt_getRetValue(stmt); - if (value_expr == nullptr) { - return trans_create_node(c, NodeTypeReturnExpr); - } else { - AstNode *return_node = trans_create_node(c, NodeTypeReturnExpr); - return_node->data.return_expr.expr = trans_expr(c, ResultUsedYes, scope, value_expr, TransRValue); - if (return_node->data.return_expr.expr == nullptr) - return nullptr; - return return_node; - } -} - -static AstNode *trans_integer_literal(Context *c, ResultUsed result_used, const ZigClangIntegerLiteral *stmt) { - ZigClangExprEvalResult result; - if (!ZigClangIntegerLiteral_EvaluateAsInt(stmt, &result, c->ctx)) { - emit_warning(c, ZigClangExpr_getBeginLoc((ZigClangExpr*)stmt), "invalid integer literal"); - return nullptr; - } - AstNode *node = trans_create_node_apint(c, ZigClangAPValue_getInt(&result.Val)); - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_floating_literal(Context *c, ResultUsed result_used, const ZigClangFloatingLiteral *stmt) { - llvm::APFloat result{0.0f}; - if (!reinterpret_cast(stmt)->EvaluateAsFloat(result, *reinterpret_cast(c->ctx))) { - emit_warning(c, ZigClangExpr_getBeginLoc((ZigClangExpr*)stmt), "invalid floating literal"); - return nullptr; - } - AstNode *node = trans_create_node_apfloat(c, result); - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_character_literal(Context *c, ResultUsed result_used, const ZigClangCharacterLiteral *stmt) { - switch (ZigClangCharacterLiteral_getKind(stmt)) { - case ZigClangCharacterLiteral_CharacterKind_Ascii: - { - unsigned val = ZigClangCharacterLiteral_getValue(stmt); - // C has a somewhat obscure feature called multi-character character - // constant - if (val > 255) - return trans_create_node_unsigned(c, val); - } - // fallthrough - case ZigClangCharacterLiteral_CharacterKind_UTF8: - { - AstNode *node = trans_create_node(c, NodeTypeCharLiteral); - node->data.char_literal.value = ZigClangCharacterLiteral_getValue(stmt); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangCharacterLiteral_CharacterKind_UTF16: - emit_warning(c, ZigClangCharacterLiteral_getBeginLoc(stmt), "TODO support UTF16 character literals"); - return nullptr; - case ZigClangCharacterLiteral_CharacterKind_UTF32: - emit_warning(c, ZigClangCharacterLiteral_getBeginLoc(stmt), "TODO support UTF32 character literals"); - return nullptr; - case ZigClangCharacterLiteral_CharacterKind_Wide: - emit_warning(c, ZigClangCharacterLiteral_getBeginLoc(stmt), "TODO support wide character literals"); - return nullptr; - } - zig_unreachable(); -} - -static AstNode *trans_constant_expr(Context *c, ResultUsed result_used, const ZigClangConstantExpr *expr) { - const ZigClangExpr *as_expr = reinterpret_cast(expr); - clang::Expr::EvalResult result; - if (!reinterpret_cast(expr)->EvaluateAsConstantExpr(result, - clang::Expr::EvaluateForCodeGen, - *reinterpret_cast(c->ctx))) - { - emit_warning(c, ZigClangExpr_getBeginLoc(as_expr), "invalid constant expression"); - return nullptr; - } - AstNode *node = trans_ap_value(c, bitcast(&result.Val), ZigClangExpr_getType(as_expr), - ZigClangExpr_getBeginLoc(as_expr)); - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangConditionalOperator *stmt) -{ - AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr); - - const ZigClangExpr *cond_expr = ZigClangConditionalOperator_getCond(stmt); - const ZigClangExpr *true_expr = ZigClangConditionalOperator_getTrueExpr(stmt); - const ZigClangExpr *false_expr = ZigClangConditionalOperator_getFalseExpr(stmt); - - node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue); - if (node->data.if_bool_expr.condition == nullptr) - return nullptr; - - node->data.if_bool_expr.then_block = trans_expr(c, result_used, scope, true_expr, TransRValue); - if (node->data.if_bool_expr.then_block == nullptr) - return nullptr; - - node->data.if_bool_expr.else_node = trans_expr(c, result_used, scope, false_expr, TransRValue); - if (node->data.if_bool_expr.else_node == nullptr) - return nullptr; - - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_create_bin_op(Context *c, TransScope *scope, const ZigClangExpr *lhs, - BinOpType bin_op, const ZigClangExpr *rhs) -{ - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.bin_op = bin_op; - - node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransRValue); - if (node->data.bin_op_expr.op1 == nullptr) - return nullptr; - - node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue); - if (node->data.bin_op_expr.op2 == nullptr) - return nullptr; - - return node; -} - -static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, const ZigClangExpr *lhs, - BinOpType bin_op, const ZigClangExpr *rhs) -{ - assert(bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeBoolOr); - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.bin_op = bin_op; - - node->data.bin_op_expr.op1 = trans_bool_expr(c, ResultUsedYes, scope, lhs, TransRValue); - if (node->data.bin_op_expr.op1 == nullptr) - return nullptr; - - node->data.bin_op_expr.op2 = trans_bool_expr(c, ResultUsedYes, scope, rhs, TransRValue); - if (node->data.bin_op_expr.op2 == nullptr) - return nullptr; - - return node; -} - -static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangExpr *lhs, const ZigClangExpr *rhs) -{ - if (result_used == ResultUsedNo) { - // common case - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.bin_op = BinOpTypeAssign; - - node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransLValue); - if (node->data.bin_op_expr.op1 == nullptr) - return nullptr; - - node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue); - if (node->data.bin_op_expr.op2 == nullptr) - return nullptr; - - return node; - } else { - // worst case - // c: lhs = rhs - // zig: (x: { - // zig: const _tmp = rhs; - // zig: lhs = _tmp; - // zig: break :x _tmp - // zig: }) - - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - child_scope->node->data.block.name = label_name; - - // const _tmp = rhs; - AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue); - if (rhs_node == nullptr) return nullptr; - // TODO: avoid name collisions with generated variable names - Buf* tmp_var_name = buf_create_from_str("_tmp"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, rhs_node); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // lhs = _tmp; - AstNode *lhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, lhs, TransLValue); - if (lhs_node == nullptr) return nullptr; - child_scope->node->data.block.statements.append( - trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign, - trans_create_node_symbol(c, tmp_var_name))); - - // break :x _tmp - AstNode *tmp_symbol_node = trans_create_node_symbol(c, tmp_var_name); - child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, tmp_symbol_node)); - - return trans_create_node_grouped_expr(c, child_scope->node); - } -} - -static AstNode *trans_create_shift_op(Context *c, TransScope *scope, ZigClangQualType result_type, - const ZigClangExpr *lhs_expr, BinOpType bin_op, const ZigClangExpr *rhs_expr) -{ - ZigClangSourceLocation rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); - AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location); - // lhs >> u5(rh) - - AstNode *lhs = trans_expr(c, ResultUsedYes, scope, lhs_expr, TransLValue); - if (lhs == nullptr) return nullptr; - - AstNode *rhs = trans_expr(c, ResultUsedYes, scope, rhs_expr, TransRValue); - if (rhs == nullptr) return nullptr; - AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); - - return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs); -} - -static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangBinaryOperator *stmt) -{ - switch (ZigClangBinaryOperator_getOpcode(stmt)) { - case ZigClangBO_PtrMemD: - emit_warning(c, ZigClangBinaryOperator_getBeginLoc(stmt), "TODO handle more C binary operators: BO_PtrMemD"); - return nullptr; - case ZigClangBO_PtrMemI: - emit_warning(c, ZigClangBinaryOperator_getBeginLoc(stmt), "TODO handle more C binary operators: BO_PtrMemI"); - return nullptr; - case ZigClangBO_Cmp: - emit_warning(c, ZigClangBinaryOperator_getBeginLoc(stmt), "TODO handle more C binary operators: BO_Cmp"); - return nullptr; - case ZigClangBO_Mul: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), - qual_type_has_wrapping_overflow(c, ZigClangBinaryOperator_getType(stmt)) ? BinOpTypeMultWrap : BinOpTypeMult, - ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Div: - if (qual_type_has_wrapping_overflow(c, ZigClangBinaryOperator_getType(stmt))) { - // unsigned/float division uses the operator - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeDiv, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } else { - // signed integer division uses @divTrunc - AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "divTrunc"); - AstNode *lhs = trans_expr(c, ResultUsedYes, scope, ZigClangBinaryOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - fn_call->data.fn_call_expr.params.append(lhs); - AstNode *rhs = trans_expr(c, ResultUsedYes, scope, ZigClangBinaryOperator_getRHS(stmt), TransLValue); - if (rhs == nullptr) return nullptr; - fn_call->data.fn_call_expr.params.append(rhs); - return maybe_suppress_result(c, result_used, fn_call); - } - case ZigClangBO_Rem: - if (qual_type_has_wrapping_overflow(c, ZigClangBinaryOperator_getType(stmt))) { - // unsigned/float division uses the operator - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeMod, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } else { - // signed integer division uses @rem - AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem"); - AstNode *lhs = trans_expr(c, ResultUsedYes, scope, ZigClangBinaryOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - fn_call->data.fn_call_expr.params.append(lhs); - AstNode *rhs = trans_expr(c, ResultUsedYes, scope, ZigClangBinaryOperator_getRHS(stmt), TransLValue); - if (rhs == nullptr) return nullptr; - fn_call->data.fn_call_expr.params.append(rhs); - return maybe_suppress_result(c, result_used, fn_call); - } - case ZigClangBO_Add: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), - qual_type_has_wrapping_overflow(c, ZigClangBinaryOperator_getType(stmt)) ? BinOpTypeAddWrap : BinOpTypeAdd, - ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Sub: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), - qual_type_has_wrapping_overflow(c, ZigClangBinaryOperator_getType(stmt)) ? BinOpTypeSubWrap : BinOpTypeSub, - ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Shl: { - AstNode *node = trans_create_shift_op(c, scope, ZigClangBinaryOperator_getType(stmt), ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBitShiftLeft, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Shr: { - AstNode *node = trans_create_shift_op(c, scope, ZigClangBinaryOperator_getType(stmt), ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBitShiftRight, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_LT: { - AstNode *node =trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpLessThan, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_GT: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpGreaterThan, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_LE: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpLessOrEq, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_GE: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpGreaterOrEq, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_EQ: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpEq, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_NE: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeCmpNotEq, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_And: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBinAnd, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Xor: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBinXor, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Or: { - AstNode *node = trans_create_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBinOr, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_LAnd: { - AstNode *node = trans_create_bool_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBoolAnd, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_LOr: { - AstNode *node = trans_create_bool_bin_op(c, scope, ZigClangBinaryOperator_getLHS(stmt), BinOpTypeBoolOr, ZigClangBinaryOperator_getRHS(stmt)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangBO_Assign: - return trans_create_assign(c, result_used, scope, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)); - case ZigClangBO_Comma: - { - TransScopeBlock *scope_block = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - scope_block->node->data.block.name = label_name; - - AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, ZigClangBinaryOperator_getLHS(stmt), TransRValue); - if (lhs == nullptr) - return nullptr; - scope_block->node->data.block.statements.append(lhs); - - AstNode *rhs = trans_expr(c, ResultUsedYes, &scope_block->base, ZigClangBinaryOperator_getRHS(stmt), TransRValue); - if (rhs == nullptr) - return nullptr; - - rhs = trans_create_node_break(c, label_name, rhs); - scope_block->node->data.block.statements.append(rhs); - return maybe_suppress_result(c, result_used, scope_block->node); - } - case ZigClangBO_MulAssign: - case ZigClangBO_DivAssign: - case ZigClangBO_RemAssign: - case ZigClangBO_AddAssign: - case ZigClangBO_SubAssign: - case ZigClangBO_ShlAssign: - case ZigClangBO_ShrAssign: - case ZigClangBO_AndAssign: - case ZigClangBO_XorAssign: - case ZigClangBO_OrAssign: - zig_unreachable(); - } - - zig_unreachable(); -} - -static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangCompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) -{ - ZigClangSourceLocation rhs_location = ZigClangExpr_getBeginLoc(ZigClangCompoundAssignOperator_getRHS(stmt)); - ZigClangQualType computation_lhs_type = ZigClangCompoundAssignOperator_getComputationLHSType(stmt); - AstNode *rhs_type = qual_type_to_log2_int_ref(c, computation_lhs_type, rhs_location); - ZigClangQualType computation_result_type = ZigClangCompoundAssignOperator_getComputationResultType(stmt); - - bool use_intermediate_casts = ZigClangQualType_getTypePtr(computation_lhs_type) != - ZigClangQualType_getTypePtr(computation_result_type); - if (!use_intermediate_casts && result_used == ResultUsedNo) { - // simple common case, where the C and Zig are identical: - // lhs >>= rhs - AstNode *lhs = trans_expr(c, ResultUsedYes, scope, ZigClangCompoundAssignOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - - AstNode *rhs = trans_expr(c, ResultUsedYes, scope, ZigClangCompoundAssignOperator_getRHS(stmt), TransRValue); - if (rhs == nullptr) return nullptr; - AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); - - return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs); - } else { - // need more complexity. worst case, this looks like this: - // c: lhs >>= rhs - // zig: (x: { - // zig: const _ref = &lhs; - // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs)); - // zig: break :x *_ref - // zig: }) - // where u5 is the appropriate type - - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - child_scope->node->data.block.name = label_name; - - // const _ref = &lhs; - AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, - ZigClangCompoundAssignOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); - // TODO: avoid name collisions with generated variable names - Buf* tmp_var_name = buf_create_from_str("_ref"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // *_ref = result_type(operation_type(*_ref) >> u5(rhs)); - - AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, ZigClangCompoundAssignOperator_getRHS(stmt), TransRValue); - if (rhs == nullptr) return nullptr; - AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); - - // operation_type(*_ref) - AstNode *operation_type_cast = trans_c_cast(c, rhs_location, - computation_lhs_type, - ZigClangExpr_getType(ZigClangCompoundAssignOperator_getLHS(stmt)), - trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name))); - - // result_type(... >> u5(rhs)) - AstNode *result_type_cast = trans_c_cast(c, rhs_location, - computation_result_type, - computation_lhs_type, - trans_create_node_bin_op(c, - operation_type_cast, - bin_op, - coerced_rhs)); - - // *_ref = ... - AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, tmp_var_name)), - BinOpTypeAssign, result_type_cast); - - child_scope->node->data.block.statements.append(assign_statement); - - if (result_used == ResultUsedYes) { - // break :x *_ref - child_scope->node->data.block.statements.append( - trans_create_node_break(c, label_name, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, tmp_var_name)))); - } - - return trans_create_node_grouped_expr(c, child_scope->node); - } -} - -static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangCompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) -{ - if (result_used == ResultUsedNo) { - // simple common case, where the C and Zig are identical: - // lhs += rhs - AstNode *lhs = trans_expr(c, ResultUsedYes, scope, ZigClangCompoundAssignOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - AstNode *rhs = trans_expr(c, ResultUsedYes, scope, ZigClangCompoundAssignOperator_getRHS(stmt), TransRValue); - if (rhs == nullptr) return nullptr; - return trans_create_node_bin_op(c, lhs, assign_op, rhs); - } else { - // need more complexity. worst case, this looks like this: - // c: lhs += rhs - // zig: (x: { - // zig: const _ref = &lhs; - // zig: *_ref = *_ref + rhs; - // zig: break :x *_ref - // zig: }) - - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - child_scope->node->data.block.name = label_name; - - // const _ref = &lhs; - AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, - ZigClangCompoundAssignOperator_getLHS(stmt), TransLValue); - if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); - // TODO: avoid name collisions with generated variable names - Buf* tmp_var_name = buf_create_from_str("_ref"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // *_ref = *_ref + rhs; - - AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, - ZigClangCompoundAssignOperator_getRHS(stmt), TransRValue); - if (rhs == nullptr) return nullptr; - - AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, tmp_var_name)), - BinOpTypeAssign, - trans_create_node_bin_op(c, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, tmp_var_name)), - bin_op, - rhs)); - child_scope->node->data.block.statements.append(assign_statement); - - // break :x *_ref - child_scope->node->data.block.statements.append( - trans_create_node_break(c, label_name, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, tmp_var_name)))); - - return trans_create_node_grouped_expr(c, child_scope->node); - } -} - - -static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangCompoundAssignOperator *stmt) -{ - switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) { - case ZigClangBO_MulAssign: - if (qual_type_has_wrapping_overflow(c, ZigClangCompoundAssignOperator_getType(stmt))) - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap); - else - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimes, BinOpTypeMult); - case ZigClangBO_DivAssign: - emit_warning(c, ZigClangCompoundAssignOperator_getBeginLoc(stmt), "TODO handle more C compound assign operators: BO_DivAssign"); - return nullptr; - case ZigClangBO_RemAssign: - emit_warning(c, ZigClangCompoundAssignOperator_getBeginLoc(stmt), "TODO handle more C compound assign operators: BO_RemAssign"); - return nullptr; - case ZigClangBO_Cmp: - emit_warning(c, ZigClangCompoundAssignOperator_getBeginLoc(stmt), "TODO handle more C compound assign operators: BO_Cmp"); - return nullptr; - case ZigClangBO_AddAssign: - if (qual_type_has_wrapping_overflow(c, ZigClangCompoundAssignOperator_getType(stmt))) - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap); - else - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlus, BinOpTypeAdd); - case ZigClangBO_SubAssign: - if (qual_type_has_wrapping_overflow(c, ZigClangCompoundAssignOperator_getType(stmt))) - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap); - else - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinus, BinOpTypeSub); - case ZigClangBO_ShlAssign: - return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft); - case ZigClangBO_ShrAssign: - return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight); - case ZigClangBO_AndAssign: - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd); - case ZigClangBO_XorAssign: - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor); - case ZigClangBO_OrAssign: - return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr); - case ZigClangBO_PtrMemD: - case ZigClangBO_PtrMemI: - case ZigClangBO_Assign: - case ZigClangBO_Mul: - case ZigClangBO_Div: - case ZigClangBO_Rem: - case ZigClangBO_Add: - case ZigClangBO_Sub: - case ZigClangBO_Shl: - case ZigClangBO_Shr: - case ZigClangBO_LT: - case ZigClangBO_GT: - case ZigClangBO_LE: - case ZigClangBO_GE: - case ZigClangBO_EQ: - case ZigClangBO_NE: - case ZigClangBO_And: - case ZigClangBO_Xor: - case ZigClangBO_Or: - case ZigClangBO_LAnd: - case ZigClangBO_LOr: - case ZigClangBO_Comma: - zig_unreachable(); - } - - zig_unreachable(); -} - -static AstNode *trans_implicit_cast_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangImplicitCastExpr *stmt) -{ - switch (ZigClangImplicitCastExpr_getCastKind(stmt)) { - case ZigClangCK_LValueToRValue: - return trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - case ZigClangCK_IntegralCast: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - AstNode *node = trans_c_cast(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), - ZigClangExpr_getType(reinterpret_cast(stmt)), - ZigClangExpr_getType(ZigClangImplicitCastExpr_getSubExpr(stmt)), - target_node); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangCK_FunctionToPointerDecay: - case ZigClangCK_ArrayToPointerDecay: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - return maybe_suppress_result(c, result_used, target_node); - } - case ZigClangCK_BitCast: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - - const ZigClangQualType dest_type = get_expr_qual_type(c, reinterpret_cast(stmt)); - const ZigClangQualType src_type = get_expr_qual_type(c, ZigClangImplicitCastExpr_getSubExpr(stmt)); - - return trans_c_cast(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), - dest_type, src_type, target_node); - } - case ZigClangCK_NullToPointer: - return trans_create_node(c, NodeTypeNullLiteral); - case ZigClangCK_NoOp: - return trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - case ZigClangCK_Dependent: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_Dependent"); - return nullptr; - case ZigClangCK_LValueBitCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_LValueBitCast"); - return nullptr; - case ZigClangCK_BaseToDerived: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_BaseToDerived"); - return nullptr; - case ZigClangCK_DerivedToBase: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_DerivedToBase"); - return nullptr; - case ZigClangCK_UncheckedDerivedToBase: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_UncheckedDerivedToBase"); - return nullptr; - case ZigClangCK_Dynamic: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_Dynamic"); - return nullptr; - case ZigClangCK_ToUnion: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_ToUnion"); - return nullptr; - case ZigClangCK_NullToMemberPointer: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_NullToMemberPointer"); - return nullptr; - case ZigClangCK_BaseToDerivedMemberPointer: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_BaseToDerivedMemberPointer"); - return nullptr; - case ZigClangCK_DerivedToBaseMemberPointer: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_DerivedToBaseMemberPointer"); - return nullptr; - case ZigClangCK_MemberPointerToBoolean: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_MemberPointerToBoolean"); - return nullptr; - case ZigClangCK_ReinterpretMemberPointer: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_ReinterpretMemberPointer"); - return nullptr; - case ZigClangCK_UserDefinedConversion: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C translation cast CK_UserDefinedConversion"); - return nullptr; - case ZigClangCK_ConstructorConversion: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ConstructorConversion"); - return nullptr; - case ZigClangCK_PointerToBoolean: - { - const ZigClangExpr *expr = ZigClangImplicitCastExpr_getSubExpr(stmt); - AstNode *val = trans_expr(c, ResultUsedYes, scope, expr, TransRValue); - if (val == nullptr) - return nullptr; - - AstNode *val_ptr = trans_create_node_builtin_fn_call_str(c, "ptrToInt"); - val_ptr->data.fn_call_expr.params.append(val); - - AstNode *zero = trans_create_node_unsigned(c, 0); - - // Translate as @ptrToInt((&val) != 0) - return trans_create_node_bin_op(c, val_ptr, BinOpTypeCmpNotEq, zero); - } - case ZigClangCK_ToVoid: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ToVoid"); - return nullptr; - case ZigClangCK_VectorSplat: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_VectorSplat"); - return nullptr; - case ZigClangCK_IntegralToBoolean: - { - const ZigClangExpr *expr = ZigClangImplicitCastExpr_getSubExpr(stmt); - - bool expr_val; - if (ZigClangExpr_EvaluateAsBooleanCondition(expr, &expr_val, c->ctx, false)) { - return trans_create_node_bool(c, expr_val); - } - - AstNode *val = trans_expr(c, ResultUsedYes, scope, expr, TransRValue); - if (val == nullptr) - return nullptr; - - AstNode *zero = trans_create_node_unsigned(c, 0); - - // Translate as val != 0 - return trans_create_node_bin_op(c, val, BinOpTypeCmpNotEq, zero); - } - case ZigClangCK_PointerToIntegral: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - - AstNode *dest_type_node = get_expr_type(c, (const ZigClangExpr *)stmt); - if (dest_type_node == nullptr) - return nullptr; - - AstNode *val_node = trans_create_node_builtin_fn_call_str(c, "ptrToInt"); - val_node->data.fn_call_expr.params.append(target_node); - // @ptrToInt always returns a usize - AstNode *node = trans_create_node_builtin_fn_call_str(c, "intCast"); - node->data.fn_call_expr.params.append(dest_type_node); - node->data.fn_call_expr.params.append(val_node); - - return maybe_suppress_result(c, result_used, node); - } - case ZigClangCK_IntegralToPointer: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - - AstNode *dest_type_node = get_expr_type(c, (const ZigClangExpr *)stmt); - if (dest_type_node == nullptr) - return nullptr; - - AstNode *node = trans_create_node_builtin_fn_call_str(c, "intToPtr"); - node->data.fn_call_expr.params.append(dest_type_node); - node->data.fn_call_expr.params.append(target_node); - - return maybe_suppress_result(c, result_used, node); - } - case ZigClangCK_IntegralToFloating: - case ZigClangCK_FloatingToIntegral: - { - AstNode *target_node = trans_expr(c, ResultUsedYes, scope, ZigClangImplicitCastExpr_getSubExpr(stmt), TransRValue); - if (target_node == nullptr) - return nullptr; - - AstNode *dest_type_node = get_expr_type(c, (const ZigClangExpr *)stmt); - if (dest_type_node == nullptr) - return nullptr; - - char const *fn = (ZigClangImplicitCastExpr_getCastKind(stmt) == ZigClangCK_IntegralToFloating) ? - "intToFloat" : "floatToInt"; - AstNode *node = trans_create_node_builtin_fn_call_str(c, fn); - node->data.fn_call_expr.params.append(dest_type_node); - node->data.fn_call_expr.params.append(target_node); - - return maybe_suppress_result(c, result_used, node); - } - case ZigClangCK_FixedPointCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FixedPointCast"); - return nullptr; - case ZigClangCK_FixedPointToBoolean: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FixedPointToBoolean"); - return nullptr; - case ZigClangCK_FloatingToBoolean: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingToBoolean"); - return nullptr; - case ZigClangCK_BooleanToSignedIntegral: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_BooleanToSignedIntegral"); - return nullptr; - case ZigClangCK_FloatingCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingCast"); - return nullptr; - case ZigClangCK_CPointerToObjCPointerCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_CPointerToObjCPointerCast"); - return nullptr; - case ZigClangCK_BlockPointerToObjCPointerCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_BlockPointerToObjCPointerCast"); - return nullptr; - case ZigClangCK_AnyPointerToBlockPointerCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_AnyPointerToBlockPointerCast"); - return nullptr; - case ZigClangCK_ObjCObjectLValueCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ObjCObjectLValueCast"); - return nullptr; - case ZigClangCK_FloatingRealToComplex: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingRealToComplex"); - return nullptr; - case ZigClangCK_FloatingComplexToReal: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingComplexToReal"); - return nullptr; - case ZigClangCK_FloatingComplexToBoolean: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingComplexToBoolean"); - return nullptr; - case ZigClangCK_FloatingComplexCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingComplexCast"); - return nullptr; - case ZigClangCK_FloatingComplexToIntegralComplex: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FloatingComplexToIntegralComplex"); - return nullptr; - case ZigClangCK_IntegralRealToComplex: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralRealToComplex"); - return nullptr; - case ZigClangCK_IntegralComplexToReal: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralComplexToReal"); - return nullptr; - case ZigClangCK_IntegralComplexToBoolean: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralComplexToBoolean"); - return nullptr; - case ZigClangCK_IntegralComplexCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralComplexCast"); - return nullptr; - case ZigClangCK_IntegralComplexToFloatingComplex: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralComplexToFloatingComplex"); - return nullptr; - case ZigClangCK_ARCProduceObject: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ARCProduceObject"); - return nullptr; - case ZigClangCK_ARCConsumeObject: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ARCConsumeObject"); - return nullptr; - case ZigClangCK_ARCReclaimReturnedObject: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ARCReclaimReturnedObject"); - return nullptr; - case ZigClangCK_ARCExtendBlockObject: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ARCExtendBlockObject"); - return nullptr; - case ZigClangCK_AtomicToNonAtomic: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_AtomicToNonAtomic"); - return nullptr; - case ZigClangCK_NonAtomicToAtomic: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_NonAtomicToAtomic"); - return nullptr; - case ZigClangCK_CopyAndAutoreleaseBlockObject: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_CopyAndAutoreleaseBlockObject"); - return nullptr; - case ZigClangCK_BuiltinFnToFnPtr: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_BuiltinFnToFnPtr"); - return nullptr; - case ZigClangCK_ZeroToOCLOpaqueType: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_ZeroToOCLOpaqueType"); - return nullptr; - case ZigClangCK_AddressSpaceConversion: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_AddressSpaceConversion"); - return nullptr; - case ZigClangCK_IntToOCLSampler: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntToOCLSampler"); - return nullptr; - case ZigClangCK_LValueToRValueBitCast: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_LValueToRValueBitCast"); - return nullptr; - case ZigClangCK_FixedPointToIntegral: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_FixedPointToIntegral"); - return nullptr; - case ZigClangCK_IntegralToFixedPoint: - emit_warning(c, ZigClangImplicitCastExpr_getBeginLoc(stmt), "TODO handle C CK_IntegralToFixedPointral"); - return nullptr; - } - zig_unreachable(); -} - -static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const ZigClangDeclRefExpr *stmt, TransLRValue lrval) { - const ZigClangValueDecl *value_decl = ZigClangDeclRefExpr_getDecl(stmt); - Buf *c_symbol_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)value_decl)); - Buf *zig_symbol_name = trans_lookup_zig_symbol(c, scope, c_symbol_name); - if (lrval == TransLValue) { - c->ptr_params.put(zig_symbol_name, true); - } - return trans_create_node_symbol(c, zig_symbol_name); -} - -static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangUnaryOperator *stmt, BinOpType assign_op) -{ - const ZigClangExpr *op_expr = ZigClangUnaryOperator_getSubExpr(stmt); - - if (result_used == ResultUsedNo) { - // common case - // c: expr++ - // zig: expr += 1 - return trans_create_node_bin_op(c, - trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue), - assign_op, - trans_create_node_unsigned(c, 1)); - } - // worst case - // c: expr++ - // zig: (x: { - // zig: const _ref = &expr; - // zig: const _tmp = *_ref; - // zig: *_ref += 1; - // zig: break :x _tmp - // zig: }) - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - child_scope->node->data.block.name = label_name; - - // const _ref = &expr; - AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); - if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); - // TODO: avoid name collisions with generated variable names - Buf* ref_var_name = buf_create_from_str("_ref"); - AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); - child_scope->node->data.block.statements.append(ref_var_decl); - - // const _tmp = *_ref; - Buf* tmp_var_name = buf_create_from_str("_tmp"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, ref_var_name))); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // *_ref += 1; - AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, ref_var_name)), - assign_op, - trans_create_node_unsigned(c, 1)); - child_scope->node->data.block.statements.append(assign_statement); - - // break :x _tmp - child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, trans_create_node_symbol(c, tmp_var_name))); - - return trans_create_node_grouped_expr(c, child_scope->node); -} - -static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangUnaryOperator *stmt, BinOpType assign_op) -{ - const ZigClangExpr *op_expr = ZigClangUnaryOperator_getSubExpr(stmt); - - if (result_used == ResultUsedNo) { - // common case - // c: ++expr - // zig: expr += 1 - return trans_create_node_bin_op(c, - trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue), - assign_op, - trans_create_node_unsigned(c, 1)); - } - // worst case - // c: ++expr - // zig: (x: { - // zig: const _ref = &expr; - // zig: *_ref += 1; - // zig: break :x *_ref - // zig: }) - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("x"); - child_scope->node->data.block.name = label_name; - - // const _ref = &expr; - AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); - if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); - // TODO: avoid name collisions with generated variable names - Buf* ref_var_name = buf_create_from_str("_ref"); - AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); - child_scope->node->data.block.statements.append(ref_var_decl); - - // *_ref += 1; - AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, ref_var_name)), - assign_op, - trans_create_node_unsigned(c, 1)); - child_scope->node->data.block.statements.append(assign_statement); - - // break :x *_ref - AstNode *deref_expr = trans_create_node_ptr_deref(c, - trans_create_node_symbol(c, ref_var_name)); - child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr)); - - return trans_create_node_grouped_expr(c, child_scope->node); -} - -static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangUnaryOperator *stmt) -{ - switch (ZigClangUnaryOperator_getOpcode(stmt)) { - case ZigClangUO_PostInc: - if (qual_type_has_wrapping_overflow(c, ZigClangUnaryOperator_getType(stmt))) - return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); - else - return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case ZigClangUO_PostDec: - if (qual_type_has_wrapping_overflow(c, ZigClangUnaryOperator_getType(stmt))) - return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); - else - return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case ZigClangUO_PreInc: - if (qual_type_has_wrapping_overflow(c, ZigClangUnaryOperator_getType(stmt))) - return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); - else - return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case ZigClangUO_PreDec: - if (qual_type_has_wrapping_overflow(c, ZigClangUnaryOperator_getType(stmt))) - return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); - else - return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case ZigClangUO_AddrOf: - { - AstNode *value_node = trans_expr(c, result_used, scope, ZigClangUnaryOperator_getSubExpr(stmt), TransLValue); - if (value_node == nullptr) - return value_node; - return trans_create_node_addr_of(c, value_node); - } - case ZigClangUO_Deref: - { - AstNode *value_node = trans_expr(c, result_used, scope, ZigClangUnaryOperator_getSubExpr(stmt), TransRValue); - if (value_node == nullptr) - return nullptr; - bool is_fn_ptr = qual_type_is_fn_ptr(ZigClangExpr_getType(ZigClangUnaryOperator_getSubExpr(stmt))); - if (is_fn_ptr) - return value_node; - AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node); - return trans_create_node_ptr_deref(c, unwrapped); - } - case ZigClangUO_Plus: - emit_warning(c, ZigClangUnaryOperator_getBeginLoc(stmt), "TODO handle C translation UO_Plus"); - return nullptr; - case ZigClangUO_Minus: - { - const ZigClangExpr *op_expr = ZigClangUnaryOperator_getSubExpr(stmt); - if (!qual_type_has_wrapping_overflow(c, ZigClangExpr_getType(op_expr))) { - AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); - node->data.prefix_op_expr.prefix_op = PrefixOpNegation; - - node->data.prefix_op_expr.primary_expr = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue); - if (node->data.prefix_op_expr.primary_expr == nullptr) - return nullptr; - - return node; - } else if (c_is_unsigned_integer(c, ZigClangExpr_getType(op_expr))) { - // we gotta emit 0 -% x - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0); - - node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue); - if (node->data.bin_op_expr.op2 == nullptr) - return nullptr; - - node->data.bin_op_expr.bin_op = BinOpTypeSubWrap; - return node; - } else { - emit_warning(c, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer"); - return nullptr; - } - } - case ZigClangUO_Not: - { - const ZigClangExpr *op_expr = ZigClangUnaryOperator_getSubExpr(stmt); - AstNode *sub_node = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue); - if (sub_node == nullptr) - return nullptr; - - return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); - } - case ZigClangUO_LNot: - { - const ZigClangExpr *op_expr = ZigClangUnaryOperator_getSubExpr(stmt); - AstNode *sub_node = trans_bool_expr(c, ResultUsedYes, scope, op_expr, TransRValue); - if (sub_node == nullptr) - return nullptr; - - return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); - } - case ZigClangUO_Real: - emit_warning(c, ZigClangUnaryOperator_getBeginLoc(stmt), "TODO handle C translation UO_Real"); - return nullptr; - case ZigClangUO_Imag: - emit_warning(c, ZigClangUnaryOperator_getBeginLoc(stmt), "TODO handle C translation UO_Imag"); - return nullptr; - case ZigClangUO_Extension: - return trans_expr(c, result_used, scope, ZigClangUnaryOperator_getSubExpr(stmt), TransLValue); - case ZigClangUO_Coawait: - emit_warning(c, ZigClangUnaryOperator_getBeginLoc(stmt), "TODO handle C translation UO_Coawait"); - return nullptr; - } - zig_unreachable(); -} - -static int trans_local_declaration(Context *c, TransScope *scope, const ZigClangDeclStmt *stmt, - AstNode **out_node, TransScope **out_scope) -{ - // declarations are added via the scope - *out_node = nullptr; - - TransScopeBlock *scope_block = trans_scope_block_find(scope); - assert(scope_block != nullptr); - - for (auto iter = reinterpret_cast(stmt)->decl_begin(); - iter != reinterpret_cast(stmt)->decl_end(); iter++) { - ZigClangDecl *decl = reinterpret_cast(*iter); - switch (ZigClangDecl_getKind(decl)) { - case ZigClangDeclVar: { - clang::VarDecl *var_decl = (clang::VarDecl *)decl; - ZigClangQualType qual_type = bitcast(var_decl->getTypeSourceInfo()->getType()); - AstNode *init_node = nullptr; - if (var_decl->hasInit()) { - init_node = trans_expr(c, ResultUsedYes, scope, bitcast(var_decl->getInit()), TransRValue); - if (init_node == nullptr) - return ErrorUnexpected; - - } else { - init_node = trans_create_node(c, NodeTypeUndefinedLiteral); - } - AstNode *type_node = trans_qual_type(c, qual_type, ZigClangDeclStmt_getBeginLoc(stmt)); - if (type_node == nullptr) - return ErrorUnexpected; - - Buf *c_symbol_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)var_decl)); - - TransScopeVar *var_scope = trans_scope_var_create(c, scope, c_symbol_name); - scope = &var_scope->base; - - AstNode *node = trans_create_node_var_decl_local(c, - ZigClangQualType_isConstQualified(qual_type), - var_scope->zig_name, type_node, init_node); - - scope_block->node->data.block.statements.append(node); - continue; - } - case ZigClangDeclAccessSpec: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle decl kind AccessSpec"); - return ErrorUnexpected; - case ZigClangDeclBlock: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Block"); - return ErrorUnexpected; - case ZigClangDeclCaptured: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Captured"); - return ErrorUnexpected; - case ZigClangDeclClassScopeFunctionSpecialization: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ClassScopeFunctionSpecialization"); - return ErrorUnexpected; - case ZigClangDeclEmpty: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Empty"); - return ErrorUnexpected; - case ZigClangDeclExport: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Export"); - return ErrorUnexpected; - case ZigClangDeclExternCContext: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ExternCContext"); - return ErrorUnexpected; - case ZigClangDeclFileScopeAsm: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C FileScopeAsm"); - return ErrorUnexpected; - case ZigClangDeclFriend: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Friend"); - return ErrorUnexpected; - case ZigClangDeclFriendTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C FriendTemplate"); - return ErrorUnexpected; - case ZigClangDeclImport: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Import"); - return ErrorUnexpected; - case ZigClangDeclLinkageSpec: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C LinkageSpec"); - return ErrorUnexpected; - case ZigClangDeclLabel: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Label"); - return ErrorUnexpected; - case ZigClangDeclNamespace: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Namespace"); - return ErrorUnexpected; - case ZigClangDeclNamespaceAlias: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C NamespaceAlias"); - return ErrorUnexpected; - case ZigClangDeclObjCCompatibleAlias: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCCompatibleAlias"); - return ErrorUnexpected; - case ZigClangDeclObjCCategory: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCCategory"); - return ErrorUnexpected; - case ZigClangDeclObjCCategoryImpl: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCCategoryImpl"); - return ErrorUnexpected; - case ZigClangDeclObjCImplementation: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCImplementation"); - return ErrorUnexpected; - case ZigClangDeclObjCInterface: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCInterface"); - return ErrorUnexpected; - case ZigClangDeclObjCProtocol: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCProtocol"); - return ErrorUnexpected; - case ZigClangDeclObjCMethod: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCMethod"); - return ErrorUnexpected; - case ZigClangDeclObjCProperty: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCProperty"); - return ErrorUnexpected; - case ZigClangDeclBuiltinTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C BuiltinTemplate"); - return ErrorUnexpected; - case ZigClangDeclClassTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ClassTemplate"); - return ErrorUnexpected; - case ZigClangDeclFunctionTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C FunctionTemplate"); - return ErrorUnexpected; - case ZigClangDeclTypeAliasTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C TypeAliasTemplate"); - return ErrorUnexpected; - case ZigClangDeclVarTemplate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C VarTemplate"); - return ErrorUnexpected; - case ZigClangDeclTemplateTemplateParm: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C TemplateTemplateParm"); - return ErrorUnexpected; - case ZigClangDeclEnum: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Enum"); - return ErrorUnexpected; - case ZigClangDeclRecord: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Record"); - return ErrorUnexpected; - case ZigClangDeclCXXRecord: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXRecord"); - return ErrorUnexpected; - case ZigClangDeclClassTemplateSpecialization: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ClassTemplateSpecialization"); - return ErrorUnexpected; - case ZigClangDeclClassTemplatePartialSpecialization: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ClassTemplatePartialSpecialization"); - return ErrorUnexpected; - case ZigClangDeclTemplateTypeParm: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C TemplateTypeParm"); - return ErrorUnexpected; - case ZigClangDeclObjCTypeParam: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCTypeParam"); - return ErrorUnexpected; - case ZigClangDeclTypeAlias: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C TypeAlias"); - return ErrorUnexpected; - case ZigClangDeclTypedef: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Typedef"); - return ErrorUnexpected; - case ZigClangDeclUnresolvedUsingTypename: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C UnresolvedUsingTypename"); - return ErrorUnexpected; - case ZigClangDeclUsing: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Using"); - return ErrorUnexpected; - case ZigClangDeclUsingDirective: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C UsingDirective"); - return ErrorUnexpected; - case ZigClangDeclUsingPack: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C UsingPack"); - return ErrorUnexpected; - case ZigClangDeclUsingShadow: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C UsingShadow"); - return ErrorUnexpected; - case ZigClangDeclConstructorUsingShadow: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ConstructorUsingShadow"); - return ErrorUnexpected; - case ZigClangDeclBinding: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Binding"); - return ErrorUnexpected; - case ZigClangDeclField: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Field"); - return ErrorUnexpected; - case ZigClangDeclObjCAtDefsField: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCAtDefsField"); - return ErrorUnexpected; - case ZigClangDeclObjCIvar: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCIvar"); - return ErrorUnexpected; - case ZigClangDeclFunction: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Function"); - return ErrorUnexpected; - case ZigClangDeclCXXDeductionGuide: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXDeductionGuide"); - return ErrorUnexpected; - case ZigClangDeclCXXMethod: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXMethod"); - return ErrorUnexpected; - case ZigClangDeclCXXConstructor: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXConstructor"); - return ErrorUnexpected; - case ZigClangDeclCXXConversion: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXConversion"); - return ErrorUnexpected; - case ZigClangDeclCXXDestructor: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C CXXDestructor"); - return ErrorUnexpected; - case ZigClangDeclMSProperty: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C MSProperty"); - return ErrorUnexpected; - case ZigClangDeclNonTypeTemplateParm: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C NonTypeTemplateParm"); - return ErrorUnexpected; - case ZigClangDeclDecomposition: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Decomposition"); - return ErrorUnexpected; - case ZigClangDeclImplicitParam: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ImplicitParam"); - return ErrorUnexpected; - case ZigClangDeclOMPCapturedExpr: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPCapturedExpr"); - return ErrorUnexpected; - case ZigClangDeclParmVar: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ParmVar"); - return ErrorUnexpected; - case ZigClangDeclVarTemplateSpecialization: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C VarTemplateSpecialization"); - return ErrorUnexpected; - case ZigClangDeclVarTemplatePartialSpecialization: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C VarTemplatePartialSpecialization"); - return ErrorUnexpected; - case ZigClangDeclEnumConstant: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C EnumConstant"); - return ErrorUnexpected; - case ZigClangDeclIndirectField: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C IndirectField"); - return ErrorUnexpected; - case ZigClangDeclOMPDeclareReduction: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPDeclareReduction"); - return ErrorUnexpected; - case ZigClangDeclUnresolvedUsingValue: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C UnresolvedUsingValue"); - return ErrorUnexpected; - case ZigClangDeclOMPRequires: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPRequires"); - return ErrorUnexpected; - case ZigClangDeclOMPThreadPrivate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPThreadPrivate"); - return ErrorUnexpected; - case ZigClangDeclObjCPropertyImpl: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C ObjCPropertyImpl"); - return ErrorUnexpected; - case ZigClangDeclPragmaComment: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C PragmaComment"); - return ErrorUnexpected; - case ZigClangDeclPragmaDetectMismatch: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C PragmaDetectMismatch"); - return ErrorUnexpected; - case ZigClangDeclStaticAssert: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C StaticAssert"); - return ErrorUnexpected; - case ZigClangDeclTranslationUnit: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C TranslationUnit"); - return ErrorUnexpected; - case ZigClangDeclConcept: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C Concept"); - return ErrorUnexpected; - case ZigClangDeclOMPDeclareMapper: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPDeclareMapper"); - return ErrorUnexpected; - case ZigClangDeclOMPAllocate: - emit_warning(c, ZigClangDeclStmt_getBeginLoc(stmt), "TODO handle C OMPAllocate"); - return ErrorUnexpected; - } - zig_unreachable(); - } - - *out_scope = scope; - return ErrorNone; -} - -static AstNode *to_enum_zero_cmp(Context *c, AstNode *expr, AstNode *enum_type) { - AstNode *tag_type = trans_create_node_builtin_fn_call_str(c, "TagType"); - tag_type->data.fn_call_expr.params.append(enum_type); - - // @TagType(Enum)(0) - AstNode *zero = trans_create_node_unsigned_negative(c, 0, false); - AstNode *casted_zero = trans_create_node_fn_call_1(c, tag_type, zero); - - // @bitCast(Enum, @TagType(Enum)(0)) - AstNode *bitcast = trans_create_node_builtin_fn_call_str(c, "bitCast"); - bitcast->data.fn_call_expr.params.append(enum_type); - bitcast->data.fn_call_expr.params.append(casted_zero); - - return trans_create_node_bin_op(c, expr, BinOpTypeCmpNotEq, bitcast); -} - -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const ZigClangExpr *expr, TransLRValue lrval) { - AstNode *res = trans_expr(c, result_used, scope, expr, lrval); - if (res == nullptr) - return nullptr; - - switch (res->type) { - case NodeTypeBinOpExpr: - switch (res->data.bin_op_expr.bin_op) { - case BinOpTypeBoolOr: - case BinOpTypeBoolAnd: - case BinOpTypeCmpEq: - case BinOpTypeCmpNotEq: - case BinOpTypeCmpLessThan: - case BinOpTypeCmpGreaterThan: - case BinOpTypeCmpLessOrEq: - case BinOpTypeCmpGreaterOrEq: - return res; - default: - break; - } - - case NodeTypePrefixOpExpr: - switch (res->data.prefix_op_expr.prefix_op) { - case PrefixOpBoolNot: - return res; - default: - break; - } - - case NodeTypeBoolLiteral: - return res; - - default: - break; - } - - - const ZigClangType *ty = ZigClangQualType_getTypePtr(get_expr_qual_type_before_implicit_cast(c, expr)); - auto classs = ZigClangType_getTypeClass(ty); - switch (classs) { - case ZigClangType_Builtin: - { - const ZigClangBuiltinType *builtin_ty = reinterpret_cast(ty); - switch (ZigClangBuiltinType_getKind(builtin_ty)) { - case ZigClangBuiltinTypeBool: - case ZigClangBuiltinTypeChar_U: - case ZigClangBuiltinTypeUChar: - case ZigClangBuiltinTypeChar_S: - case ZigClangBuiltinTypeSChar: - case ZigClangBuiltinTypeUShort: - case ZigClangBuiltinTypeUInt: - case ZigClangBuiltinTypeULong: - case ZigClangBuiltinTypeULongLong: - case ZigClangBuiltinTypeShort: - case ZigClangBuiltinTypeInt: - case ZigClangBuiltinTypeLong: - case ZigClangBuiltinTypeLongLong: - case ZigClangBuiltinTypeUInt128: - case ZigClangBuiltinTypeInt128: - case ZigClangBuiltinTypeFloat: - case ZigClangBuiltinTypeDouble: - case ZigClangBuiltinTypeFloat128: - case ZigClangBuiltinTypeLongDouble: - case ZigClangBuiltinTypeWChar_U: - case ZigClangBuiltinTypeChar8: - case ZigClangBuiltinTypeChar16: - case ZigClangBuiltinTypeChar32: - case ZigClangBuiltinTypeWChar_S: - case ZigClangBuiltinTypeFloat16: - return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); - case ZigClangBuiltinTypeNullPtr: - return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, - trans_create_node(c, NodeTypeNullLiteral)); - - case ZigClangBuiltinTypeVoid: - case ZigClangBuiltinTypeHalf: - case ZigClangBuiltinTypeObjCId: - case ZigClangBuiltinTypeObjCClass: - case ZigClangBuiltinTypeObjCSel: - case ZigClangBuiltinTypeOMPArraySection: - case ZigClangBuiltinTypeDependent: - case ZigClangBuiltinTypeOverload: - case ZigClangBuiltinTypeBoundMember: - case ZigClangBuiltinTypePseudoObject: - case ZigClangBuiltinTypeUnknownAny: - case ZigClangBuiltinTypeBuiltinFn: - case ZigClangBuiltinTypeARCUnbridgedCast: - case ZigClangBuiltinTypeOCLImage1dRO: - case ZigClangBuiltinTypeOCLImage1dArrayRO: - case ZigClangBuiltinTypeOCLImage1dBufferRO: - case ZigClangBuiltinTypeOCLImage2dRO: - case ZigClangBuiltinTypeOCLImage2dArrayRO: - case ZigClangBuiltinTypeOCLImage2dDepthRO: - case ZigClangBuiltinTypeOCLImage2dArrayDepthRO: - case ZigClangBuiltinTypeOCLImage2dMSAARO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAARO: - case ZigClangBuiltinTypeOCLImage2dMSAADepthRO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRO: - case ZigClangBuiltinTypeOCLImage3dRO: - case ZigClangBuiltinTypeOCLImage1dWO: - case ZigClangBuiltinTypeOCLImage1dArrayWO: - case ZigClangBuiltinTypeOCLImage1dBufferWO: - case ZigClangBuiltinTypeOCLImage2dWO: - case ZigClangBuiltinTypeOCLImage2dArrayWO: - case ZigClangBuiltinTypeOCLImage2dDepthWO: - case ZigClangBuiltinTypeOCLImage2dArrayDepthWO: - case ZigClangBuiltinTypeOCLImage2dMSAAWO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAAWO: - case ZigClangBuiltinTypeOCLImage2dMSAADepthWO: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthWO: - case ZigClangBuiltinTypeOCLImage3dWO: - case ZigClangBuiltinTypeOCLImage1dRW: - case ZigClangBuiltinTypeOCLImage1dArrayRW: - case ZigClangBuiltinTypeOCLImage1dBufferRW: - case ZigClangBuiltinTypeOCLImage2dRW: - case ZigClangBuiltinTypeOCLImage2dArrayRW: - case ZigClangBuiltinTypeOCLImage2dDepthRW: - case ZigClangBuiltinTypeOCLImage2dArrayDepthRW: - case ZigClangBuiltinTypeOCLImage2dMSAARW: - case ZigClangBuiltinTypeOCLImage2dArrayMSAARW: - case ZigClangBuiltinTypeOCLImage2dMSAADepthRW: - case ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRW: - case ZigClangBuiltinTypeOCLImage3dRW: - case ZigClangBuiltinTypeOCLSampler: - case ZigClangBuiltinTypeOCLEvent: - case ZigClangBuiltinTypeOCLClkEvent: - case ZigClangBuiltinTypeOCLQueue: - case ZigClangBuiltinTypeOCLReserveID: - case ZigClangBuiltinTypeShortAccum: - case ZigClangBuiltinTypeAccum: - case ZigClangBuiltinTypeLongAccum: - case ZigClangBuiltinTypeUShortAccum: - case ZigClangBuiltinTypeUAccum: - case ZigClangBuiltinTypeULongAccum: - case ZigClangBuiltinTypeShortFract: - case ZigClangBuiltinTypeFract: - case ZigClangBuiltinTypeLongFract: - case ZigClangBuiltinTypeUShortFract: - case ZigClangBuiltinTypeUFract: - case ZigClangBuiltinTypeULongFract: - case ZigClangBuiltinTypeSatShortAccum: - case ZigClangBuiltinTypeSatAccum: - case ZigClangBuiltinTypeSatLongAccum: - case ZigClangBuiltinTypeSatUShortAccum: - case ZigClangBuiltinTypeSatUAccum: - case ZigClangBuiltinTypeSatULongAccum: - case ZigClangBuiltinTypeSatShortFract: - case ZigClangBuiltinTypeSatFract: - case ZigClangBuiltinTypeSatLongFract: - case ZigClangBuiltinTypeSatUShortFract: - case ZigClangBuiltinTypeSatUFract: - case ZigClangBuiltinTypeSatULongFract: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCMcePayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImePayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCRefPayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCSicPayload: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCMceResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCRefResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCSicResult: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultSingleRefStreamout: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultDualRefStreamout: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeSingleRefStreamin: - case ZigClangBuiltinTypeOCLIntelSubgroupAVCImeDualRefStreamin: - case ZigClangBuiltinTypeSveInt8: - case ZigClangBuiltinTypeSveInt16: - case ZigClangBuiltinTypeSveInt32: - case ZigClangBuiltinTypeSveInt64: - case ZigClangBuiltinTypeSveUint8: - case ZigClangBuiltinTypeSveUint16: - case ZigClangBuiltinTypeSveUint32: - case ZigClangBuiltinTypeSveUint64: - case ZigClangBuiltinTypeSveFloat16: - case ZigClangBuiltinTypeSveFloat32: - case ZigClangBuiltinTypeSveFloat64: - case ZigClangBuiltinTypeSveBool: - return res; - } - break; - } - case ZigClangType_Pointer: - return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral)); - - case ZigClangType_Typedef: - { - const ZigClangTypedefType *typedef_ty = reinterpret_cast(ty); - const ZigClangTypedefNameDecl *typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - auto existing_entry = c->decl_table.maybe_get((void*)ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)); - if (existing_entry) { - return existing_entry->value; - } - - return res; - } - - case ZigClangType_Enum: - { - const ZigClangEnumType *enum_ty = reinterpret_cast(ty); - AstNode *enum_type = resolve_enum_decl(c, ZigClangEnumType_getDecl(enum_ty)); - return to_enum_zero_cmp(c, res, enum_type); - } - - case ZigClangType_Elaborated: - { - const ZigClangElaboratedType *elaborated_ty = reinterpret_cast(ty); - switch (ZigClangElaboratedType_getKeyword(elaborated_ty)) { - case ZigClangETK_Enum: { - AstNode *enum_type = trans_qual_type(c, ZigClangElaboratedType_getNamedType(elaborated_ty), - ZigClangExpr_getBeginLoc(expr)); - return to_enum_zero_cmp(c, res, enum_type); - } - case ZigClangETK_Struct: - case ZigClangETK_Union: - case ZigClangETK_Interface: - case ZigClangETK_Class: - case ZigClangETK_Typename: - case ZigClangETK_None: - return res; - } - } - - case ZigClangType_FunctionProto: - case ZigClangType_Record: - case ZigClangType_ConstantArray: - case ZigClangType_Paren: - case ZigClangType_Decayed: - case ZigClangType_Attributed: - case ZigClangType_IncompleteArray: - case ZigClangType_BlockPointer: - case ZigClangType_LValueReference: - case ZigClangType_RValueReference: - case ZigClangType_MemberPointer: - case ZigClangType_VariableArray: - case ZigClangType_DependentSizedArray: - case ZigClangType_DependentSizedExtVector: - case ZigClangType_Vector: - case ZigClangType_ExtVector: - case ZigClangType_FunctionNoProto: - case ZigClangType_UnresolvedUsing: - case ZigClangType_Adjusted: - case ZigClangType_TypeOfExpr: - case ZigClangType_TypeOf: - case ZigClangType_Decltype: - case ZigClangType_UnaryTransform: - case ZigClangType_TemplateTypeParm: - case ZigClangType_SubstTemplateTypeParm: - case ZigClangType_SubstTemplateTypeParmPack: - case ZigClangType_TemplateSpecialization: - case ZigClangType_Auto: - case ZigClangType_InjectedClassName: - case ZigClangType_DependentName: - case ZigClangType_DependentTemplateSpecialization: - case ZigClangType_PackExpansion: - case ZigClangType_ObjCObject: - case ZigClangType_ObjCInterface: - case ZigClangType_Complex: - case ZigClangType_ObjCObjectPointer: - case ZigClangType_Atomic: - case ZigClangType_Pipe: - case ZigClangType_ObjCTypeParam: - case ZigClangType_DeducedTemplateSpecialization: - case ZigClangType_DependentAddressSpace: - case ZigClangType_DependentVector: - case ZigClangType_MacroQualified: - return res; - } - zig_unreachable(); -} - -static AstNode *trans_while_loop(Context *c, TransScope *scope, const ZigClangWhileStmt *stmt) { - TransScopeWhile *while_scope = trans_scope_while_create(c, scope); - - while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, - ZigClangWhileStmt_getCond(stmt), TransRValue); - if (while_scope->node->data.while_expr.condition == nullptr) - return nullptr; - - TransScope *body_scope = trans_stmt(c, &while_scope->base, ZigClangWhileStmt_getBody(stmt), - &while_scope->node->data.while_expr.body); - if (body_scope == nullptr) - return nullptr; - - return while_scope->node; -} - -static AstNode *trans_if_statement(Context *c, TransScope *scope, const ZigClangIfStmt *stmt) { - // if (c) t - // if (c) t else e - AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr); - - TransScope *then_scope = trans_stmt(c, scope, ZigClangIfStmt_getThen(stmt), &if_node->data.if_bool_expr.then_block); - if (then_scope == nullptr) - return nullptr; - - if (ZigClangIfStmt_getElse(stmt) != nullptr) { - TransScope *else_scope = trans_stmt(c, scope, ZigClangIfStmt_getElse(stmt), &if_node->data.if_bool_expr.else_node); - if (else_scope == nullptr) - return nullptr; - } - - if_node->data.if_bool_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, ZigClangIfStmt_getCond(stmt), - TransRValue); - if (if_node->data.if_bool_expr.condition == nullptr) - return nullptr; - - return if_node; -} - -static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const ZigClangCallExpr *stmt) { - AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - - AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, ZigClangCallExpr_getCallee(stmt), TransRValue); - if (callee_raw_node == nullptr) - return nullptr; - - bool is_ptr = false; - const ZigClangFunctionProtoType *fn_ty = qual_type_get_fn_proto( - ZigClangExpr_getType(ZigClangCallExpr_getCallee(stmt)), &is_ptr); - AstNode *callee_node = nullptr; - if (is_ptr && fn_ty) { - if (ZigClangExpr_getStmtClass(ZigClangCallExpr_getCallee(stmt)) == ZigClangStmt_ImplicitCastExprClass) { - const ZigClangImplicitCastExpr *implicit_cast = reinterpret_cast( - ZigClangCallExpr_getCallee(stmt)); - if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == ZigClangCK_FunctionToPointerDecay) { - const ZigClangExpr *subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast); - if (ZigClangExpr_getStmtClass(subexpr) == ZigClangStmt_DeclRefExprClass) { - const ZigClangDeclRefExpr *decl_ref = reinterpret_cast(subexpr); - const ZigClangNamedDecl *named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref); - if (ZigClangDecl_getKind((const ZigClangDecl *)named_decl) == ZigClangDeclFunction) { - callee_node = callee_raw_node; - } - } - } - } - if (callee_node == nullptr) { - callee_node = trans_create_node_unwrap_null(c, callee_raw_node); - } - } else { - callee_node = callee_raw_node; - } - - node->data.fn_call_expr.fn_ref_expr = callee_node; - - unsigned num_args = ZigClangCallExpr_getNumArgs(stmt); - const ZigClangExpr * const* args = ZigClangCallExpr_getArgs(stmt); - for (unsigned i = 0; i < num_args; i += 1) { - AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue); - if (arg_node == nullptr) - return nullptr; - - node->data.fn_call_expr.params.append(arg_node); - } - - if (result_used == ResultUsedNo && fn_ty && - !ZigClangType_isVoidType(qual_type_canon(ZigClangFunctionProtoType_getReturnType(fn_ty)))) - { - node = trans_create_node_bin_op(c, trans_create_node_symbol_str(c, "_"), BinOpTypeAssign, node); - } - - return node; -} - -static AstNode *trans_member_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangMemberExpr *stmt) -{ - AstNode *container_node = trans_expr(c, ResultUsedYes, scope, ZigClangMemberExpr_getBase(stmt), TransRValue); - if (container_node == nullptr) - return nullptr; - - if (ZigClangMemberExpr_isArrow(stmt)) { - container_node = trans_create_node_unwrap_null(c, container_node); - } - - const char *name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)ZigClangMemberExpr_getMemberDecl(stmt)); - - AstNode *node = trans_create_node_field_access_str(c, container_node, name); - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_array_subscript_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangArraySubscriptExpr *stmt) -{ - AstNode *container_node = trans_expr(c, ResultUsedYes, scope, ZigClangArraySubscriptExpr_getBase(stmt), - TransRValue); - if (container_node == nullptr) - return nullptr; - - AstNode *idx_node = trans_expr(c, ResultUsedYes, scope, ZigClangArraySubscriptExpr_getIdx(stmt), TransRValue); - if (idx_node == nullptr) - return nullptr; - - - AstNode *node = trans_create_node(c, NodeTypeArrayAccessExpr); - node->data.array_access_expr.array_ref_expr = container_node; - node->data.array_access_expr.subscript = idx_node; - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangCStyleCastExpr *stmt, TransLRValue lrvalue) -{ - AstNode *sub_expr_node = trans_expr(c, ResultUsedYes, scope, ZigClangCStyleCastExpr_getSubExpr(stmt), lrvalue); - if (sub_expr_node == nullptr) - return nullptr; - - AstNode *cast = trans_c_cast(c, ZigClangCStyleCastExpr_getBeginLoc(stmt), ZigClangCStyleCastExpr_getType(stmt), - ZigClangExpr_getType(ZigClangCStyleCastExpr_getSubExpr(stmt)), sub_expr_node); - if (cast == nullptr) - return nullptr; - - return maybe_suppress_result(c, result_used, cast); -} - -static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, ResultUsed result_used, - TransScope *scope, const ZigClangUnaryExprOrTypeTraitExpr *stmt) -{ - AstNode *type_node = trans_qual_type(c, ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt), - ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt)); - if (type_node == nullptr) - return nullptr; - - AstNode *node = trans_create_node_builtin_fn_call_str(c, "sizeOf"); - node->data.fn_call_expr.params.append(type_node); - return maybe_suppress_result(c, result_used, node); -} - -static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const ZigClangDoStmt *stmt) { - TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope); - - while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); - - AstNode *body_node; - TransScope *child_scope; - if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == ZigClangStmt_CompoundStmtClass) { - // there's already a block in C, so we'll append our condition to it. - // c: do { - // c: a; - // c: b; - // c: } while(c); - // zig: while (true) { - // zig: a; - // zig: b; - // zig: if (!cond) break; - // zig: } - - // We call the low level function so that we can set child_scope to the scope of the generated block. - if (trans_stmt_extra(c, &while_scope->base, ZigClangDoStmt_getBody(stmt), ResultUsedNo, TransRValue, - &body_node, nullptr, &child_scope)) - { - return nullptr; - } - assert(body_node->type == NodeTypeBlock); - } else { - // the C statement is without a block, so we need to create a block to contain it. - // c: do - // c: a; - // c: while(c); - // zig: while (true) { - // zig: a; - // zig: if (!cond) break; - // zig: } - TransScopeBlock *child_block_scope = trans_scope_block_create(c, &while_scope->base); - body_node = child_block_scope->node; - AstNode *child_statement; - child_scope = trans_stmt(c, &child_block_scope->base, ZigClangDoStmt_getBody(stmt), &child_statement); - if (child_scope == nullptr) return nullptr; - if (child_statement != nullptr) { - body_node->data.block.statements.append(child_statement); - } - } - - // if (!cond) break; - AstNode *condition_node = trans_expr(c, ResultUsedYes, child_scope, ZigClangDoStmt_getCond(stmt), TransRValue); - if (condition_node == nullptr) return nullptr; - AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr); - terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node); - terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak); - - assert(terminator_node != nullptr); - body_node->data.block.statements.append(terminator_node); - - while_scope->node->data.while_expr.body = body_node; - - return while_scope->node; -} - -static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ZigClangForStmt *stmt) { - AstNode *loop_block_node; - TransScopeWhile *while_scope; - TransScope *cond_scope; - const ZigClangStmt *init_stmt = ZigClangForStmt_getInit(stmt); - if (init_stmt == nullptr) { - while_scope = trans_scope_while_create(c, parent_scope); - loop_block_node = while_scope->node; - cond_scope = parent_scope; - } else { - TransScopeBlock *child_scope = trans_scope_block_create(c, parent_scope); - loop_block_node = child_scope->node; - - AstNode *vars_node; - cond_scope = trans_stmt(c, &child_scope->base, init_stmt, &vars_node); - if (cond_scope == nullptr) - return nullptr; - if (vars_node != nullptr) - child_scope->node->data.block.statements.append(vars_node); - - while_scope = trans_scope_while_create(c, cond_scope); - - child_scope->node->data.block.statements.append(while_scope->node); - } - - const ZigClangExpr *cond_expr = ZigClangForStmt_getCond(stmt); - if (cond_expr == nullptr) { - while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); - } else { - while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, cond_scope, - cond_expr, TransRValue); - - if (while_scope->node->data.while_expr.condition == nullptr) - return nullptr; - } - - const ZigClangExpr *inc_expr = ZigClangForStmt_getInc(stmt); - if (inc_expr != nullptr) { - AstNode *inc_node = trans_expr(c, ResultUsedNo, cond_scope, inc_expr, TransRValue); - if (inc_node == nullptr) - return nullptr; - while_scope->node->data.while_expr.continue_expr = inc_node; - } - - AstNode *body_statement; - TransScope *body_scope = trans_stmt(c, &while_scope->base, ZigClangForStmt_getBody(stmt), &body_statement); - if (body_scope == nullptr) - return nullptr; - - if (body_statement == nullptr) { - while_scope->node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); - } else { - while_scope->node->data.while_expr.body = body_statement; - } - - return loop_block_node; -} - -static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const ZigClangSwitchStmt *stmt) { - TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); - - TransScopeSwitch *switch_scope; - - const ZigClangDeclStmt *var_decl_stmt = ZigClangSwitchStmt_getConditionVariableDeclStmt(stmt); - if (var_decl_stmt == nullptr) { - switch_scope = trans_scope_switch_create(c, &block_scope->base); - } else { - AstNode *vars_node; - TransScope *var_scope = trans_stmt(c, &block_scope->base, (const ZigClangStmt *)var_decl_stmt, &vars_node); - if (var_scope == nullptr) - return nullptr; - if (vars_node != nullptr) - block_scope->node->data.block.statements.append(vars_node); - switch_scope = trans_scope_switch_create(c, var_scope); - } - block_scope->node->data.block.statements.append(switch_scope->switch_node); - - // TODO avoid name collisions - Buf *end_label_name = buf_create_from_str("__switch"); - switch_scope->end_label_name = end_label_name; - block_scope->node->data.block.name = end_label_name; - - const ZigClangExpr *cond_expr = ZigClangSwitchStmt_getCond(stmt); - assert(cond_expr != nullptr); - - AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue); - if (expr_node == nullptr) - return nullptr; - switch_scope->switch_node->data.switch_expr.expr = expr_node; - - AstNode *body_node; - const ZigClangStmt *body_stmt = ZigClangSwitchStmt_getBody(stmt); - if (ZigClangStmt_getStmtClass(body_stmt) == ZigClangStmt_CompoundStmtClass) { - if (trans_compound_stmt_inline(c, &switch_scope->base, (const ZigClangCompoundStmt *)body_stmt, - block_scope->node, nullptr)) - { - return nullptr; - } - } else { - TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node); - if (body_scope == nullptr) - return nullptr; - if (body_node != nullptr) - block_scope->node->data.block.statements.append(body_node); - } - - if (!switch_scope->found_default && !ZigClangSwitchStmt_isAllEnumCasesCovered(stmt)) { - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - prong_node->data.switch_prong.expr = trans_create_node_break(c, end_label_name, nullptr); - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - } - - return block_scope->node; -} - -static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) { - while (scope != nullptr) { - if (scope->id == TransScopeIdSwitch) { - return (TransScopeSwitch *)scope; - } - scope = scope->parent; - } - return nullptr; -} - -static int trans_switch_case(Context *c, TransScope *parent_scope, const ZigClangCaseStmt *stmt, AstNode **out_node, - TransScope **out_scope) { - *out_node = nullptr; - - if (ZigClangCaseStmt_getRHS(stmt) != nullptr) { - emit_warning(c, ZigClangCaseStmt_getBeginLoc(stmt), "TODO support GNU switch case a ... b extension"); - return ErrorUnexpected; - } - - TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); - assert(switch_scope != nullptr); - - Buf *label_name = buf_sprintf("__case_%" PRIu32, switch_scope->case_index); - switch_scope->case_index += 1; - - { - // Add the prong - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, ZigClangCaseStmt_getLHS(stmt), - TransRValue); - if (item_node == nullptr) - return ErrorUnexpected; - prong_node->data.switch_prong.items.append(item_node); - prong_node->data.switch_prong.expr = trans_create_node_break(c, label_name, nullptr); - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - } - - TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - - AstNode *case_block = trans_create_node(c, NodeTypeBlock); - case_block->data.block.name = label_name; - case_block->data.block.statements = scope_block->node->data.block.statements; - scope_block->node->data.block.statements = {0}; - scope_block->node->data.block.statements.append(case_block); - - AstNode *sub_stmt_node; - TransScope *new_scope = trans_stmt(c, parent_scope, ZigClangCaseStmt_getSubStmt(stmt), &sub_stmt_node); - if (new_scope == nullptr) - return ErrorUnexpected; - if (sub_stmt_node != nullptr) - scope_block->node->data.block.statements.append(sub_stmt_node); - - *out_scope = new_scope; - return ErrorNone; -} - -static int trans_switch_default(Context *c, TransScope *parent_scope, const ZigClangDefaultStmt *stmt, - AstNode **out_node, TransScope **out_scope) -{ - *out_node = nullptr; - - TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); - assert(switch_scope != nullptr); - - Buf *label_name = buf_sprintf("__default"); - - { - // Add the prong - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - prong_node->data.switch_prong.expr = trans_create_node_break(c, label_name, nullptr); - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - switch_scope->found_default = true; - } - - TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - - AstNode *case_block = trans_create_node(c, NodeTypeBlock); - case_block->data.block.name = label_name; - case_block->data.block.statements = scope_block->node->data.block.statements; - scope_block->node->data.block.statements = {0}; - scope_block->node->data.block.statements.append(case_block); - - AstNode *sub_stmt_node; - TransScope *new_scope = trans_stmt(c, parent_scope, ZigClangDefaultStmt_getSubStmt(stmt), &sub_stmt_node); - if (new_scope == nullptr) - return ErrorUnexpected; - if (sub_stmt_node != nullptr) - scope_block->node->data.block.statements.append(sub_stmt_node); - - *out_scope = new_scope; - return ErrorNone; -} - -static AstNode *trans_string_literal(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangStringLiteral *stmt) -{ - switch (ZigClangStringLiteral_getKind(stmt)) { - case ZigClangStringLiteral_StringKind_Ascii: - case ZigClangStringLiteral_StringKind_UTF8: { - size_t str_len; - const char *str_ptr = ZigClangStringLiteral_getString_bytes_begin_size(stmt, &str_len); - AstNode *node = trans_create_node_str_lit_c(c, buf_create_from_mem(str_ptr, str_len)); - return maybe_suppress_result(c, result_used, node); - } - case ZigClangStringLiteral_StringKind_UTF16: - emit_warning(c, ZigClangStmt_getBeginLoc((const ZigClangStmt *)stmt), "TODO support UTF16 string literals"); - return nullptr; - case ZigClangStringLiteral_StringKind_UTF32: - emit_warning(c, ZigClangStmt_getBeginLoc((const ZigClangStmt *)stmt), "TODO support UTF32 string literals"); - return nullptr; - case ZigClangStringLiteral_StringKind_Wide: - emit_warning(c, ZigClangStmt_getBeginLoc((const ZigClangStmt *)stmt), "TODO support wide string literals"); - return nullptr; - } - zig_unreachable(); -} - -static AstNode *trans_break_stmt(Context *c, TransScope *scope, const ZigClangBreakStmt *stmt) { - TransScope *cur_scope = scope; - while (cur_scope != nullptr) { - if (cur_scope->id == TransScopeIdWhile) { - return trans_create_node(c, NodeTypeBreak); - } else if (cur_scope->id == TransScopeIdSwitch) { - TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope; - return trans_create_node_break(c, switch_scope->end_label_name, nullptr); - } - cur_scope = cur_scope->parent; - } - zig_unreachable(); -} - -static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ZigClangContinueStmt *stmt) { - return trans_create_node(c, NodeTypeContinue); -} - -static AstNode *trans_predefined_expr(Context *c, ResultUsed result_used, TransScope *scope, - const ZigClangPredefinedExpr *expr) -{ - return trans_string_literal(c, result_used, scope, ZigClangPredefinedExpr_getFunctionName(expr)); -} - -static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_scope, AstNode *result_node) { - if (result_node == nullptr) - return ErrorUnexpected; - *out_node = result_node; - if (out_scope != nullptr) - *out_scope = in_scope; - return ErrorNone; -} - -static int trans_stmt_extra(Context *c, TransScope *scope, const ZigClangStmt *stmt, - ResultUsed result_used, TransLRValue lrvalue, - AstNode **out_node, TransScope **out_child_scope, - TransScope **out_node_scope) -{ - ZigClangStmtClass sc = ZigClangStmt_getStmtClass(stmt); - switch (sc) { - case ZigClangStmt_ReturnStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_return_stmt(c, scope, (const ZigClangReturnStmt *)stmt)); - case ZigClangStmt_CompoundStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_stmt(c, scope, (const ZigClangCompoundStmt *)stmt, out_node_scope)); - case ZigClangStmt_IntegerLiteralClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_integer_literal(c, result_used, (const ZigClangIntegerLiteral *)stmt)); - case ZigClangStmt_ConditionalOperatorClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_conditional_operator(c, result_used, scope, (const ZigClangConditionalOperator *)stmt)); - case ZigClangStmt_BinaryOperatorClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_binary_operator(c, result_used, scope, (const ZigClangBinaryOperator *)stmt)); - case ZigClangStmt_CompoundAssignOperatorClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_assign_operator(c, result_used, scope, (const ZigClangCompoundAssignOperator *)stmt)); - case ZigClangStmt_ImplicitCastExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_implicit_cast_expr(c, result_used, scope, (const ZigClangImplicitCastExpr *)stmt)); - case ZigClangStmt_DeclRefExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_decl_ref_expr(c, scope, (const ZigClangDeclRefExpr *)stmt, lrvalue)); - case ZigClangStmt_UnaryOperatorClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_operator(c, result_used, scope, (const ZigClangUnaryOperator *)stmt)); - case ZigClangStmt_DeclStmtClass: - return trans_local_declaration(c, scope, (const ZigClangDeclStmt *)stmt, out_node, out_child_scope); - case ZigClangStmt_DoStmtClass: - case ZigClangStmt_WhileStmtClass: { - AstNode *while_node = sc == ZigClangStmt_DoStmtClass - ? trans_do_loop(c, scope, (const ZigClangDoStmt *)stmt) - : trans_while_loop(c, scope, (const ZigClangWhileStmt *)stmt); - - if (while_node == nullptr) - return ErrorUnexpected; - - assert(while_node->type == NodeTypeWhileExpr); - if (while_node->data.while_expr.body == nullptr) - while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); - - return wrap_stmt(out_node, out_child_scope, scope, while_node); - } - case ZigClangStmt_IfStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_if_statement(c, scope, (const ZigClangIfStmt *)stmt)); - case ZigClangStmt_CallExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_call_expr(c, result_used, scope, (const ZigClangCallExpr *)stmt)); - case ZigClangStmt_NullStmtClass: - *out_node = trans_create_node(c, NodeTypeBlock); - *out_child_scope = scope; - return ErrorNone; - case ZigClangStmt_MemberExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_member_expr(c, result_used, scope, (const ZigClangMemberExpr *)stmt)); - case ZigClangStmt_ArraySubscriptExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_array_subscript_expr(c, result_used, scope, (const ZigClangArraySubscriptExpr *)stmt)); - case ZigClangStmt_CStyleCastExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_c_style_cast_expr(c, result_used, scope, (const ZigClangCStyleCastExpr *)stmt, lrvalue)); - case ZigClangStmt_UnaryExprOrTypeTraitExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_expr_or_type_trait_expr(c, result_used, scope, (const ZigClangUnaryExprOrTypeTraitExpr *)stmt)); - case ZigClangStmt_ForStmtClass: { - AstNode *node = trans_for_loop(c, scope, (const ZigClangForStmt *)stmt); - return wrap_stmt(out_node, out_child_scope, scope, node); - } - case ZigClangStmt_StringLiteralClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_string_literal(c, result_used, scope, (const ZigClangStringLiteral *)stmt)); - case ZigClangStmt_BreakStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_break_stmt(c, scope, (const ZigClangBreakStmt *)stmt)); - case ZigClangStmt_ContinueStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_continue_stmt(c, scope, (const ZigClangContinueStmt *)stmt)); - case ZigClangStmt_ParenExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_expr(c, result_used, scope, - ZigClangParenExpr_getSubExpr((const ZigClangParenExpr *)stmt), lrvalue)); - case ZigClangStmt_SwitchStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_switch_stmt(c, scope, (const ZigClangSwitchStmt *)stmt)); - case ZigClangStmt_CaseStmtClass: - return trans_switch_case(c, scope, (const ZigClangCaseStmt *)stmt, out_node, out_child_scope); - case ZigClangStmt_DefaultStmtClass: - return trans_switch_default(c, scope, (const ZigClangDefaultStmt *)stmt, out_node, out_child_scope); - case ZigClangStmt_ConstantExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_constant_expr(c, result_used, (const ZigClangConstantExpr *)stmt)); - case ZigClangStmt_PredefinedExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_predefined_expr(c, result_used, scope, (const ZigClangPredefinedExpr *)stmt)); - case ZigClangStmt_StmtExprClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_stmt_expr(c, result_used, scope, (const ZigClangStmtExpr *)stmt, out_node_scope)); - case ZigClangStmt_NoStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C NoStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_GCCAsmStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C GCCAsmStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_MSAsmStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C MSAsmStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_AttributedStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C AttributedStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXCatchStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXCatchStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXForRangeStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXForRangeStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXTryStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXTryStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CapturedStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CapturedStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CoreturnStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CoreturnStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_CoroutineBodyStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CoroutineBodyStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_BinaryConditionalOperatorClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C BinaryConditionalOperatorClass"); - return ErrorUnexpected; - case ZigClangStmt_AddrLabelExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C AddrLabelExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ArrayInitIndexExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ArrayInitIndexExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ArrayInitLoopExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ArrayInitLoopExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ArrayTypeTraitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ArrayTypeTraitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_AsTypeExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C AsTypeExprClass"); - return ErrorUnexpected; - case ZigClangStmt_AtomicExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C AtomicExprClass"); - return ErrorUnexpected; - case ZigClangStmt_BlockExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C BlockExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXBindTemporaryExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXBindTemporaryExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXBoolLiteralExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXBoolLiteralExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXConstructExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXConstructExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXTemporaryObjectExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXTemporaryObjectExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXDefaultArgExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXDefaultArgExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXDefaultInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXDefaultInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXDeleteExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXDeleteExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXDependentScopeMemberExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXDependentScopeMemberExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXFoldExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXFoldExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXInheritedCtorInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXInheritedCtorInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXNewExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXNewExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXNoexceptExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXNoexceptExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXNullPtrLiteralExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXNullPtrLiteralExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXPseudoDestructorExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXPseudoDestructorExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXScalarValueInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXScalarValueInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXStdInitializerListExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXStdInitializerListExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXThisExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXThisExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXThrowExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXThrowExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXTypeidExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXTypeidExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXUnresolvedConstructExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXUnresolvedConstructExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXUuidofExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXUuidofExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CUDAKernelCallExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CUDAKernelCallExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXMemberCallExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXMemberCallExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXOperatorCallExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXOperatorCallExprClass"); - return ErrorUnexpected; - case ZigClangStmt_UserDefinedLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C UserDefinedLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXFunctionalCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXFunctionalCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXConstCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXConstCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXDynamicCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXDynamicCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXReinterpretCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXReinterpretCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CXXStaticCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CXXStaticCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCBridgedCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCBridgedCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CharacterLiteralClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_character_literal(c, result_used, (const ZigClangCharacterLiteral *)stmt)); - return ErrorUnexpected; - case ZigClangStmt_ChooseExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ChooseExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CompoundLiteralExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CompoundLiteralExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ConvertVectorExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ConvertVectorExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CoawaitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CoawaitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_CoyieldExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C CoyieldExprClass"); - return ErrorUnexpected; - case ZigClangStmt_DependentCoawaitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C DependentCoawaitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_DependentScopeDeclRefExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C DependentScopeDeclRefExprClass"); - return ErrorUnexpected; - case ZigClangStmt_DesignatedInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C DesignatedInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_DesignatedInitUpdateExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C DesignatedInitUpdateExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ExpressionTraitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ExpressionTraitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ExtVectorElementExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ExtVectorElementExprClass"); - return ErrorUnexpected; - case ZigClangStmt_FixedPointLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C FixedPointLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_FloatingLiteralClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_floating_literal(c, result_used, (const ZigClangFloatingLiteral *)stmt)); - case ZigClangStmt_ExprWithCleanupsClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ExprWithCleanupsClass"); - return ErrorUnexpected; - case ZigClangStmt_FunctionParmPackExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C FunctionParmPackExprClass"); - return ErrorUnexpected; - case ZigClangStmt_GNUNullExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C GNUNullExprClass"); - return ErrorUnexpected; - case ZigClangStmt_GenericSelectionExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C GenericSelectionExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ImaginaryLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ImaginaryLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_ImplicitValueInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ImplicitValueInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_InitListExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C InitListExprClass"); - return ErrorUnexpected; - case ZigClangStmt_LambdaExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C LambdaExprClass"); - return ErrorUnexpected; - case ZigClangStmt_MSPropertyRefExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C MSPropertyRefExprClass"); - return ErrorUnexpected; - case ZigClangStmt_MSPropertySubscriptExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C MSPropertySubscriptExprClass"); - return ErrorUnexpected; - case ZigClangStmt_MaterializeTemporaryExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C MaterializeTemporaryExprClass"); - return ErrorUnexpected; - case ZigClangStmt_NoInitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C NoInitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPArraySectionExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPArraySectionExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCArrayLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCArrayLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAvailabilityCheckExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAvailabilityCheckExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCBoolLiteralExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCBoolLiteralExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCBoxedExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCBoxedExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCDictionaryLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCDictionaryLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCEncodeExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCEncodeExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCIndirectCopyRestoreExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCIndirectCopyRestoreExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCIsaExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCIsaExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCIvarRefExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCIvarRefExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCMessageExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCMessageExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCPropertyRefExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCPropertyRefExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCProtocolExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCProtocolExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCSelectorExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCSelectorExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCStringLiteralClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCStringLiteralClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCSubscriptRefExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCSubscriptRefExprClass"); - return ErrorUnexpected; - case ZigClangStmt_OffsetOfExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OffsetOfExprClass"); - return ErrorUnexpected; - case ZigClangStmt_OpaqueValueExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OpaqueValueExprClass"); - return ErrorUnexpected; - case ZigClangStmt_UnresolvedLookupExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C UnresolvedLookupExprClass"); - return ErrorUnexpected; - case ZigClangStmt_UnresolvedMemberExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C UnresolvedMemberExprClass"); - return ErrorUnexpected; - case ZigClangStmt_PackExpansionExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C PackExpansionExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ParenListExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ParenListExprClass"); - return ErrorUnexpected; - case ZigClangStmt_PseudoObjectExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C PseudoObjectExprClass"); - return ErrorUnexpected; - case ZigClangStmt_ShuffleVectorExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ShuffleVectorExprClass"); - return ErrorUnexpected; - case ZigClangStmt_SizeOfPackExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SizeOfPackExprClass"); - return ErrorUnexpected; - case ZigClangStmt_SubstNonTypeTemplateParmExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SubstNonTypeTemplateParmExprClass"); - return ErrorUnexpected; - case ZigClangStmt_SubstNonTypeTemplateParmPackExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SubstNonTypeTemplateParmPackExprClass"); - return ErrorUnexpected; - case ZigClangStmt_TypeTraitExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C TypeTraitExprClass"); - return ErrorUnexpected; - case ZigClangStmt_TypoExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C TypoExprClass"); - return ErrorUnexpected; - case ZigClangStmt_VAArgExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C VAArgExprClass"); - return ErrorUnexpected; - case ZigClangStmt_GotoStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C GotoStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_IndirectGotoStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C IndirectGotoStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_LabelStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C LabelStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_MSDependentExistsStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C MSDependentExistsStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPAtomicDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPAtomicDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPBarrierDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPBarrierDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPCancelDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPCancelDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPCancellationPointDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPCancellationPointDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPCriticalDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPCriticalDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPFlushDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPFlushDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPDistributeDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPDistributeDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPDistributeParallelForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPDistributeParallelForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPDistributeParallelForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPDistributeParallelForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPDistributeSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPDistributeSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPParallelForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPParallelForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPParallelForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPParallelForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetParallelForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetParallelForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetTeamsDistributeDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetTeamsDistributeDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetTeamsDistributeParallelForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetTeamsDistributeParallelForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetTeamsDistributeSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskLoopDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskLoopDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskLoopSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskLoopSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTeamsDistributeDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTeamsDistributeDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTeamsDistributeParallelForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTeamsDistributeParallelForSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTeamsDistributeSimdDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTeamsDistributeSimdDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPMasterDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPMasterDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPOrderedDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPOrderedDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPParallelDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPParallelDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPParallelSectionsDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPParallelSectionsDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPSectionDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPSectionDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPSectionsDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPSectionsDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPSingleDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPSingleDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetDataDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetDataDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetEnterDataDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetEnterDataDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetExitDataDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetExitDataDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetParallelDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetParallelDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetParallelForDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetParallelForDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetTeamsDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetTeamsDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTargetUpdateDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTargetUpdateDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskgroupDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskgroupDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskwaitDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskwaitDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTaskyieldDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTaskyieldDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPTeamsDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPTeamsDirectiveClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAtCatchStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAtCatchStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAtFinallyStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAtFinallyStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAtSynchronizedStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAtSynchronizedStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAtThrowStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAtThrowStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAtTryStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAtTryStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCAutoreleasePoolStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCAutoreleasePoolStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_ObjCForCollectionStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C ObjCForCollectionStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_SEHExceptStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SEHExceptStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_SEHFinallyStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SEHFinallyStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_SEHLeaveStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SEHLeaveStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_SEHTryStmtClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SEHTryStmtClass"); - return ErrorUnexpected; - case ZigClangStmt_BuiltinBitCastExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C BuiltinBitCastExprClass"); - return ErrorUnexpected; - case ZigClangStmt_SourceLocExprClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C SourceLocExprClass"); - return ErrorUnexpected; - case ZigClangStmt_OMPMasterTaskLoopDirectiveClass: - emit_warning(c, ZigClangStmt_getBeginLoc(stmt), "TODO handle C OMPMasterTaskLoopDirectiveClass"); - return ErrorUnexpected; - } - zig_unreachable(); -} - -// Returns null if there was an error -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const ZigClangExpr *expr, - TransLRValue lrval) -{ - AstNode *result_node; - TransScope *result_scope; - if (trans_stmt_extra(c, scope, (const ZigClangStmt *)expr, result_used, lrval, &result_node, &result_scope, nullptr)) { - return nullptr; - } - return result_node; -} - -// Statements have no result and no concept of L or R value. -// Returns child scope, or null if there was an error -static TransScope *trans_stmt(Context *c, TransScope *scope, const ZigClangStmt *stmt, AstNode **out_node) { - TransScope *child_scope; - if (trans_stmt_extra(c, scope, stmt, ResultUsedNo, TransRValue, out_node, &child_scope, nullptr)) { - return nullptr; - } - return child_scope; -} - -static void visit_fn_decl(Context *c, const ZigClangFunctionDecl *fn_decl) { - Buf *fn_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)fn_decl)); - - if (get_global(c, fn_name)) { - // we already saw this function - return; - } - - AstNode *proto_node = trans_qual_type(c, ZigClangFunctionDecl_getType(fn_decl), - ZigClangFunctionDecl_getLocation(fn_decl)); - if (proto_node == nullptr) { - emit_warning(c, ZigClangFunctionDecl_getLocation(fn_decl), - "unable to resolve prototype of function '%s'", buf_ptr(fn_name)); - return; - } - - proto_node->data.fn_proto.name = fn_name; - proto_node->data.fn_proto.is_extern = !ZigClangFunctionDecl_hasBody(fn_decl); - - ZigClangStorageClass sc = ZigClangFunctionDecl_getStorageClass(fn_decl); - if (sc == ZigClangStorageClass_None) { - proto_node->data.fn_proto.visib_mod = c->visib_mod; - proto_node->data.fn_proto.is_export = ZigClangFunctionDecl_hasBody(fn_decl) ? c->want_export : false; - } else if (sc == ZigClangStorageClass_Extern || sc == ZigClangStorageClass_Static) { - proto_node->data.fn_proto.visib_mod = c->visib_mod; - } else if (sc == ZigClangStorageClass_PrivateExtern) { - emit_warning(c, ZigClangFunctionDecl_getLocation(fn_decl), "unsupported storage class: private extern"); - return; - } else { - emit_warning(c, ZigClangFunctionDecl_getLocation(fn_decl), "unsupported storage class: unknown"); - return; - } - - TransScope *scope = &c->global_scope->base; - - for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { - AstNode *param_node = proto_node->data.fn_proto.params.at(i); - const ZigClangParmVarDecl *param = ZigClangFunctionDecl_getParamDecl(fn_decl, i); - const char *name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)param); - - Buf *proto_param_name; - if (strlen(name) != 0) { - proto_param_name = buf_create_from_str(name); - } else { - proto_param_name = param_node->data.param_decl.name; - if (proto_param_name == nullptr) { - proto_param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); - } - } - - TransScopeVar *scope_var = trans_scope_var_create(c, scope, proto_param_name); - scope = &scope_var->base; - - param_node->data.param_decl.name = scope_var->zig_name; - } - - if (!ZigClangFunctionDecl_hasBody(fn_decl)) { - // just a prototype - add_top_level_decl(c, proto_node->data.fn_proto.name, proto_node); - return; - } - - // actual function definition with body - c->ptr_params.clear(); - const ZigClangStmt *body = ZigClangFunctionDecl_getBody(fn_decl); - AstNode *actual_body_node; - TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node); - if (result_scope == nullptr) { - emit_warning(c, ZigClangFunctionDecl_getLocation(fn_decl), "unable to translate function"); - return; - } - assert(actual_body_node != nullptr); - assert(actual_body_node->type == NodeTypeBlock); - - // it worked - - AstNode *body_node_with_param_inits = trans_create_node(c, NodeTypeBlock); - - for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { - AstNode *param_node = proto_node->data.fn_proto.params.at(i); - Buf *good_name = param_node->data.param_decl.name; - - if (c->ptr_params.maybe_get(good_name) != nullptr) { - // TODO: avoid name collisions - Buf *mangled_name = buf_sprintf("_arg_%s", buf_ptr(good_name)); - param_node->data.param_decl.name = mangled_name; - - // var c_name = _mangled_name; - AstNode *parameter_init = trans_create_node_var_decl_local(c, false, good_name, nullptr, trans_create_node_symbol(c, mangled_name)); - - body_node_with_param_inits->data.block.statements.append(parameter_init); - } - } - - for (size_t i = 0; i < actual_body_node->data.block.statements.length; i += 1) { - body_node_with_param_inits->data.block.statements.append(actual_body_node->data.block.statements.at(i)); - } - - AstNode *fn_def_node = trans_create_node(c, NodeTypeFnDef); - fn_def_node->data.fn_def.fn_proto = proto_node; - fn_def_node->data.fn_def.body = body_node_with_param_inits; - - proto_node->data.fn_proto.fn_def_node = fn_def_node; - add_top_level_decl(c, fn_def_node->data.fn_def.fn_proto->data.fn_proto.name, fn_def_node); -} - -static AstNode *resolve_typdef_as_builtin(Context *c, const ZigClangTypedefNameDecl *typedef_decl, const char *primitive_name) { - AstNode *node = trans_create_node_symbol_str(c, primitive_name); - c->decl_table.put(typedef_decl, node); - return node; -} - -static AstNode *resolve_typedef_decl(Context *c, const ZigClangTypedefNameDecl *typedef_decl) { - auto existing_entry = c->decl_table.maybe_get((void*)ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)); - if (existing_entry) { - return existing_entry->value; - } - - ZigClangQualType child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); - Buf *type_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)typedef_decl)); - - if (buf_eql_str(type_name, "uint8_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "u8"); - } else if (buf_eql_str(type_name, "int8_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "i8"); - } else if (buf_eql_str(type_name, "uint16_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "u16"); - } else if (buf_eql_str(type_name, "int16_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "i16"); - } else if (buf_eql_str(type_name, "uint32_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "u32"); - } else if (buf_eql_str(type_name, "int32_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "i32"); - } else if (buf_eql_str(type_name, "uint64_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "u64"); - } else if (buf_eql_str(type_name, "int64_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "i64"); - } else if (buf_eql_str(type_name, "intptr_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "isize"); - } else if (buf_eql_str(type_name, "uintptr_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "usize"); - } else if (buf_eql_str(type_name, "ssize_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "isize"); - } else if (buf_eql_str(type_name, "size_t")) { - return resolve_typdef_as_builtin(c, typedef_decl, "usize"); - } - - // if the underlying type is anonymous, we can special case it to just - // use the name of this typedef - // TODO - - // trans_qual_type here might cause us to look at this typedef again so we put the item in the map first - AstNode *symbol_node = trans_create_node_symbol(c, type_name); - c->decl_table.put(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl), symbol_node); - - AstNode *type_node = trans_qual_type(c, child_qt, ZigClangTypedefNameDecl_getLocation(typedef_decl)); - if (type_node == nullptr) { - emit_warning(c, ZigClangTypedefNameDecl_getLocation(typedef_decl), - "typedef %s - unresolved child type", buf_ptr(type_name)); - c->decl_table.put(typedef_decl, nullptr); - // TODO add global var with type_name equal to @compileError("unable to resolve C type") - return nullptr; - } - add_global_var(c, type_name, type_node); - - return symbol_node; -} - -struct AstNode *demote_enum_to_opaque(Context *c, const ZigClangEnumDecl *enum_decl, Buf *full_type_name, - Buf *bare_name) -{ - AstNode *opaque_node = trans_create_node_opaque(c); - if (full_type_name == nullptr) { - c->decl_table.put(ZigClangEnumDecl_getCanonicalDecl(enum_decl), opaque_node); - return opaque_node; - } - AstNode *symbol_node = trans_create_node_symbol(c, full_type_name); - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, opaque_node); - c->decl_table.put(ZigClangEnumDecl_getCanonicalDecl(enum_decl), symbol_node); - return symbol_node; -} - -static AstNode *resolve_enum_decl(Context *c, const ZigClangEnumDecl *enum_decl) { - auto existing_entry = c->decl_table.maybe_get(ZigClangEnumDecl_getCanonicalDecl(enum_decl)); - if (existing_entry) { - return existing_entry->value; - } - - const char *raw_name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)enum_decl); - bool is_anonymous = (raw_name[0] == 0); - Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name); - Buf *full_type_name = is_anonymous ? nullptr : buf_sprintf("enum_%s", buf_ptr(bare_name)); - - const ZigClangEnumDecl *enum_def = ZigClangEnumDecl_getDefinition(enum_decl); - if (!enum_def) { - return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); - } - - - bool pure_enum = true; - uint32_t field_count = 0; - for (auto it = reinterpret_cast(enum_def)->enumerator_begin(), - it_end = reinterpret_cast(enum_def)->enumerator_end(); - it != it_end; ++it, field_count += 1) - { - const clang::EnumConstantDecl *enum_const = *it; - if (enum_const->getInitExpr()) { - pure_enum = false; - } - } - AstNode *tag_int_type = trans_qual_type(c, ZigClangEnumDecl_getIntegerType(enum_decl), - ZigClangEnumDecl_getLocation(enum_decl)); - assert(tag_int_type); - - AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); - enum_node->data.container_decl.kind = ContainerKindEnum; - enum_node->data.container_decl.layout = ContainerLayoutExtern; - // TODO only emit this tag type if the enum tag type is not the default. - // I don't know what the default is, need to figure out how clang is deciding. - // it appears to at least be different across gcc/msvc - if (!c_is_builtin_type(c, ZigClangEnumDecl_getIntegerType(enum_decl), ZigClangBuiltinTypeUInt) && - !c_is_builtin_type(c, ZigClangEnumDecl_getIntegerType(enum_decl), ZigClangBuiltinTypeInt)) - { - enum_node->data.container_decl.init_arg_expr = tag_int_type; - } - enum_node->data.container_decl.fields.resize(field_count); - uint32_t i = 0; - for (auto it = reinterpret_cast(enum_def)->enumerator_begin(), - it_end = reinterpret_cast(enum_def)->enumerator_end(); - it != it_end; ++it, i += 1) - { - const clang::EnumConstantDecl *enum_const = *it; - - Buf *enum_val_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)enum_const)); - Buf *field_name; - if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { - field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); - } else { - field_name = enum_val_name; - } - - AstNode *int_node = pure_enum && !is_anonymous ? - nullptr : trans_create_node_apint(c, bitcast(&enum_const->getInitVal())); - AstNode *field_node = trans_create_node(c, NodeTypeStructField); - field_node->data.struct_field.name = field_name; - field_node->data.struct_field.type = nullptr; - field_node->data.struct_field.value = int_node; - enum_node->data.container_decl.fields.items[i] = field_node; - - // in C each enum value is in the global namespace. so we put them there too. - // at this point we can rely on the enum emitting successfully - if (is_anonymous) { - Buf *enum_val_name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)enum_const)); - add_global_var(c, enum_val_name, int_node); - } else { - AstNode *field_access_node = trans_create_node_field_access(c, - trans_create_node_symbol(c, full_type_name), field_name); - add_global_var(c, enum_val_name, field_access_node); - } - } - - if (is_anonymous) { - c->decl_table.put(ZigClangEnumDecl_getCanonicalDecl(enum_decl), enum_node); - return enum_node; - } else { - AstNode *symbol_node = trans_create_node_symbol(c, full_type_name); - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, enum_node); - c->decl_table.put(ZigClangEnumDecl_getCanonicalDecl(enum_decl), symbol_node); - return enum_node; - } -} - -static AstNode *demote_struct_to_opaque(Context *c, const ZigClangRecordDecl *record_decl, - Buf *full_type_name, Buf *bare_name) -{ - AstNode *opaque_node = trans_create_node_opaque(c); - if (full_type_name == nullptr) { - c->decl_table.put(ZigClangRecordDecl_getCanonicalDecl(record_decl), opaque_node); - return opaque_node; - } - AstNode *symbol_node = trans_create_node_symbol(c, full_type_name); - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, opaque_node); - c->decl_table.put(ZigClangRecordDecl_getCanonicalDecl(record_decl), symbol_node); - return symbol_node; -} - -static AstNode *resolve_record_decl(Context *c, const ZigClangRecordDecl *record_decl) { - auto existing_entry = c->decl_table.maybe_get(ZigClangRecordDecl_getCanonicalDecl(record_decl)); - if (existing_entry) { - return existing_entry->value; - } - - const char *raw_name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)record_decl); - const char *container_kind_name; - ContainerKind container_kind; - if (ZigClangRecordDecl_isUnion(record_decl)) { - container_kind_name = "union"; - container_kind = ContainerKindUnion; - } else if (ZigClangRecordDecl_isStruct(record_decl)) { - container_kind_name = "struct"; - container_kind = ContainerKindStruct; - } else { - emit_warning(c, ZigClangRecordDecl_getLocation(record_decl), - "skipping record %s, not a struct or union", raw_name); - c->decl_table.put(ZigClangRecordDecl_getCanonicalDecl(record_decl), nullptr); - return nullptr; - } - - bool is_anonymous = ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) || raw_name[0] == 0; - Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name); - Buf *full_type_name = (bare_name == nullptr) ? - nullptr : buf_sprintf("%s_%s", container_kind_name, buf_ptr(bare_name)); - - const ZigClangRecordDecl *record_def = ZigClangRecordDecl_getDefinition(record_decl); - if (record_def == nullptr) { - return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name); - } - - // count fields and validate - uint32_t field_count = 0; - for (auto it = reinterpret_cast(record_def)->field_begin(), - it_end = reinterpret_cast(record_def)->field_end(); - it != it_end; ++it, field_count += 1) - { - const clang::FieldDecl *field_decl = *it; - - if (field_decl->isBitField()) { - emit_warning(c, bitcast(field_decl->getLocation()), "%s %s demoted to opaque type - has bitfield", - container_kind_name, - is_anonymous ? "(anon)" : buf_ptr(bare_name)); - return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name); - } - } - - AstNode *struct_node = trans_create_node(c, NodeTypeContainerDecl); - struct_node->data.container_decl.kind = container_kind; - struct_node->data.container_decl.layout = ContainerLayoutExtern; - - // TODO handle attribute packed - - struct_node->data.container_decl.fields.resize(field_count); - - // must be before fields in case a circular reference happens - if (is_anonymous) { - c->decl_table.put(ZigClangRecordDecl_getCanonicalDecl(record_decl), struct_node); - } else { - c->decl_table.put(ZigClangRecordDecl_getCanonicalDecl(record_decl), trans_create_node_symbol(c, full_type_name)); - } - - uint32_t i = 0; - for (auto it = reinterpret_cast(record_def)->field_begin(), - it_end = reinterpret_cast(record_def)->field_end(); - it != it_end; ++it, i += 1) - { - const clang::FieldDecl *field_decl = *it; - - AstNode *field_node = trans_create_node(c, NodeTypeStructField); - field_node->data.struct_field.name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)field_decl)); - field_node->data.struct_field.type = trans_qual_type(c, bitcast(field_decl->getType()), - bitcast(field_decl->getLocation())); - - if (field_node->data.struct_field.type == nullptr) { - emit_warning(c, bitcast(field_decl->getLocation()), - "%s %s demoted to opaque type - unresolved type", - container_kind_name, - is_anonymous ? "(anon)" : buf_ptr(bare_name)); - - return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name); - } - - struct_node->data.container_decl.fields.items[i] = field_node; - } - - if (is_anonymous) { - return struct_node; - } else { - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, struct_node); - return trans_create_node_symbol(c, full_type_name); - } -} - -static AstNode *trans_ap_value(Context *c, const ZigClangAPValue *ap_value, ZigClangQualType qt, - ZigClangSourceLocation source_loc) -{ - switch (ZigClangAPValue_getKind(ap_value)) { - case ZigClangAPValueInt: - return trans_create_node_apint(c, ZigClangAPValue_getInt(ap_value)); - case ZigClangAPValueNone: - return trans_create_node(c, NodeTypeUndefinedLiteral); - case ZigClangAPValueArray: { - emit_warning(c, source_loc, "TODO add a test case for this code"); - - unsigned init_count = ZigClangAPValue_getArrayInitializedElts(ap_value); - unsigned all_count = ZigClangAPValue_getArraySize(ap_value); - unsigned leftover_count = all_count - init_count; - AstNode *init_node = trans_create_node(c, NodeTypeContainerInitExpr); - AstNode *arr_type_node = trans_qual_type(c, qt, source_loc); - if (leftover_count != 0) { // We can't use the size of the final array for a partial initializer. - bigint_init_unsigned(arr_type_node->data.array_type.size->data.int_literal.bigint, init_count); - } - init_node->data.container_init_expr.type = arr_type_node; - init_node->data.container_init_expr.kind = ContainerInitKindArray; - - const clang::Type *qt_type = reinterpret_cast(ZigClangQualType_getTypePtr(qt)); - ZigClangQualType child_qt = bitcast(qt_type->getAsArrayTypeUnsafe()->getElementType()); - - for (size_t i = 0; i < init_count; i += 1) { - const ZigClangAPValue *elem_ap_val = ZigClangAPValue_getArrayInitializedElt(ap_value, i); - AstNode *elem_node = trans_ap_value(c, elem_ap_val, child_qt, source_loc); - if (elem_node == nullptr) - return nullptr; - init_node->data.container_init_expr.entries.append(elem_node); - } - if (leftover_count == 0) { - return init_node; - } - - const ZigClangAPValue *filler_ap_val = ZigClangAPValue_getArrayFiller(ap_value); - AstNode *filler_node = trans_ap_value(c, filler_ap_val, child_qt, source_loc); - if (filler_node == nullptr) - return nullptr; - - AstNode* filler_arr_type = trans_create_node(c, NodeTypeArrayType); - *filler_arr_type = *arr_type_node; - filler_arr_type->data.array_type.size = trans_create_node_unsigned(c, 1); - - AstNode *filler_arr_1 = trans_create_node(c, NodeTypeContainerInitExpr); - filler_arr_1->data.container_init_expr.type = filler_arr_type; - filler_arr_1->data.container_init_expr.kind = ContainerInitKindArray; - filler_arr_1->data.container_init_expr.entries.append(filler_node); - - AstNode *rhs_node; - if (leftover_count == 1) { - rhs_node = filler_arr_1; - } else { - AstNode *amt_node = trans_create_node_unsigned(c, leftover_count); - rhs_node = trans_create_node_bin_op(c, filler_arr_1, BinOpTypeArrayMult, amt_node); - } - - if (init_count == 0) { - return rhs_node; - } - - return trans_create_node_bin_op(c, init_node, BinOpTypeArrayCat, rhs_node); - } - case ZigClangAPValueLValue: { - const ZigClangAPValueLValueBase lval_base = ZigClangAPValue_getLValueBase(ap_value); - if (const ZigClangExpr *expr = ZigClangAPValueLValueBase_dyn_cast_Expr(lval_base)) { - return trans_expr(c, ResultUsedYes, &c->global_scope->base, expr, TransRValue); - } - //const clang::ValueDecl *value_decl = lval_base.get(); - emit_warning(c, source_loc, "TODO handle initializer LValue clang::ValueDecl"); - return nullptr; - } - case ZigClangAPValueFloat: - emit_warning(c, source_loc, "unsupported initializer value kind: Float"); - return nullptr; - case ZigClangAPValueComplexInt: - emit_warning(c, source_loc, "unsupported initializer value kind: ComplexInt"); - return nullptr; - case ZigClangAPValueComplexFloat: - emit_warning(c, source_loc, "unsupported initializer value kind: ComplexFloat"); - return nullptr; - case ZigClangAPValueVector: - emit_warning(c, source_loc, "unsupported initializer value kind: Vector"); - return nullptr; - case ZigClangAPValueStruct: - emit_warning(c, source_loc, "unsupported initializer value kind: Struct"); - return nullptr; - case ZigClangAPValueUnion: - emit_warning(c, source_loc, "unsupported initializer value kind: Union"); - return nullptr; - case ZigClangAPValueMemberPointer: - emit_warning(c, source_loc, "unsupported initializer value kind: MemberPointer"); - return nullptr; - case ZigClangAPValueAddrLabelDiff: - emit_warning(c, source_loc, "unsupported initializer value kind: AddrLabelDiff"); - return nullptr; - case ZigClangAPValueIndeterminate: - emit_warning(c, source_loc, "unsupported initializer value kind: Indeterminate"); - return nullptr; - case ZigClangAPValueFixedPoint: - emit_warning(c, source_loc, "unsupported initializer value kind: FixedPoint"); - return nullptr; - } - zig_unreachable(); -} - -static void visit_var_decl(Context *c, const clang::VarDecl *var_decl) { - Buf *name = buf_create_from_str(ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)var_decl)); - - switch (var_decl->getTLSKind()) { - case clang::VarDecl::TLS_None: - break; - case clang::VarDecl::TLS_Static: - emit_warning(c, bitcast(var_decl->getLocation()), - "ignoring variable '%s' - static thread local storage", buf_ptr(name)); - return; - case clang::VarDecl::TLS_Dynamic: - emit_warning(c, bitcast(var_decl->getLocation()), - "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name)); - return; - } - - ZigClangQualType qt = bitcast(var_decl->getType()); - AstNode *var_type = trans_qual_type(c, qt, bitcast(var_decl->getLocation())); - if (var_type == nullptr) { - emit_warning(c, bitcast(var_decl->getLocation()), "ignoring variable '%s' - unresolved type", buf_ptr(name)); - return; - } - - bool is_extern = var_decl->hasExternalStorage(); - bool is_static = var_decl->isFileVarDecl(); - bool is_const = ZigClangQualType_isConstQualified(qt); - - if (is_static && !is_extern) { - AstNode *init_node; - if (var_decl->hasInit()) { - const ZigClangAPValue *ap_value = bitcast(var_decl->evaluateValue()); - if (ap_value == nullptr) { - emit_warning(c, bitcast(var_decl->getLocation()), - "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name)); - return; - } - init_node = trans_ap_value(c, ap_value, qt, bitcast(var_decl->getLocation())); - if (init_node == nullptr) - return; - } else { - init_node = trans_create_node(c, NodeTypeUndefinedLiteral); - } - - AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, init_node); - add_top_level_decl(c, name, var_node); - return; - } - - if (is_extern) { - AstNode *var_node = trans_create_node_var_decl_global(c, is_const, name, var_type, nullptr); - var_node->data.variable_declaration.is_extern = true; - add_top_level_decl(c, name, var_node); - return; - } - - emit_warning(c, bitcast(var_decl->getLocation()), - "ignoring variable '%s' - non-extern, non-static variable", buf_ptr(name)); - return; -} - -static bool decl_visitor(void *context, const ZigClangDecl *decl) { - Context *c = (Context*)context; - - switch (ZigClangDecl_getKind(decl)) { - case ZigClangDeclFunction: - visit_fn_decl(c, reinterpret_cast(decl)); - break; - case ZigClangDeclTypedef: - resolve_typedef_decl(c, reinterpret_cast(decl)); - break; - case ZigClangDeclEnum: - resolve_enum_decl(c, reinterpret_cast(decl)); - break; - case ZigClangDeclRecord: - resolve_record_decl(c, reinterpret_cast(decl)); - break; - case ZigClangDeclVar: - visit_var_decl(c, reinterpret_cast(decl)); - break; - default: - emit_warning(c, ZigClangDecl_getLocation(decl), "ignoring %s decl", ZigClangDecl_getDeclKindName(decl)); - } - - return true; -} - -static bool name_exists_global(Context *c, Buf *name) { - return get_global(c, name) != nullptr; -} - -static bool name_exists_scope(Context *c, Buf *name, TransScope *scope) { - while (scope != nullptr) { - if (scope->id == TransScopeIdVar) { - TransScopeVar *var_scope = (TransScopeVar *)scope; - if (buf_eql_buf(name, var_scope->zig_name)) { - return true; - } - } - scope = scope->parent; - } - return name_exists_global(c, name); -} - -static Buf *get_unique_name(Context *c, Buf *name, TransScope *scope) { - Buf *proposed_name = name; - int count = 0; - while (name_exists_scope(c, proposed_name, scope)) { - if (proposed_name == name) { - proposed_name = buf_alloc(); - } - buf_resize(proposed_name, 0); - buf_appendf(proposed_name, "%s_%d", buf_ptr(name), count); - count += 1; - } - return proposed_name; -} - -static TransScopeRoot *trans_scope_root_create(Context *c) { - TransScopeRoot *result = allocate(1); - result->base.id = TransScopeIdRoot; - return result; -} - -static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope) { - TransScopeWhile *result = allocate(1); - result->base.id = TransScopeIdWhile; - result->base.parent = parent_scope; - result->node = trans_create_node(c, NodeTypeWhileExpr); - return result; -} - -static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope) { - TransScopeBlock *result = allocate(1); - result->base.id = TransScopeIdBlock; - result->base.parent = parent_scope; - result->node = trans_create_node(c, NodeTypeBlock); - return result; -} - -static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name) { - TransScopeVar *result = allocate(1); - result->base.id = TransScopeIdVar; - result->base.parent = parent_scope; - result->c_name = wanted_name; - result->zig_name = get_unique_name(c, wanted_name, parent_scope); - return result; -} - -static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) { - TransScopeSwitch *result = allocate(1); - result->base.id = TransScopeIdSwitch; - result->base.parent = parent_scope; - result->switch_node = trans_create_node(c, NodeTypeSwitchExpr); - return result; -} - -static TransScopeBlock *trans_scope_block_find(TransScope *scope) { - while (scope != nullptr) { - if (scope->id == TransScopeIdBlock) { - return (TransScopeBlock *)scope; - } - scope = scope->parent; - } - return nullptr; -} - -static void render_aliases(Context *c) { - for (size_t i = 0; i < c->aliases.length; i += 1) { - Alias *alias = &c->aliases.at(i); - if (name_exists_global(c, alias->new_name)) - continue; - - add_global_var(c, alias->new_name, trans_create_node_symbol(c, alias->canon_name)); - } -} - -static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node); - -static AstNode *trans_lookup_ast_container(Context *c, AstNode *type_node) { - if (type_node == nullptr) { - return nullptr; - } else if (type_node->type == NodeTypeContainerDecl) { - return type_node; - } else if (type_node->type == NodeTypePrefixOpExpr) { - return type_node; - } else if (type_node->type == NodeTypeSymbol) { - AstNode *existing_node = get_global(c, type_node->data.symbol_expr.symbol); - if (existing_node == nullptr) - return nullptr; - if (existing_node->type != NodeTypeVariableDeclaration) - return nullptr; - return trans_lookup_ast_container(c, existing_node->data.variable_declaration.expr); - } else if (type_node->type == NodeTypeFieldAccessExpr) { - AstNode *container_node = trans_lookup_ast_container_typeof(c, type_node->data.field_access_expr.struct_expr); - if (container_node == nullptr) - return nullptr; - if (container_node->type != NodeTypeContainerDecl) - return container_node; - - for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) { - AstNode *field_node = container_node->data.container_decl.fields.items[i]; - if (buf_eql_buf(field_node->data.struct_field.name, type_node->data.field_access_expr.field_name)) { - return trans_lookup_ast_container(c, field_node->data.struct_field.type); - } - } - return nullptr; - } else { - return nullptr; - } -} - -static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node) { - if (ref_node->type == NodeTypeSymbol) { - AstNode *existing_node = get_global(c, ref_node->data.symbol_expr.symbol); - if (existing_node == nullptr) - return nullptr; - if (existing_node->type != NodeTypeVariableDeclaration) - return nullptr; - return trans_lookup_ast_container(c, existing_node->data.variable_declaration.type); - } else if (ref_node->type == NodeTypeFieldAccessExpr) { - AstNode *container_node = trans_lookup_ast_container_typeof(c, ref_node->data.field_access_expr.struct_expr); - if (container_node == nullptr) - return nullptr; - if (container_node->type != NodeTypeContainerDecl) - return container_node; - for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) { - AstNode *field_node = container_node->data.container_decl.fields.items[i]; - if (buf_eql_buf(field_node->data.struct_field.name, ref_node->data.field_access_expr.field_name)) { - return trans_lookup_ast_container(c, field_node->data.struct_field.type); - } - } - return nullptr; - } else { - return nullptr; - } -} - -static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) { - AstNode *prefix_node = trans_lookup_ast_container_typeof(c, ref_node); - if (prefix_node == nullptr) - return nullptr; - if (prefix_node->type != NodeTypePrefixOpExpr) - return nullptr; - if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional) - return nullptr; - - AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr; - if (fn_proto_node->type != NodeTypeFnProto) - return nullptr; - - return fn_proto_node; -} - -static void render_macros(Context *c) { - auto it = c->macro_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - AstNode *proto_node; - AstNode *value_node = entry->value; - if (value_node->type == NodeTypeFnDef) { - add_top_level_decl(c, value_node->data.fn_def.fn_proto->data.fn_proto.name, value_node); - } else if ((proto_node = trans_lookup_ast_maybe_fn(c, value_node))) { - // If a macro aliases a global variable which is a function pointer, we conclude that - // the macro is intended to represent a function that assumes the function pointer - // variable is non-null and calls it. - AstNode *inline_fn_node = trans_create_node_inline_fn(c, entry->key, value_node, proto_node); - add_top_level_decl(c, entry->key, inline_fn_node); - } else { - add_global_var(c, entry->key, value_node); - } - } -} - -static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i); -static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i); -static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i); - -static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, bool negate) { - CTok *tok = &ctok->tokens.at(*tok_i); - if (tok->id == CTokIdNumLitInt) { - *tok_i += 1; - switch (tok->data.num_lit_int.suffix) { - case CNumLitSuffixNone: - return trans_create_node_unsigned_negative(c, tok->data.num_lit_int.x, negate); - case CNumLitSuffixL: - return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_long"); - case CNumLitSuffixU: - return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_uint"); - case CNumLitSuffixLU: - return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulong"); - case CNumLitSuffixLL: - return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_longlong"); - case CNumLitSuffixLLU: - return trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate, "c_ulonglong"); - } - zig_unreachable(); - } else if (tok->id == CTokIdNumLitFloat) { - *tok_i += 1; - double value = negate ? -tok->data.num_lit_float : tok->data.num_lit_float; - return trans_create_node_float_lit(c, value); - } - return nullptr; -} - -static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i) { - CTok *tok = &ctok->tokens.at(*tok_i); - switch (tok->id) { - case CTokIdCharLit: - *tok_i += 1; - return trans_create_node_unsigned(c, tok->data.char_lit); - case CTokIdStrLit: - *tok_i += 1; - return trans_create_node_str_lit_c(c, buf_create_from_buf(&tok->data.str_lit)); - case CTokIdMinus: - *tok_i += 1; - return parse_ctok_num_lit(c, ctok, tok_i, true); - case CTokIdNumLitInt: - case CTokIdNumLitFloat: - return parse_ctok_num_lit(c, ctok, tok_i, false); - case CTokIdSymbol: - { - *tok_i += 1; - Buf *symbol_name = buf_create_from_buf(&tok->data.symbol); - return trans_create_node_symbol(c, symbol_name); - } - case CTokIdLParen: - { - *tok_i += 1; - AstNode *inner_node = parse_ctok_expr(c, ctok, tok_i); - if (inner_node == nullptr) { - return nullptr; - } - - CTok *next_tok = &ctok->tokens.at(*tok_i); - if (next_tok->id == CTokIdRParen) { - *tok_i += 1; - return inner_node; - } - - AstNode *node_to_cast = parse_ctok_expr(c, ctok, tok_i); - if (node_to_cast == nullptr) { - return nullptr; - } - - CTok *next_tok2 = &ctok->tokens.at(*tok_i); - if (next_tok2->id != CTokIdRParen) { - return nullptr; - } - *tok_i += 1; - - - //if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Pointer) - // @ptrCast(dest, x) - //else if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Integer) - // @intToPtr(dest, x) - //else - // (dest)(x) - - AstNode *import_builtin = trans_create_node_builtin_fn_call_str(c, "import"); - import_builtin->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("builtin"))); - AstNode *typeid_type = trans_create_node_field_access_str(c, import_builtin, "TypeId"); - AstNode *typeid_pointer = trans_create_node_field_access_str(c, typeid_type, "Pointer"); - AstNode *typeid_integer = trans_create_node_field_access_str(c, typeid_type, "Int"); - AstNode *typeof_x = trans_create_node_builtin_fn_call_str(c, "typeOf"); - typeof_x->data.fn_call_expr.params.append(node_to_cast); - AstNode *typeid_value = trans_create_node_builtin_fn_call_str(c, "typeId"); - typeid_value->data.fn_call_expr.params.append(typeof_x); - - AstNode *outer_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_pointer); - AstNode *inner_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_integer); - AstNode *inner_if_then = trans_create_node_builtin_fn_call_str(c, "intToPtr"); - inner_if_then->data.fn_call_expr.params.append(inner_node); - inner_if_then->data.fn_call_expr.params.append(node_to_cast); - AstNode *inner_if_else = trans_create_node_cast(c, inner_node, node_to_cast); - AstNode *inner_if = trans_create_node_if(c, inner_if_cond, inner_if_then, inner_if_else); - AstNode *outer_if_then = trans_create_node_builtin_fn_call_str(c, "ptrCast"); - outer_if_then->data.fn_call_expr.params.append(inner_node); - outer_if_then->data.fn_call_expr.params.append(node_to_cast); - return trans_create_node_if(c, outer_if_cond, outer_if_then, inner_if); - } - case CTokIdDot: - case CTokIdEOF: - case CTokIdRParen: - case CTokIdAsterisk: - case CTokIdBang: - case CTokIdTilde: - case CTokIdShl: - case CTokIdLt: - // not able to make sense of this - return nullptr; - } - zig_unreachable(); -} - -static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i) { - return parse_ctok_prefix_op_expr(c, ctok, tok_i); -} - -static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) { - AstNode *node = parse_ctok_primary_expr(c, ctok, tok_i); - if (node == nullptr) - return nullptr; - - while (true) { - CTok *first_tok = &ctok->tokens.at(*tok_i); - if (first_tok->id == CTokIdDot) { - *tok_i += 1; - - CTok *name_tok = &ctok->tokens.at(*tok_i); - if (name_tok->id != CTokIdSymbol) { - return nullptr; - } - *tok_i += 1; - - node = trans_create_node_field_access(c, node, buf_create_from_buf(&name_tok->data.symbol)); - } else if (first_tok->id == CTokIdAsterisk) { - *tok_i += 1; - - node = trans_create_node_ptr_type(c, false, false, node, PtrLenC); - } else if (first_tok->id == CTokIdShl) { - *tok_i += 1; - - AstNode *rhs_node = parse_ctok_expr(c, ctok, tok_i); - if (rhs_node == nullptr) - return nullptr; - node = trans_create_node_bin_op(c, node, BinOpTypeBitShiftLeft, rhs_node); - } else { - return node; - } - } -} - -static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) { - CTok *op_tok = &ctok->tokens.at(*tok_i); - - switch (op_tok->id) { - case CTokIdBang: - { - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_prefix_op(c, PrefixOpBoolNot, prefix_op_expr); - } - case CTokIdMinus: - { - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_prefix_op(c, PrefixOpNegation, prefix_op_expr); - } - case CTokIdTilde: - { - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_prefix_op(c, PrefixOpBinNot, prefix_op_expr); - } - case CTokIdAsterisk: - { - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_ptr_deref(c, prefix_op_expr); - } - default: - return parse_ctok_suffix_op_expr(c, ctok, tok_i); - } -} - -static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) { - tokenize_c_macro(ctok, (const uint8_t *)char_ptr); - - if (ctok->error) { - return; - } - - size_t tok_i = 0; - CTok *name_tok = &ctok->tokens.at(tok_i); - assert(name_tok->id == CTokIdSymbol && buf_eql_buf(&name_tok->data.symbol, name)); - tok_i += 1; - - AstNode *result_node = parse_ctok_suffix_op_expr(c, ctok, &tok_i); - if (result_node == nullptr) { - return; - } - CTok *eof_tok = &ctok->tokens.at(tok_i); - if (eof_tok->id != CTokIdEOF) { - return; - } - if (result_node->type == NodeTypeSymbol) { - // if it equals itself, ignore. for example, from stdio.h: - // #define stdin stdin - Buf *symbol_name = result_node->data.symbol_expr.symbol; - if (buf_eql_buf(name, symbol_name)) { - return; - } - } - c->macro_table.put(name, result_node); -} - -static void process_preprocessor_entities(Context *c, ZigClangASTUnit *zunit) { - clang::ASTUnit *unit = reinterpret_cast(zunit); - CTokenize ctok = {{0}}; - - // TODO if we see #undef, delete it from the table - - for (clang::PreprocessedEntity *entity : unit->getLocalPreprocessingEntities()) { - switch (entity->getKind()) { - case clang::PreprocessedEntity::InvalidKind: - case clang::PreprocessedEntity::InclusionDirectiveKind: - case clang::PreprocessedEntity::MacroExpansionKind: - continue; - case clang::PreprocessedEntity::MacroDefinitionKind: - { - clang::MacroDefinitionRecord *macro = static_cast(entity); - const char *raw_name = macro->getName()->getNameStart(); - clang::SourceRange range = macro->getSourceRange(); - ZigClangSourceLocation begin_loc = bitcast(range.getBegin()); - ZigClangSourceLocation end_loc = bitcast(range.getEnd()); - - if (ZigClangSourceLocation_eq(begin_loc, end_loc)) { - // this means it is a macro without a value - // we don't care about such things - continue; - } - Buf *name = buf_create_from_str(raw_name); - if (name_exists_global(c, name)) { - continue; - } - - const char *begin_c = ZigClangSourceManager_getCharacterData(c->source_manager, begin_loc); - process_macro(c, &ctok, name, begin_c); - } - } - } -} - -Error parse_h_file(CodeGen *codegen, AstNode **out_root_node, - Stage2ErrorMsg **errors_ptr, size_t *errors_len, - const char **args_begin, const char **args_end, - Stage2TranslateMode mode, const char *resources_path) -{ - Context context = {0}; - Context *c = &context; - c->warnings_on = codegen->verbose_cimport; - if (mode == Stage2TranslateModeImport) { - c->visib_mod = VisibModPub; - c->want_export = false; - } else { - c->visib_mod = VisibModPub; - c->want_export = true; - } - c->decl_table.init(8); - c->macro_table.init(8); - c->global_table.init(8); - c->ptr_params.init(8); - c->codegen = codegen; - c->global_scope = trans_scope_root_create(c); - - ZigClangASTUnit *ast_unit = ZigClangLoadFromCommandLine(args_begin, args_end, errors_ptr, errors_len, - resources_path); - if (ast_unit == nullptr) { - if (*errors_len == 0) return ErrorNoMem; - return ErrorCCompileErrors; - } - - c->ctx = ZigClangASTUnit_getASTContext(ast_unit); - c->source_manager = ZigClangASTUnit_getSourceManager(ast_unit); - c->root = trans_create_node(c, NodeTypeContainerDecl); - c->root->data.container_decl.is_root = true; - - ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, decl_visitor); - - process_preprocessor_entities(c, ast_unit); - - render_macros(c); - render_aliases(c); - - *out_root_node = c->root; - - ZigClangASTUnit_delete(ast_unit); - - return ErrorNone; -} diff --git a/src/translate_c.hpp b/src/translate_c.hpp deleted file mode 100644 index 4eac26ddb..000000000 --- a/src/translate_c.hpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - - -#ifndef ZIG_PARSEC_HPP -#define ZIG_PARSEC_HPP - -#include "all_types.hpp" - -Error parse_h_file(CodeGen *codegen, AstNode **out_root_node, - Stage2ErrorMsg **errors_ptr, size_t *errors_len, - const char **args_begin, const char **args_end, - Stage2TranslateMode mode, const char *resources_path); - -#endif diff --git a/src/userland.cpp b/src/userland.cpp index d82f776cd..263ef0cbc 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -9,8 +9,7 @@ Error stage2_translate_c(struct Stage2Ast **out_ast, struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, enum Stage2TranslateMode mode, - const char *resources_path) + const char **args_begin, const char **args_end, const char *resources_path) { const char *msg = "stage0 called stage2_translate_c"; stage2_panic(msg, strlen(msg)); @@ -59,3 +58,33 @@ stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { const char *msg = "stage0 called stage2_DepTokenizer_next"; stage2_panic(msg, strlen(msg)); } + + +struct Stage2Progress { + int trash; +}; + +struct Stage2ProgressNode { + int trash; +}; + +Stage2Progress *stage2_progress_create(void) { + return nullptr; +} + +void stage2_progress_destroy(Stage2Progress *progress) {} + +Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +void stage2_progress_end(Stage2ProgressNode *node) {} +void stage2_progress_complete_one(Stage2ProgressNode *node) {} +void stage2_progress_disable_tty(Stage2Progress *progress) {} +void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} diff --git a/src/userland.h b/src/userland.h index c92caf327..fe3f072ae 100644 --- a/src/userland.h +++ b/src/userland.h @@ -77,12 +77,7 @@ enum Error { ErrorNoSpaceLeft, ErrorNotLazy, ErrorIsAsync, -}; - -// ABI warning -enum Stage2TranslateMode { - Stage2TranslateModeImport, - Stage2TranslateModeTranslate, + ErrorImportOutsidePkgPath, }; // ABI warning @@ -103,8 +98,7 @@ struct Stage2Ast; // ABI warning ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast, struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, enum Stage2TranslateMode mode, - const char *resources_path); + const char **args_begin, const char **args_end, const char *resources_path); // ABI warning ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len); @@ -156,4 +150,28 @@ ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self); // ABI warning ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self); +// ABI warning +struct Stage2Progress; +// ABI warning +struct Stage2ProgressNode; +// ABI warning +ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); +// ABI warning +ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, + size_t completed_count, size_t estimated_total_items); + #endif diff --git a/src/util.cpp b/src/util.cpp index 13bfbbcd4..56f1de983 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,13 +5,12 @@ * See http://opensource.org/licenses/MIT */ -#include -#include -#include - #include "util.hpp" #include "userland.h" +#include +#include + void zig_panic(const char *format, ...) { va_list ap; va_start(ap, format); @@ -119,3 +118,21 @@ Slice SplitIterator_rest(SplitIterator *self) { SplitIterator memSplit(Slice buffer, Slice split_bytes) { return SplitIterator{0, buffer, split_bytes}; } + +void zig_pretty_print_bytes(FILE *f, double n) { + if (n > 1024.0 * 1024.0 * 1024.0) { + fprintf(f, "%.03f GiB", n / 1024.0 / 1024.0 / 1024.0); + return; + } + if (n > 1024.0 * 1024.0) { + fprintf(f, "%.03f MiB", n / 1024.0 / 1024.0); + return; + } + if (n > 1024.0) { + fprintf(f, "%.03f KiB", n / 1024.0); + return; + } + fprintf(f, "%.03f bytes", n ); + return; +} + diff --git a/src/util.hpp b/src/util.hpp index 8abcef32c..8dcd41438 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -8,6 +8,8 @@ #ifndef ZIG_UTIL_HPP #define ZIG_UTIL_HPP +#include "memory_profiling.hpp" + #include #include #include @@ -24,6 +26,8 @@ #define ATTRIBUTE_NORETURN __declspec(noreturn) #define ATTRIBUTE_MUST_USE +#define BREAKPOINT __debugbreak() + #else #define ATTRIBUTE_COLD __attribute__((cold)) @@ -32,12 +36,21 @@ #define ATTRIBUTE_NORETURN __attribute__((noreturn)) #define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result)) +#if defined(__MINGW32__) || defined(__MINGW64__) +#define BREAKPOINT __debugbreak() +#elif defined(__clang__) +#define BREAKPOINT __builtin_debugtrap() +#elif defined(__GNUC__) +#define BREAKPOINT __builtin_trap() +#else +#include +#define BREAKPOINT raise(SIGTRAP) +#endif + #endif #include "softfloat.hpp" -#define BREAKPOINT __asm("int $0x03") - ATTRIBUTE_COLD ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF(1, 2) @@ -96,7 +109,10 @@ static inline int ctzll(unsigned long long mask) { template -ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { +ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count, const char *name = nullptr) { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_alloc(name, count, sizeof(T)); +#endif #ifndef NDEBUG // make behavior when size == 0 portable if (count == 0) @@ -109,7 +125,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { } template -ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { +ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count, const char *name = nullptr) { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_alloc(name, count, sizeof(T)); +#endif #ifndef NDEBUG // make behavior when size == 0 portable if (count == 0) @@ -122,7 +141,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { } template -static inline T *reallocate(T *old, size_t old_count, size_t new_count) { +static inline T *reallocate(T *old, size_t old_count, size_t new_count, const char *name = nullptr) { T *ptr = reallocate_nonzero(old, old_count, new_count); if (new_count > old_count) { memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); @@ -131,7 +150,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) { } template -static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { +static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count, const char *name = nullptr) { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_dealloc(name, old_count, sizeof(T)); + memprof_alloc(name, new_count, sizeof(T)); +#endif #ifndef NDEBUG // make behavior when size == 0 portable if (new_count == 0 && old == nullptr) @@ -143,6 +166,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) return ptr; } +template +static inline void deallocate(T *old, size_t count, const char *name = nullptr) { +#ifdef ZIG_ENABLE_MEM_PROFILE + memprof_dealloc(name, count, sizeof(T)); +#endif + free(old); +} + +template +static inline void destroy(T *old, const char *name = nullptr) { + return deallocate(old, 1, name); +} + template constexpr size_t array_length(const T (&)[n]) { return n; @@ -225,6 +261,8 @@ static inline double zig_f16_to_double(float16_t x) { return z; } +void zig_pretty_print_bytes(FILE *f, double n); + template struct Optional { T value; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index c2ea1dc3c..c2ad71937 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -13,6 +13,7 @@ * 3. Prevent C++ from infecting the rest of the project. */ #include "zig_clang.h" +#include "list.hpp" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -1372,6 +1373,31 @@ static_assert((clang::ElaboratedTypeKeyword)ZigClangETK_Enum == clang::ETK_Enum, static_assert((clang::ElaboratedTypeKeyword)ZigClangETK_Typename == clang::ETK_Typename, ""); static_assert((clang::ElaboratedTypeKeyword)ZigClangETK_None == clang::ETK_None, ""); +void ZigClang_detect_enum_EntityKind(clang::PreprocessedEntity::EntityKind x) { + switch (x) { + case clang::PreprocessedEntity::InvalidKind: + case clang::PreprocessedEntity::MacroExpansionKind: + case clang::PreprocessedEntity::MacroDefinitionKind: + case clang::PreprocessedEntity::InclusionDirectiveKind: + break; + } +} +static_assert((clang::PreprocessedEntity::EntityKind)ZigClangPreprocessedEntity_InvalidKind == clang::PreprocessedEntity::InvalidKind, ""); +static_assert((clang::PreprocessedEntity::EntityKind)ZigClangPreprocessedEntity_MacroExpansionKind == clang::PreprocessedEntity::MacroExpansionKind, ""); +static_assert((clang::PreprocessedEntity::EntityKind)ZigClangPreprocessedEntity_MacroDefinitionKind == clang::PreprocessedEntity::MacroDefinitionKind, ""); +static_assert((clang::PreprocessedEntity::EntityKind)ZigClangPreprocessedEntity_InclusionDirectiveKind == clang::PreprocessedEntity::InclusionDirectiveKind, ""); + + +void ZigClang_detect_enum_ConstExprUsage(clang::Expr::ConstExprUsage x) { + switch (x) { + case clang::Expr::EvaluateForCodeGen: + case clang::Expr::EvaluateForMangling: + break; + } +} +static_assert((clang::Expr::ConstExprUsage)ZigClangExpr_EvaluateForCodeGen == clang::Expr::EvaluateForCodeGen, ""); +static_assert((clang::Expr::ConstExprUsage)ZigClangExpr_EvaluateForMangling == clang::Expr::EvaluateForMangling, ""); + static_assert(sizeof(ZigClangAPValue) == sizeof(clang::APValue), ""); @@ -1432,6 +1458,43 @@ static ZigClangDeclStmt_const_decl_iterator bitcast(clang::DeclStmt::const_decl_ return dest; } +static_assert(sizeof(ZigClangPreprocessingRecord_iterator) == sizeof(clang::PreprocessingRecord::iterator), ""); +static ZigClangPreprocessingRecord_iterator bitcast(clang::PreprocessingRecord::iterator src) { + ZigClangPreprocessingRecord_iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangPreprocessingRecord_iterator)); + return dest; +} +static clang::PreprocessingRecord::iterator bitcast(ZigClangPreprocessingRecord_iterator src) { + clang::PreprocessingRecord::iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangPreprocessingRecord_iterator)); + return dest; +} + +static_assert(sizeof(ZigClangRecordDecl_field_iterator) == sizeof(clang::RecordDecl::field_iterator), ""); +static ZigClangRecordDecl_field_iterator bitcast(clang::RecordDecl::field_iterator src) { + ZigClangRecordDecl_field_iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangRecordDecl_field_iterator)); + return dest; +} +static clang::RecordDecl::field_iterator bitcast(ZigClangRecordDecl_field_iterator src) { + clang::RecordDecl::field_iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangRecordDecl_field_iterator)); + return dest; +} + +static_assert(sizeof(ZigClangEnumDecl_enumerator_iterator) == sizeof(clang::EnumDecl::enumerator_iterator), ""); +static ZigClangEnumDecl_enumerator_iterator bitcast(clang::EnumDecl::enumerator_iterator src) { + ZigClangEnumDecl_enumerator_iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangEnumDecl_enumerator_iterator)); + return dest; +} +static clang::EnumDecl::enumerator_iterator bitcast(ZigClangEnumDecl_enumerator_iterator src) { + clang::EnumDecl::enumerator_iterator dest; + memcpy(&dest, static_cast(&src), sizeof(ZigClangEnumDecl_enumerator_iterator)); + return dest; +} + + ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, ZigClangSourceLocation Loc) { @@ -1488,6 +1551,28 @@ bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *self, void *contex reinterpret_cast(Fn)); } +struct ZigClangPreprocessingRecord_iterator ZigClangASTUnit_getLocalPreprocessingEntities_begin( + struct ZigClangASTUnit *self) +{ + auto casted = reinterpret_cast(self); + return bitcast(casted->getLocalPreprocessingEntities().begin()); +} + +struct ZigClangPreprocessingRecord_iterator ZigClangASTUnit_getLocalPreprocessingEntities_end( + struct ZigClangASTUnit *self) +{ + auto casted = reinterpret_cast(self); + return bitcast(casted->getLocalPreprocessingEntities().end()); +} + +struct ZigClangPreprocessedEntity *ZigClangPreprocessingRecord_iterator_deref( + struct ZigClangPreprocessingRecord_iterator self) +{ + clang::PreprocessingRecord::iterator casted = bitcast(self); + clang::PreprocessedEntity *result = *casted; + return reinterpret_cast(result); +} + const ZigClangRecordDecl *ZigClangRecordType_getDecl(const ZigClangRecordType *record_ty) { const clang::RecordDecl *record_decl = reinterpret_cast(record_ty)->getDecl(); return reinterpret_cast(record_decl); @@ -1503,6 +1588,11 @@ const ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const ZigClangRecordD return reinterpret_cast(tag_decl); } +const ZigClangFieldDecl *ZigClangFieldDecl_getCanonicalDecl(const ZigClangFieldDecl *field_decl) { + const clang::FieldDecl *canon_decl = reinterpret_cast(field_decl)->getCanonicalDecl(); + return reinterpret_cast(canon_decl); +} + const ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const ZigClangEnumDecl *enum_decl) { const clang::TagDecl *tag_decl = reinterpret_cast(enum_decl)->getCanonicalDecl(); return reinterpret_cast(tag_decl); @@ -1513,6 +1603,54 @@ const ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const Zi return reinterpret_cast(decl); } +const ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self) { + const clang::FunctionDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + +const ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self) { + const clang::VarDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + +const char* ZigClangVarDecl_getSectionAttribute(const struct ZigClangVarDecl *self, size_t *len) { + auto casted = reinterpret_cast(self); + if (const clang::SectionAttr *SA = casted->getAttr()) { + llvm::StringRef str_ref = SA->getName(); + *len = str_ref.size(); + return (const char *)str_ref.bytes_begin(); + } + return nullptr; +} + +bool ZigClangRecordDecl_getPackedAttribute(const ZigClangRecordDecl *zig_record_decl) { + const clang::RecordDecl *record_decl = reinterpret_cast(zig_record_decl); + if (record_decl->getAttr()) { + return true; + } + return false; +} + +unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx) { + auto casted_self = reinterpret_cast(self); + auto casted_ctx = const_cast(reinterpret_cast(ctx)); + if (const clang::AlignedAttr *AA = casted_self->getAttr()) { + return AA->getAlignment(*casted_ctx); + } + // Zero means no explicit alignment factor was specified + return 0; +} + +unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx) { + auto casted_self = reinterpret_cast(self); + auto casted_ctx = const_cast(reinterpret_cast(ctx)); + if (const clang::AlignedAttr *AA = casted_self->getAttr()) { + return AA->getAlignment(*casted_ctx); + } + // Zero means no explicit alignment factor was specified + return 0; +} + const ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const ZigClangRecordDecl *zig_record_decl) { const clang::RecordDecl *record_decl = reinterpret_cast(zig_record_decl); const clang::RecordDecl *definition = record_decl->getDefinition(); @@ -1537,10 +1675,15 @@ bool ZigClangRecordDecl_isAnonymousStructOrUnion(const ZigClangRecordDecl *recor return reinterpret_cast(record_decl)->isAnonymousStructOrUnion(); } -const char *ZigClangDecl_getName_bytes_begin(const ZigClangDecl *zig_decl) { - const clang::Decl *decl = reinterpret_cast(zig_decl); - const clang::NamedDecl *named_decl = static_cast(decl); - return (const char *)named_decl->getName().bytes_begin(); +const ZigClangNamedDecl* ZigClangDecl_castToNamedDecl(const ZigClangDecl *self) { + auto casted = reinterpret_cast(self); + auto cast = clang::dyn_cast(casted); + return reinterpret_cast(cast); +} + +const char *ZigClangNamedDecl_getName_bytes_begin(const ZigClangNamedDecl *self) { + auto casted = reinterpret_cast(self); + return (const char *)casted->getName().bytes_begin(); } ZigClangDeclKind ZigClangDecl_getKind(const struct ZigClangDecl *self) { @@ -1617,6 +1760,56 @@ const struct ZigClangStmt *ZigClangFunctionDecl_getBody(const struct ZigClangFun return reinterpret_cast(stmt); } +bool ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefinition(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->doesDeclarationForceExternallyVisibleDefinition(); +} + +bool ZigClangFunctionDecl_isThisDeclarationADefinition(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isThisDeclarationADefinition(); +} + +bool ZigClangFunctionDecl_doesThisDeclarationHaveABody(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->doesThisDeclarationHaveABody(); +} + +bool ZigClangFunctionDecl_isDefined(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isDefined(); +} + +const ZigClangFunctionDecl* ZigClangFunctionDecl_getDefinition(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return reinterpret_cast(casted->getDefinition()); +} + +bool ZigClangTagDecl_isThisDeclarationADefinition(const struct ZigClangTagDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isThisDeclarationADefinition(); +} + +bool ZigClangFunctionDecl_isInlineSpecified(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isInlineSpecified(); +} + +const char* ZigClangFunctionDecl_getSectionAttribute(const struct ZigClangFunctionDecl *self, size_t *len) { + auto casted = reinterpret_cast(self); + if (const clang::SectionAttr *SA = casted->getAttr()) { + llvm::StringRef str_ref = SA->getName(); + *len = str_ref.size(); + return (const char *)str_ref.bytes_begin(); + } + return nullptr; +} + +const ZigClangExpr *ZigClangOpaqueValueExpr_getSourceExpr(const ZigClangOpaqueValueExpr *self) { + auto casted = reinterpret_cast(self); + return reinterpret_cast(casted->getSourceExpr()); +} + const ZigClangTypedefNameDecl *ZigClangTypedefType_getDecl(const ZigClangTypedefType *self) { auto casted = reinterpret_cast(self); const clang::TypedefNameDecl *name_decl = casted->getDecl(); @@ -1690,16 +1883,49 @@ ZigClangQualType ZigClangType_getPointeeType(const ZigClangType *self) { return bitcast(casted->getPointeeType()); } +bool ZigClangType_isBooleanType(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + return casted->isBooleanType(); +} + bool ZigClangType_isVoidType(const ZigClangType *self) { auto casted = reinterpret_cast(self); return casted->isVoidType(); } +bool ZigClangType_isArrayType(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + return casted->isArrayType(); +} + +bool ZigClangType_isRecordType(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + return casted->isRecordType(); +} + const char *ZigClangType_getTypeClassName(const ZigClangType *self) { auto casted = reinterpret_cast(self); return casted->getTypeClassName(); } +const ZigClangArrayType *ZigClangType_getAsArrayTypeUnsafe(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + const clang::ArrayType *result = casted->getAsArrayTypeUnsafe(); + return reinterpret_cast(result); +} + +const ZigClangRecordType *ZigClangType_getAsRecordType(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + const clang::RecordType *result = casted->getAsStructureType(); + return reinterpret_cast(result); +} + +const ZigClangRecordType *ZigClangType_getAsUnionType(const ZigClangType *self) { + auto casted = reinterpret_cast(self); + const clang::RecordType *result = casted->getAsUnionType(); + return reinterpret_cast(result); +} + ZigClangSourceLocation ZigClangStmt_getBeginLoc(const ZigClangStmt *self) { auto casted = reinterpret_cast(self); return bitcast(casted->getBeginLoc()); @@ -1738,6 +1964,52 @@ bool ZigClangExpr_EvaluateAsBooleanCondition(const ZigClangExpr *self, bool *res return casted->EvaluateAsBooleanCondition(*result, *casted_ctx, in_constant_context); } +bool ZigClangExpr_EvaluateAsFloat(const ZigClangExpr *self, ZigClangAPFloat **result, + const struct ZigClangASTContext *ctx) +{ + llvm::APFloat *ap_float = new llvm::APFloat(0.0f); + *result = reinterpret_cast(ap_float); + auto casted = reinterpret_cast(self); + auto casted_ctx = reinterpret_cast(ctx); + return casted->EvaluateAsFloat(*ap_float, *casted_ctx); +} + +bool ZigClangExpr_EvaluateAsConstantExpr(const ZigClangExpr *self, ZigClangExprEvalResult *result, + ZigClangExpr_ConstExprUsage usage, const struct ZigClangASTContext *ctx) +{ + auto casted_self = reinterpret_cast(self); + auto casted_ctx = reinterpret_cast(ctx); + clang::Expr::EvalResult eval_result; + if (!casted_self->EvaluateAsConstantExpr(eval_result, (clang::Expr::ConstExprUsage)usage, *casted_ctx)) { + return false; + } + *result = bitcast(eval_result); + return true; +} + +const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *self, unsigned i) { + auto casted = reinterpret_cast(self); + const clang::Expr *result = casted->getInit(i); + return reinterpret_cast(result); +} + +const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListExpr *self) { + auto casted = reinterpret_cast(self); + const clang::Expr *result = casted->getArrayFiller(); + return reinterpret_cast(result); +} + +const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self) { + auto casted = reinterpret_cast(self); + const clang::FieldDecl *result = casted->getInitializedFieldInUnion(); + return reinterpret_cast(result); +} + +unsigned ZigClangInitListExpr_getNumInits(const ZigClangInitListExpr *self) { + auto casted = reinterpret_cast(self); + return casted->getNumInits(); +} + ZigClangAPValueKind ZigClangAPValue_getKind(const ZigClangAPValue *self) { auto casted = reinterpret_cast(self); return (ZigClangAPValueKind)casted->getKind(); @@ -1834,35 +2106,30 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char bool single_file_parse = false; bool for_serialization = false; bool retain_excluded_conditional_blocks = false; - std::unique_ptr *err_unit = new std::unique_ptr(); + std::unique_ptr err_unit; clang::ASTUnit *ast_unit = clang::ASTUnit::LoadFromCommandLine( args_begin, args_end, pch_container_ops, diags, resources_path, only_local_decls, clang::CaptureDiagsKind::All, clang::None, true, 0, clang::TU_Complete, false, false, allow_pch_with_compiler_errors, clang::SkipFunctionBodiesScope::None, single_file_parse, user_files_are_volatile, for_serialization, retain_excluded_conditional_blocks, - clang::None, err_unit, nullptr); + clang::None, &err_unit, nullptr); + + *errors_len = 0; // Early failures in LoadFromCommandLine may return with ErrUnit unset. if (!ast_unit && !err_unit) { return nullptr; } - if (diags->getClient()->getNumErrors() > 0) { - if (ast_unit) { - *err_unit = std::unique_ptr(ast_unit); - } + if (diags->hasErrorOccurred()) { + // Take ownership of the err_unit ASTUnit object so that it won't be + // free'd when we return, invalidating the error message pointers + clang::ASTUnit *unit = ast_unit ? ast_unit : err_unit.release(); + ZigList errors = {}; - size_t cap = 4; - *errors_len = 0; - *errors_ptr = reinterpret_cast(malloc(cap * sizeof(Stage2ErrorMsg))); - if (*errors_ptr == nullptr) { - return nullptr; - } - - for (clang::ASTUnit::stored_diag_iterator it = (*err_unit)->stored_diag_begin(), - it_end = (*err_unit)->stored_diag_end(); - it != it_end; ++it) + for (clang::ASTUnit::stored_diag_iterator it = unit->stored_diag_begin(), + it_end = unit->stored_diag_end(); it != it_end; ++it) { switch (it->getLevel()) { case clang::DiagnosticsEngine::Ignored: @@ -1874,50 +2141,49 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char case clang::DiagnosticsEngine::Fatal: break; } + llvm::StringRef msg_str_ref = it->getMessage(); - if (*errors_len >= cap) { - cap *= 2; - Stage2ErrorMsg *new_errors = reinterpret_cast( - realloc(*errors_ptr, cap * sizeof(Stage2ErrorMsg))); - if (new_errors == nullptr) { - free(*errors_ptr); - *errors_ptr = nullptr; - *errors_len = 0; - return nullptr; - } - *errors_ptr = new_errors; - } - Stage2ErrorMsg *msg = *errors_ptr + *errors_len; - *errors_len += 1; + + Stage2ErrorMsg *msg = errors.add_one(); + memset(msg, 0, sizeof(*msg)); + msg->msg_ptr = (const char *)msg_str_ref.bytes_begin(); msg->msg_len = msg_str_ref.size(); clang::FullSourceLoc fsl = it->getLocation(); + // Expand the location if possible + fsl = fsl.getFileLoc(); + + // The only known way to obtain a Loc without a manager associated + // to it is if you have a lot of errors clang emits "too many errors + // emitted, stopping now" if (fsl.hasManager()) { - clang::FileID file_id = fsl.getFileID(); - clang::StringRef filename = fsl.getManager().getFilename(fsl); - if (filename.empty()) { - msg->filename_ptr = nullptr; - } else { + const clang::SourceManager &SM = fsl.getManager(); + + clang::PresumedLoc presumed_loc = SM.getPresumedLoc(fsl); + assert(!presumed_loc.isInvalid()); + + msg->line = presumed_loc.getLine() - 1; + msg->column = presumed_loc.getColumn() - 1; + + clang::StringRef filename = presumed_loc.getFilename(); + if (!filename.empty()) { msg->filename_ptr = (const char *)filename.bytes_begin(); msg->filename_len = filename.size(); } - msg->source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin(); - msg->line = fsl.getSpellingLineNumber() - 1; - msg->column = fsl.getSpellingColumnNumber() - 1; - msg->offset = fsl.getManager().getFileOffset(fsl); - } else { - // The only known way this gets triggered right now is if you have a lot of errors - // clang emits "too many errors emitted, stopping now" - msg->filename_ptr = nullptr; - msg->source = nullptr; + + bool invalid; + clang::StringRef buffer = fsl.getBufferData(&invalid); + + if (!invalid) { + msg->source = (const char *)buffer.bytes_begin(); + msg->offset = SM.getFileOffset(fsl); + } } } - if (*errors_len == 0) { - free(*errors_ptr); - *errors_ptr = nullptr; - } + *errors_ptr = errors.items; + *errors_len = errors.length; return nullptr; } @@ -1938,6 +2204,11 @@ struct ZigClangQualType ZigClangVarDecl_getType(const struct ZigClangVarDecl *se return bitcast(casted->getType()); } +struct ZigClangQualType ZigClangVarDecl_getTypeSourceInfo_getType(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getTypeSourceInfo()->getType()); +} + const struct ZigClangExpr *ZigClangVarDecl_getInit(const struct ZigClangVarDecl *self) { auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getInit()); @@ -1948,6 +2219,37 @@ enum ZigClangVarDecl_TLSKind ZigClangVarDecl_getTLSKind(const ZigClangVarDecl *s return (ZigClangVarDecl_TLSKind)casted->getTLSKind(); } +struct ZigClangSourceLocation ZigClangVarDecl_getLocation(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getLocation()); +} + +bool ZigClangVarDecl_hasExternalStorage(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return casted->hasExternalStorage(); +} + +bool ZigClangVarDecl_isFileVarDecl(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isFileVarDecl(); +} + +bool ZigClangVarDecl_hasInit(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return casted->hasInit(); +} + +const ZigClangAPValue * ZigClangVarDecl_evaluateValue(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + const clang::APValue *result = casted->evaluateValue(); + return reinterpret_cast(result); +} + +enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangVarDecl *self) { + auto casted = reinterpret_cast(self); + return (ZigClangStorageClass)casted->getStorageClass(); +} + enum ZigClangBuiltinTypeKind ZigClangBuiltinType_getKind(const struct ZigClangBuiltinType *self) { auto casted = reinterpret_cast(self); return (ZigClangBuiltinTypeKind)casted->getKind(); @@ -2022,6 +2324,11 @@ unsigned ZigClangAPFloat_convertToHexString(const ZigClangAPFloat *self, char *D return casted->convertToHexString(DST, HexDigits, UpperCase, (llvm::APFloat::roundingMode)RM); } +double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self) { + auto casted = reinterpret_cast(self); + return casted->getValueAsApproximateDouble(); +} + enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) { auto casted = reinterpret_cast(self); return (ZigClangStringLiteral_StringKind)casted->getKind(); @@ -2097,6 +2404,11 @@ struct ZigClangQualType ZigClangAttributedType_getEquivalentType(const struct Zi return bitcast(casted->getEquivalentType()); } +struct ZigClangQualType ZigClangMacroQualifiedType_getModifiedType(const struct ZigClangMacroQualifiedType *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getModifiedType()); +} + struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *self) { auto casted = reinterpret_cast(self); return bitcast(casted->getNamedType()); @@ -2193,18 +2505,18 @@ unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral return casted->getValue(); } -const struct ZigClangExpr *ZigClangConditionalOperator_getCond(const struct ZigClangConditionalOperator *self) { - auto casted = reinterpret_cast(self); +const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *self) { + auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getCond()); } -const struct ZigClangExpr *ZigClangConditionalOperator_getTrueExpr(const struct ZigClangConditionalOperator *self) { - auto casted = reinterpret_cast(self); +const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getTrueExpr(const struct ZigClangAbstractConditionalOperator *self) { + auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getTrueExpr()); } -const struct ZigClangExpr *ZigClangConditionalOperator_getFalseExpr(const struct ZigClangConditionalOperator *self) { - auto casted = reinterpret_cast(self); +const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getFalseExpr(const struct ZigClangAbstractConditionalOperator *self) { + auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getFalseExpr()); } @@ -2423,3 +2735,126 @@ const struct ZigClangExpr *ZigClangParenExpr_getSubExpr(const struct ZigClangPar auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getSubExpr()); } + +enum ZigClangPreprocessedEntity_EntityKind ZigClangPreprocessedEntity_getKind( + const struct ZigClangPreprocessedEntity *self) +{ + auto casted = reinterpret_cast(self); + return (ZigClangPreprocessedEntity_EntityKind)casted->getKind(); +} + +const char *ZigClangMacroDefinitionRecord_getName_getNameStart(const struct ZigClangMacroDefinitionRecord *self) { + auto casted = reinterpret_cast(self); + return casted->getName()->getNameStart(); +} + +struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getBegin(const struct ZigClangMacroDefinitionRecord *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getSourceRange().getBegin()); +} + +struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getSourceRange().getEnd()); +} + +ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_begin(const struct ZigClangRecordDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->field_begin()); +} + +ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_end(const struct ZigClangRecordDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->field_end()); +} + +bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *self) { + auto casted = reinterpret_cast(self); + return casted->isBitField(); +} + +bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *field_decl) { + return reinterpret_cast(field_decl)->isAnonymousStructOrUnion(); +} + +ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getLocation()); +} + +ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getType()); +} + +ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_iterator_next( + struct ZigClangRecordDecl_field_iterator self) +{ + clang::RecordDecl::field_iterator casted = bitcast(self); + ++casted; + return bitcast(casted); +} + +const struct ZigClangFieldDecl * ZigClangRecordDecl_field_iterator_deref( + struct ZigClangRecordDecl_field_iterator self) +{ + clang::RecordDecl::field_iterator casted = bitcast(self); + const clang::FieldDecl *result = *casted; + return reinterpret_cast(result); +} + +bool ZigClangRecordDecl_field_iterator_neq( + struct ZigClangRecordDecl_field_iterator a, + struct ZigClangRecordDecl_field_iterator b) +{ + clang::RecordDecl::field_iterator casted_a = bitcast(a); + clang::RecordDecl::field_iterator casted_b = bitcast(b); + return casted_a != casted_b; +} + +ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_begin(const struct ZigClangEnumDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->enumerator_begin()); +} + +ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_end(const struct ZigClangEnumDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->enumerator_end()); +} + +ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_iterator_next( + struct ZigClangEnumDecl_enumerator_iterator self) +{ + clang::EnumDecl::enumerator_iterator casted = bitcast(self); + ++casted; + return bitcast(casted); +} + +const struct ZigClangEnumConstantDecl * ZigClangEnumDecl_enumerator_iterator_deref( + struct ZigClangEnumDecl_enumerator_iterator self) +{ + clang::EnumDecl::enumerator_iterator casted = bitcast(self); + const clang::EnumConstantDecl *result = *casted; + return reinterpret_cast(result); +} + +bool ZigClangEnumDecl_enumerator_iterator_neq( + struct ZigClangEnumDecl_enumerator_iterator a, + struct ZigClangEnumDecl_enumerator_iterator b) +{ + clang::EnumDecl::enumerator_iterator casted_a = bitcast(a); + clang::EnumDecl::enumerator_iterator casted_b = bitcast(b); + return casted_a != casted_b; +} + +const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *self) { + auto casted = reinterpret_cast(self); + const clang::Expr *result = casted->getInitExpr(); + return reinterpret_cast(result); +} + +const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *self) { + auto casted = reinterpret_cast(self); + const llvm::APSInt *result = &casted->getInitVal(); + return reinterpret_cast(result); +} diff --git a/src/zig_clang.h b/src/zig_clang.h index 8573c930f..494a6d6e0 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -112,9 +112,11 @@ struct ZigClangImplicitCastExpr; struct ZigClangIncompleteArrayType; struct ZigClangIntegerLiteral; struct ZigClangMacroDefinitionRecord; +struct ZigClangMacroQualifiedType; struct ZigClangMemberExpr; struct ZigClangNamedDecl; struct ZigClangNone; +struct ZigClangOpaqueValueExpr; struct ZigClangPCHContainerOperations; struct ZigClangParenExpr; struct ZigClangParenType; @@ -122,6 +124,7 @@ struct ZigClangParmVarDecl; struct ZigClangPointerType; struct ZigClangPredefinedExpr; struct ZigClangPreprocessedEntity; +struct ZigClangPreprocessingRecord; struct ZigClangRecordDecl; struct ZigClangRecordType; struct ZigClangReturnStmt; @@ -142,10 +145,24 @@ struct ZigClangUnaryOperator; struct ZigClangValueDecl; struct ZigClangVarDecl; struct ZigClangWhileStmt; +struct ZigClangInitListExpr; typedef struct ZigClangStmt *const * ZigClangCompoundStmt_const_body_iterator; typedef struct ZigClangDecl *const * ZigClangDeclStmt_const_decl_iterator; +struct ZigClangRecordDecl_field_iterator { + void *opaque; +}; + +struct ZigClangEnumDecl_enumerator_iterator { + void *opaque; +}; + +struct ZigClangPreprocessingRecord_iterator { + int I; + struct ZigClangPreprocessingRecord *Self; +}; + enum ZigClangBO { ZigClangBO_PtrMemD, ZigClangBO_PtrMemI, @@ -803,6 +820,18 @@ enum ZigClangElaboratedTypeKeyword { ZigClangETK_None, }; +enum ZigClangPreprocessedEntity_EntityKind { + ZigClangPreprocessedEntity_InvalidKind, + ZigClangPreprocessedEntity_MacroExpansionKind, + ZigClangPreprocessedEntity_MacroDefinitionKind, + ZigClangPreprocessedEntity_InclusionDirectiveKind, +}; + +enum ZigClangExpr_ConstExprUsage { + ZigClangExpr_EvaluateForCodeGen, + ZigClangExpr_EvaluateForMangling, +}; + ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *, struct ZigClangSourceLocation Loc); ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *, @@ -827,14 +856,30 @@ ZIG_EXTERN_C struct ZigClangASTContext *ZigClangASTUnit_getASTContext(struct Zig ZIG_EXTERN_C struct ZigClangSourceManager *ZigClangASTUnit_getSourceManager(struct ZigClangASTUnit *); ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(struct ZigClangASTUnit *, void *context, bool (*Fn)(void *context, const struct ZigClangDecl *decl)); +ZIG_EXTERN_C struct ZigClangPreprocessingRecord_iterator ZigClangASTUnit_getLocalPreprocessingEntities_begin(struct ZigClangASTUnit *); +ZIG_EXTERN_C struct ZigClangPreprocessingRecord_iterator ZigClangASTUnit_getLocalPreprocessingEntities_end(struct ZigClangASTUnit *); + +ZIG_EXTERN_C struct ZigClangPreprocessedEntity *ZigClangPreprocessingRecord_iterator_deref( + struct ZigClangPreprocessingRecord_iterator); + +ZIG_EXTERN_C enum ZigClangPreprocessedEntity_EntityKind ZigClangPreprocessedEntity_getKind(const struct ZigClangPreprocessedEntity *); ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordType_getDecl(const struct ZigClangRecordType *record_ty); ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struct ZigClangEnumType *record_ty); +ZIG_EXTERN_C bool ZigClangTagDecl_isThisDeclarationADefinition(const struct ZigClangTagDecl *); + ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *); +ZIG_EXTERN_C const struct ZigClangFieldDecl *ZigClangFieldDecl_getCanonicalDecl(const ZigClangFieldDecl *); ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *); +ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self); +ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self); +ZIG_EXTERN_C const char* ZigClangVarDecl_getSectionAttribute(const struct ZigClangVarDecl *self, size_t *len); +ZIG_EXTERN_C unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx); +ZIG_EXTERN_C unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx); +ZIG_EXTERN_C bool ZigClangRecordDecl_getPackedAttribute(const struct ZigClangRecordDecl *); ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *); ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *); @@ -849,20 +894,49 @@ ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl ZIG_EXTERN_C enum ZigClangStorageClass ZigClangFunctionDecl_getStorageClass(const struct ZigClangFunctionDecl *); ZIG_EXTERN_C const struct ZigClangParmVarDecl *ZigClangFunctionDecl_getParamDecl(const struct ZigClangFunctionDecl *, unsigned i); ZIG_EXTERN_C const struct ZigClangStmt *ZigClangFunctionDecl_getBody(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefinition(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_isThisDeclarationADefinition(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_doesThisDeclarationHaveABody(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_isInlineSpecified(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_isDefined(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C const struct ZigClangFunctionDecl* ZigClangFunctionDecl_getDefinition(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C const char* ZigClangFunctionDecl_getSectionAttribute(const struct ZigClangFunctionDecl *, size_t *); ZIG_EXTERN_C bool ZigClangRecordDecl_isUnion(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C bool ZigClangRecordDecl_isStruct(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C bool ZigClangRecordDecl_isAnonymousStructOrUnion(const struct ZigClangRecordDecl *record_decl); +ZIG_EXTERN_C ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_begin(const struct ZigClangRecordDecl *); +ZIG_EXTERN_C ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_end(const struct ZigClangRecordDecl *); +ZIG_EXTERN_C ZigClangRecordDecl_field_iterator ZigClangRecordDecl_field_iterator_next(struct ZigClangRecordDecl_field_iterator); +ZIG_EXTERN_C const struct ZigClangFieldDecl * ZigClangRecordDecl_field_iterator_deref(struct ZigClangRecordDecl_field_iterator); +ZIG_EXTERN_C bool ZigClangRecordDecl_field_iterator_neq( + struct ZigClangRecordDecl_field_iterator a, + struct ZigClangRecordDecl_field_iterator b); ZIG_EXTERN_C struct ZigClangQualType ZigClangEnumDecl_getIntegerType(const struct ZigClangEnumDecl *); +ZIG_EXTERN_C ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_begin(const struct ZigClangEnumDecl *); +ZIG_EXTERN_C ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_end(const struct ZigClangEnumDecl *); +ZIG_EXTERN_C ZigClangEnumDecl_enumerator_iterator ZigClangEnumDecl_enumerator_iterator_next(struct ZigClangEnumDecl_enumerator_iterator); +ZIG_EXTERN_C const struct ZigClangEnumConstantDecl * ZigClangEnumDecl_enumerator_iterator_deref(struct ZigClangEnumDecl_enumerator_iterator); +ZIG_EXTERN_C bool ZigClangEnumDecl_enumerator_iterator_neq( + struct ZigClangEnumDecl_enumerator_iterator a, + struct ZigClangEnumDecl_enumerator_iterator b); -ZIG_EXTERN_C const char *ZigClangDecl_getName_bytes_begin(const struct ZigClangDecl *decl); +ZIG_EXTERN_C const ZigClangNamedDecl* ZigClangDecl_castToNamedDecl(const ZigClangDecl *self); +ZIG_EXTERN_C const char *ZigClangNamedDecl_getName_bytes_begin(const struct ZigClangNamedDecl *self); ZIG_EXTERN_C enum ZigClangDeclKind ZigClangDecl_getKind(const struct ZigClangDecl *decl); ZIG_EXTERN_C const char *ZigClangDecl_getDeclKindName(const struct ZigClangDecl *decl); ZIG_EXTERN_C struct ZigClangQualType ZigClangVarDecl_getType(const struct ZigClangVarDecl *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangVarDecl_getInit(const struct ZigClangVarDecl *var_decl); ZIG_EXTERN_C enum ZigClangVarDecl_TLSKind ZigClangVarDecl_getTLSKind(const struct ZigClangVarDecl *var_decl); +ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangVarDecl_getLocation(const struct ZigClangVarDecl *); +ZIG_EXTERN_C bool ZigClangVarDecl_hasExternalStorage(const struct ZigClangVarDecl *); +ZIG_EXTERN_C bool ZigClangVarDecl_isFileVarDecl(const struct ZigClangVarDecl *); +ZIG_EXTERN_C bool ZigClangVarDecl_hasInit(const struct ZigClangVarDecl *); +ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangVarDecl_evaluateValue(const struct ZigClangVarDecl *); +ZIG_EXTERN_C struct ZigClangQualType ZigClangVarDecl_getTypeSourceInfo_getType(const struct ZigClangVarDecl *); +ZIG_EXTERN_C enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangVarDecl *self); ZIG_EXTERN_C bool ZigClangSourceLocation_eq(struct ZigClangSourceLocation a, struct ZigClangSourceLocation b); @@ -880,8 +954,14 @@ ZIG_EXTERN_C bool ZigClangQualType_isRestrictQualified(struct ZigClangQualType); ZIG_EXTERN_C enum ZigClangTypeClass ZigClangType_getTypeClass(const struct ZigClangType *self); ZIG_EXTERN_C struct ZigClangQualType ZigClangType_getPointeeType(const struct ZigClangType *self); +ZIG_EXTERN_C bool ZigClangType_isBooleanType(const struct ZigClangType *self); ZIG_EXTERN_C bool ZigClangType_isVoidType(const struct ZigClangType *self); +ZIG_EXTERN_C bool ZigClangType_isArrayType(const struct ZigClangType *self); +ZIG_EXTERN_C bool ZigClangType_isRecordType(const struct ZigClangType *self); ZIG_EXTERN_C const char *ZigClangType_getTypeClassName(const struct ZigClangType *self); +ZIG_EXTERN_C const struct ZigClangArrayType *ZigClangType_getAsArrayTypeUnsafe(const struct ZigClangType *self); +ZIG_EXTERN_C const ZigClangRecordType *ZigClangType_getAsRecordType(const ZigClangType *self); +ZIG_EXTERN_C const ZigClangRecordType *ZigClangType_getAsUnionType(const ZigClangType *self); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangStmt_getBeginLoc(const struct ZigClangStmt *self); ZIG_EXTERN_C enum ZigClangStmtClass ZigClangStmt_getStmtClass(const struct ZigClangStmt *self); @@ -892,6 +972,15 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangExpr_getType(const struct ZigClangE ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangExpr_getBeginLoc(const struct ZigClangExpr *self); ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsBooleanCondition(const struct ZigClangExpr *self, bool *result, const struct ZigClangASTContext *ctx, bool in_constant_context); +ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsFloat(const struct ZigClangExpr *self, + ZigClangAPFloat **result, const struct ZigClangASTContext *ctx); +ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsConstantExpr(const struct ZigClangExpr *, + struct ZigClangExprEvalResult *, ZigClangExpr_ConstExprUsage, const struct ZigClangASTContext *); + +ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *, unsigned); +ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListExpr *); +ZIG_EXTERN_C unsigned ZigClangInitListExpr_getNumInits(const ZigClangInitListExpr *); +ZIG_EXTERN_C const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self); ZIG_EXTERN_C enum ZigClangAPValueKind ZigClangAPValue_getKind(const struct ZigClangAPValue *self); ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPValue_getInt(const struct ZigClangAPValue *self); @@ -933,6 +1022,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST, unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM); +ZIG_EXTERN_C double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self); ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self); ZIG_EXTERN_C const char *ZigClangStringLiteral_getString_bytes_begin_size(const struct ZigClangStringLiteral *self, @@ -959,6 +1049,8 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangParenType_getInnerType(const struct ZIG_EXTERN_C struct ZigClangQualType ZigClangAttributedType_getEquivalentType(const struct ZigClangAttributedType *); +ZIG_EXTERN_C struct ZigClangQualType ZigClangMacroQualifiedType_getModifiedType(const struct ZigClangMacroQualifiedType *); + ZIG_EXTERN_C struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *); ZIG_EXTERN_C enum ZigClangElaboratedTypeKeyword ZigClangElaboratedType_getKeyword(const struct ZigClangElaboratedType *); @@ -985,9 +1077,9 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangCharacterLiteral_getBeginLoc( ZIG_EXTERN_C enum ZigClangCharacterLiteral_CharacterKind ZigClangCharacterLiteral_getKind(const struct ZigClangCharacterLiteral *); ZIG_EXTERN_C unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral *); -ZIG_EXTERN_C const struct ZigClangExpr *ZigClangConditionalOperator_getCond(const struct ZigClangConditionalOperator *); -ZIG_EXTERN_C const struct ZigClangExpr *ZigClangConditionalOperator_getTrueExpr(const struct ZigClangConditionalOperator *); -ZIG_EXTERN_C const struct ZigClangExpr *ZigClangConditionalOperator_getFalseExpr(const struct ZigClangConditionalOperator *); +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *); +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getTrueExpr(const struct ZigClangAbstractConditionalOperator *); +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getFalseExpr(const struct ZigClangAbstractConditionalOperator *); ZIG_EXTERN_C struct ZigClangQualType ZigClangCompoundAssignOperator_getType(const struct ZigClangCompoundAssignOperator *); ZIG_EXTERN_C struct ZigClangQualType ZigClangCompoundAssignOperator_getComputationLHSType(const struct ZigClangCompoundAssignOperator *); @@ -1017,6 +1109,8 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangMemberExpr_getBase(const struct ZIG_EXTERN_C bool ZigClangMemberExpr_isArrow(const struct ZigClangMemberExpr *); ZIG_EXTERN_C const struct ZigClangValueDecl * ZigClangMemberExpr_getMemberDecl(const struct ZigClangMemberExpr *); +ZIG_EXTERN_C const ZigClangExpr *ZigClangOpaqueValueExpr_getSourceExpr(const struct ZigClangOpaqueValueExpr *); + ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getBase(const struct ZigClangArraySubscriptExpr *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getIdx(const struct ZigClangArraySubscriptExpr *); @@ -1044,4 +1138,16 @@ ZIG_EXTERN_C const struct ZigClangStmt *ZigClangCaseStmt_getSubStmt(const struct ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDefaultStmt_getSubStmt(const struct ZigClangDefaultStmt *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangParenExpr_getSubExpr(const struct ZigClangParenExpr *); + +ZIG_EXTERN_C const char *ZigClangMacroDefinitionRecord_getName_getNameStart(const struct ZigClangMacroDefinitionRecord *); +ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getBegin(const struct ZigClangMacroDefinitionRecord *); +ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *); + +ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *); +ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *); +ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *); +ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *); + +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *); +ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *); #endif diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 4047d906d..de989c244 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -153,6 +156,10 @@ unsigned ZigLLVMDataLayoutGetStackAlignment(LLVMTargetDataRef TD) { return unwrap(TD)->getStackAlignment().value(); } +unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) { + return unwrap(TD)->getProgramAddressSpace(); +} + bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small, bool time_report) @@ -266,19 +273,25 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) { } LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name) + unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name) { CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name); - call_inst->setCallingConv(CC); - switch (fn_inline) { - case ZigLLVM_FnInlineAuto: + call_inst->setCallingConv(static_cast(CC)); + switch (attr) { + case ZigLLVM_CallAttrAuto: break; - case ZigLLVM_FnInlineAlways: - call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline); + case ZigLLVM_CallAttrNeverTail: + call_inst->setTailCallKind(CallInst::TCK_NoTail); break; - case ZigLLVM_FnInlineNever: + case ZigLLVM_CallAttrNeverInline: call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline); break; + case ZigLLVM_CallAttrAlwaysTail: + call_inst->setTailCallKind(CallInst::TCK_MustTail); + break; + case ZigLLVM_CallAttrAlwaysInline: + call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline); + break; } return wrap(unwrap(B)->Insert(call_inst)); } @@ -919,6 +932,9 @@ void ZigLLVMFunctionSetPrefixData(LLVMValueRef function, LLVMValueRef data) { unwrap(function)->setPrefixData(unwrap(data)); } +void ZigLLVMFunctionSetCallingConv(LLVMValueRef function, ZigLLVM_CallingConv cc) { + unwrap(function)->setCallingConv(static_cast(cc)); +} class MyOStream: public raw_ostream { public: @@ -938,6 +954,85 @@ class MyOStream: public raw_ostream { size_t pos; }; +bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch, + const char *output_lib_path, const bool kill_at) +{ + COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; + + switch (arch) { + case ZigLLVM_x86: + machine = COFF::IMAGE_FILE_MACHINE_I386; + break; + case ZigLLVM_x86_64: + machine = COFF::IMAGE_FILE_MACHINE_AMD64; + break; + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + machine = COFF::IMAGE_FILE_MACHINE_ARMNT; + break; + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + machine = COFF::IMAGE_FILE_MACHINE_ARM64; + break; + default: + break; + } + + if (machine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + return true; + } + + auto bufOrErr = MemoryBuffer::getFile(def_path); + if (!bufOrErr) { + return false; + } + + MemoryBuffer& buf = *bufOrErr.get(); + Expected def = + object::parseCOFFModuleDefinition(buf, machine, /* MingwDef */ true); + + if (!def) { + return true; + } + + // The exports-juggling code below is ripped from LLVM's DllToolDriver.cpp + + // If ExtName is set (if the "ExtName = Name" syntax was used), overwrite + // Name with ExtName and clear ExtName. When only creating an import + // library and not linking, the internal name is irrelevant. This avoids + // cases where writeImportLibrary tries to transplant decoration from + // symbol decoration onto ExtName. + for (object::COFFShortExport& E : def->Exports) { + if (!E.ExtName.empty()) { + E.Name = E.ExtName; + E.ExtName.clear(); + } + } + + if (machine == COFF::IMAGE_FILE_MACHINE_I386 && kill_at) { + for (object::COFFShortExport& E : def->Exports) { + if (!E.AliasTarget.empty() || (!E.Name.empty() && E.Name[0] == '?')) + continue; + E.SymbolName = E.Name; + // Trim off the trailing decoration. Symbols will always have a + // starting prefix here (either _ for cdecl/stdcall, @ for fastcall + // or ? for C++ functions). Vectorcall functions won't have any + // fixed prefix, but the function base name will still be at least + // one char. + E.Name = E.Name.substr(0, E.Name.find('@', 1)); + // By making sure E.SymbolName != E.Name for decorated symbols, + // writeImportLibrary writes these symbols with the type + // IMPORT_NAME_UNDECORATE. + } + } + + return static_cast( + object::writeImportLibrary(def->OutputFile, output_lib_path, + def->Exports, machine, /* MinGW */ true)); +} + bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, ZigLLVM_OSType os_type) { @@ -1004,6 +1099,56 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ abort(); } +static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { + switch (BinOp) { + default: + case ZigLLVMAtomicRMWBinOpXchg: return AtomicRMWInst::Xchg; + case ZigLLVMAtomicRMWBinOpAdd: return AtomicRMWInst::Add; + case ZigLLVMAtomicRMWBinOpSub: return AtomicRMWInst::Sub; + case ZigLLVMAtomicRMWBinOpAnd: return AtomicRMWInst::And; + case ZigLLVMAtomicRMWBinOpNand: return AtomicRMWInst::Nand; + case ZigLLVMAtomicRMWBinOpOr: return AtomicRMWInst::Or; + case ZigLLVMAtomicRMWBinOpXor: return AtomicRMWInst::Xor; + case ZigLLVMAtomicRMWBinOpMax: return AtomicRMWInst::Max; + case ZigLLVMAtomicRMWBinOpMin: return AtomicRMWInst::Min; + case ZigLLVMAtomicRMWBinOpUMax: return AtomicRMWInst::UMax; + case ZigLLVMAtomicRMWBinOpUMin: return AtomicRMWInst::UMin; + case ZigLLVMAtomicRMWBinOpFAdd: return AtomicRMWInst::FAdd; + case ZigLLVMAtomicRMWBinOpFSub: return AtomicRMWInst::FSub; + } +} + +static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) { + switch (Ordering) { + default: + case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic; + case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered; + case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic; + case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire; + case LLVMAtomicOrderingRelease: return AtomicOrdering::Release; + case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease; + case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent; + } +} + +inline LLVMAttributeRef wrap(Attribute Attr) { + return reinterpret_cast(Attr.getRawPointer()); +} + +inline Attribute unwrap(LLVMAttributeRef Attr) { + return Attribute::fromRawPointer(Attr); +} + +LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, + LLVMValueRef PTR, LLVMValueRef Val, + LLVMAtomicOrdering ordering, LLVMBool singleThread) +{ + AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op); + return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR), + unwrap(Val), toLLVMOrdering(ordering), + singleThread ? SyncScope::SingleThread : SyncScope::System)); +} + static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); @@ -1173,3 +1318,49 @@ static_assert((Triple::ObjectFormatType)ZigLLVM_ELF == Triple::ELF, ""); static_assert((Triple::ObjectFormatType)ZigLLVM_MachO == Triple::MachO, ""); static_assert((Triple::ObjectFormatType)ZigLLVM_Wasm == Triple::Wasm, ""); static_assert((Triple::ObjectFormatType)ZigLLVM_XCOFF == Triple::XCOFF, ""); + +static_assert((CallingConv::ID)ZigLLVM_C == llvm::CallingConv::C, ""); +static_assert((CallingConv::ID)ZigLLVM_Fast == llvm::CallingConv::Fast, ""); +static_assert((CallingConv::ID)ZigLLVM_Cold == llvm::CallingConv::Cold, ""); +static_assert((CallingConv::ID)ZigLLVM_GHC == llvm::CallingConv::GHC, ""); +static_assert((CallingConv::ID)ZigLLVM_HiPE == llvm::CallingConv::HiPE, ""); +static_assert((CallingConv::ID)ZigLLVM_WebKit_JS == llvm::CallingConv::WebKit_JS, ""); +static_assert((CallingConv::ID)ZigLLVM_AnyReg == llvm::CallingConv::AnyReg, ""); +static_assert((CallingConv::ID)ZigLLVM_PreserveMost == llvm::CallingConv::PreserveMost, ""); +static_assert((CallingConv::ID)ZigLLVM_PreserveAll == llvm::CallingConv::PreserveAll, ""); +static_assert((CallingConv::ID)ZigLLVM_Swift == llvm::CallingConv::Swift, ""); +static_assert((CallingConv::ID)ZigLLVM_CXX_FAST_TLS == llvm::CallingConv::CXX_FAST_TLS, ""); +static_assert((CallingConv::ID)ZigLLVM_FirstTargetCC == llvm::CallingConv::FirstTargetCC, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_StdCall == llvm::CallingConv::X86_StdCall, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_FastCall == llvm::CallingConv::X86_FastCall, ""); +static_assert((CallingConv::ID)ZigLLVM_ARM_APCS == llvm::CallingConv::ARM_APCS, ""); +static_assert((CallingConv::ID)ZigLLVM_ARM_AAPCS == llvm::CallingConv::ARM_AAPCS, ""); +static_assert((CallingConv::ID)ZigLLVM_ARM_AAPCS_VFP == llvm::CallingConv::ARM_AAPCS_VFP, ""); +static_assert((CallingConv::ID)ZigLLVM_MSP430_INTR == llvm::CallingConv::MSP430_INTR, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_ThisCall == llvm::CallingConv::X86_ThisCall, ""); +static_assert((CallingConv::ID)ZigLLVM_PTX_Kernel == llvm::CallingConv::PTX_Kernel, ""); +static_assert((CallingConv::ID)ZigLLVM_PTX_Device == llvm::CallingConv::PTX_Device, ""); +static_assert((CallingConv::ID)ZigLLVM_SPIR_FUNC == llvm::CallingConv::SPIR_FUNC, ""); +static_assert((CallingConv::ID)ZigLLVM_SPIR_KERNEL == llvm::CallingConv::SPIR_KERNEL, ""); +static_assert((CallingConv::ID)ZigLLVM_Intel_OCL_BI == llvm::CallingConv::Intel_OCL_BI, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_64_SysV == llvm::CallingConv::X86_64_SysV, ""); +static_assert((CallingConv::ID)ZigLLVM_Win64 == llvm::CallingConv::Win64, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_VectorCall == llvm::CallingConv::X86_VectorCall, ""); +static_assert((CallingConv::ID)ZigLLVM_HHVM == llvm::CallingConv::HHVM, ""); +static_assert((CallingConv::ID)ZigLLVM_HHVM_C == llvm::CallingConv::HHVM_C, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_INTR == llvm::CallingConv::X86_INTR, ""); +static_assert((CallingConv::ID)ZigLLVM_AVR_INTR == llvm::CallingConv::AVR_INTR, ""); +static_assert((CallingConv::ID)ZigLLVM_AVR_SIGNAL == llvm::CallingConv::AVR_SIGNAL, ""); +static_assert((CallingConv::ID)ZigLLVM_AVR_BUILTIN == llvm::CallingConv::AVR_BUILTIN, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_VS == llvm::CallingConv::AMDGPU_VS, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_GS == llvm::CallingConv::AMDGPU_GS, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_PS == llvm::CallingConv::AMDGPU_PS, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_CS == llvm::CallingConv::AMDGPU_CS, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_KERNEL == llvm::CallingConv::AMDGPU_KERNEL, ""); +static_assert((CallingConv::ID)ZigLLVM_X86_RegCall == llvm::CallingConv::X86_RegCall, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_HS == llvm::CallingConv::AMDGPU_HS, ""); +static_assert((CallingConv::ID)ZigLLVM_MSP430_BUILTIN == llvm::CallingConv::MSP430_BUILTIN, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_LS == llvm::CallingConv::AMDGPU_LS, ""); +static_assert((CallingConv::ID)ZigLLVM_AMDGPU_ES == llvm::CallingConv::AMDGPU_ES, ""); +static_assert((CallingConv::ID)ZigLLVM_AArch64_VectorCall == llvm::CallingConv::AArch64_VectorCall, ""); +static_assert((CallingConv::ID)ZigLLVM_MaxID == llvm::CallingConv::MaxID, ""); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 8522a03c4..ba9816b4f 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -64,13 +64,63 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); -enum ZigLLVM_FnInline { - ZigLLVM_FnInlineAuto, - ZigLLVM_FnInlineAlways, - ZigLLVM_FnInlineNever, +enum ZigLLVM_CallingConv { + ZigLLVM_C = 0, + ZigLLVM_Fast = 8, + ZigLLVM_Cold = 9, + ZigLLVM_GHC = 10, + ZigLLVM_HiPE = 11, + ZigLLVM_WebKit_JS = 12, + ZigLLVM_AnyReg = 13, + ZigLLVM_PreserveMost = 14, + ZigLLVM_PreserveAll = 15, + ZigLLVM_Swift = 16, + ZigLLVM_CXX_FAST_TLS = 17, + ZigLLVM_FirstTargetCC = 64, + ZigLLVM_X86_StdCall = 64, + ZigLLVM_X86_FastCall = 65, + ZigLLVM_ARM_APCS = 66, + ZigLLVM_ARM_AAPCS = 67, + ZigLLVM_ARM_AAPCS_VFP = 68, + ZigLLVM_MSP430_INTR = 69, + ZigLLVM_X86_ThisCall = 70, + ZigLLVM_PTX_Kernel = 71, + ZigLLVM_PTX_Device = 72, + ZigLLVM_SPIR_FUNC = 75, + ZigLLVM_SPIR_KERNEL = 76, + ZigLLVM_Intel_OCL_BI = 77, + ZigLLVM_X86_64_SysV = 78, + ZigLLVM_Win64 = 79, + ZigLLVM_X86_VectorCall = 80, + ZigLLVM_HHVM = 81, + ZigLLVM_HHVM_C = 82, + ZigLLVM_X86_INTR = 83, + ZigLLVM_AVR_INTR = 84, + ZigLLVM_AVR_SIGNAL = 85, + ZigLLVM_AVR_BUILTIN = 86, + ZigLLVM_AMDGPU_VS = 87, + ZigLLVM_AMDGPU_GS = 88, + ZigLLVM_AMDGPU_PS = 89, + ZigLLVM_AMDGPU_CS = 90, + ZigLLVM_AMDGPU_KERNEL = 91, + ZigLLVM_X86_RegCall = 92, + ZigLLVM_AMDGPU_HS = 93, + ZigLLVM_MSP430_BUILTIN = 94, + ZigLLVM_AMDGPU_LS = 95, + ZigLLVM_AMDGPU_ES = 96, + ZigLLVM_AArch64_VectorCall = 97, + ZigLLVM_MaxID = 1023, +}; + +enum ZigLLVM_CallAttr { + ZigLLVM_CallAttrAuto, + ZigLLVM_CallAttrNeverTail, + ZigLLVM_CallAttrNeverInline, + ZigLLVM_CallAttrAlwaysTail, + ZigLLVM_CallAttrAlwaysInline, }; ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, unsigned CC, enum ZigLLVM_FnInline fn_inline, const char *Name); + unsigned NumArgs, enum ZigLLVM_CallingConv CC, enum ZigLLVM_CallAttr attr, const char *Name); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile); @@ -213,6 +263,7 @@ ZIG_EXTERN_C struct ZigLLVMDILocation *ZigLLVMGetDebugLoc(unsigned line, unsigne ZIG_EXTERN_C void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state); ZIG_EXTERN_C void ZigLLVMSetTailCall(LLVMValueRef Call); ZIG_EXTERN_C void ZigLLVMFunctionSetPrefixData(LLVMValueRef fn, LLVMValueRef data); +ZIG_EXTERN_C void ZigLLVMFunctionSetCallingConv(LLVMValueRef function, enum ZigLLVM_CallingConv cc); ZIG_EXTERN_C void ZigLLVMAddFunctionAttr(LLVMValueRef fn, const char *attr_name, const char *attr_value); ZIG_EXTERN_C void ZigLLVMAddByValAttr(LLVMValueRef fn_ref, unsigned ArgNo, LLVMTypeRef type_val); @@ -420,6 +471,26 @@ enum ZigLLVM_ObjectFormatType { ZigLLVM_XCOFF, }; +enum ZigLLVM_AtomicRMWBinOp { + ZigLLVMAtomicRMWBinOpXchg, + ZigLLVMAtomicRMWBinOpAdd, + ZigLLVMAtomicRMWBinOpSub, + ZigLLVMAtomicRMWBinOpAnd, + ZigLLVMAtomicRMWBinOpNand, + ZigLLVMAtomicRMWBinOpOr, + ZigLLVMAtomicRMWBinOpXor, + ZigLLVMAtomicRMWBinOpMax, + ZigLLVMAtomicRMWBinOpMin, + ZigLLVMAtomicRMWBinOpUMax, + ZigLLVMAtomicRMWBinOpUMin, + ZigLLVMAtomicRMWBinOpFAdd, + ZigLLVMAtomicRMWBinOpFSub, +}; + +LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op, + LLVMValueRef PTR, LLVMValueRef Val, + LLVMAtomicOrdering ordering, LLVMBool singleThread); + #define ZigLLVM_DIFlags_Zero 0U #define ZigLLVM_DIFlags_Private 1U #define ZigLLVM_DIFlags_Protected 2U @@ -465,10 +536,14 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char * ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, enum ZigLLVM_OSType os_type); +bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch, + const char *output_lib_path, const bool kill_at); + ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type, enum ZigLLVM_SubArchType *sub_arch_type, enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type, enum ZigLLVM_ObjectFormatType *oformat); ZIG_EXTERN_C unsigned ZigLLVMDataLayoutGetStackAlignment(LLVMTargetDataRef TD); +ZIG_EXTERN_C unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD); #endif diff --git a/test/cli.zig b/test/cli.zig index 8287c41b8..0527b5c92 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -8,7 +8,7 @@ const ChildProcess = std.ChildProcess; var a: *std.mem.Allocator = undefined; pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); var arg_it = process.args(); @@ -19,16 +19,16 @@ pub fn main() !void { a = &arena.allocator; const zig_exe_rel = try (arg_it.next(a) orelse { - std.debug.warn("Expected first argument to be path to zig compiler\n"); + std.debug.warn("Expected first argument to be path to zig compiler\n", .{}); return error.InvalidArgs; }); const cache_root = try (arg_it.next(a) orelse { - std.debug.warn("Expected second argument to be cache root directory path\n"); + std.debug.warn("Expected second argument to be cache root directory path\n", .{}); return error.InvalidArgs; }); - const zig_exe = try fs.path.resolve(a, [_][]const u8{zig_exe_rel}); + const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel}); - const dir_path = try fs.path.join(a, [_][]const u8{ cache_root, "clitest" }); + const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" }); const TestFn = fn ([]const u8, []const u8) anyerror!void; const test_fns = [_]TestFn{ testZigInitLib, @@ -37,7 +37,7 @@ pub fn main() !void { testMissingOutputPath, }; for (test_fns) |testFn| { - try fs.deleteTree(a, dir_path); + try fs.deleteTree(dir_path); try fs.makeDir(dir_path); try testFn(zig_exe, dir_path); } @@ -45,39 +45,39 @@ pub fn main() !void { fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { return arg catch |err| { - warn("Unable to parse command line: {}\n", err); + warn("Unable to parse command line: {}\n", .{err}); return err; }; } fn printCmd(cwd: []const u8, argv: []const []const u8) void { - std.debug.warn("cd {} && ", cwd); + std.debug.warn("cd {} && ", .{cwd}); for (argv) |arg| { - std.debug.warn("{} ", arg); + std.debug.warn("{} ", .{arg}); } - std.debug.warn("\n"); + std.debug.warn("\n", .{}); } fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { const max_output_size = 100 * 1024; const result = ChildProcess.exec(a, argv, cwd, null, max_output_size) catch |err| { - std.debug.warn("The following command failed:\n"); + std.debug.warn("The following command failed:\n", .{}); printCmd(cwd, argv); return err; }; switch (result.term) { .Exited => |code| { if (code != 0) { - std.debug.warn("The following command exited with error code {}:\n", code); + std.debug.warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", result.stderr); + std.debug.warn("stderr:\n{}\n", .{result.stderr}); return error.CommandFailed; } }, else => { - std.debug.warn("The following command terminated unexpectedly:\n"); + std.debug.warn("The following command terminated unexpectedly:\n", .{}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", result.stderr); + std.debug.warn("stderr:\n{}\n", .{result.stderr}); return error.CommandFailed; }, } @@ -85,22 +85,22 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { } fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" }); - const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" }); - testing.expect(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n")); + _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" }); + const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" }); + testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n")); } fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-exe" }); - const run_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "run" }); + _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); + const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" }); testing.expect(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n")); } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { if (builtin.os != .linux or builtin.arch != .x86_64) return; - const example_zig_path = try fs.path.join(a, [_][]const u8{ dir_path, "example.zig" }); - const example_s_path = try fs.path.join(a, [_][]const u8{ dir_path, "example.s" }); + const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" }); + const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" }); try std.io.writeFile(example_zig_path, \\// Type your code here, or load an example. @@ -123,7 +123,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { "--strip", "--release-fast", example_zig_path, "--disable-gen-h", }; - _ = try exec(dir_path, args); + _ = try exec(dir_path, &args); const out_asm = try std.io.readFileAlloc(a, example_s_path); testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); @@ -132,10 +132,10 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { } fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-exe" }); - const output_path = try fs.path.join(a, [_][]const u8{ "does", "not", "exist" }); - const source_path = try fs.path.join(a, [_][]const u8{ "src", "main.zig" }); - _ = try exec(dir_path, [_][]const u8{ - zig_exe, "build-exe", source_path, "--output-dir", output_path + _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); + const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" }); + const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" }); + _ = try exec(dir_path, &[_][]const u8{ + zig_exe, "build-exe", source_path, "--output-dir", output_path, }); } diff --git a/test/compare_output.zig b/test/compare_output.zig index cbc74c8be..db20709af 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -5,22 +5,26 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", - \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { - \\ _ = c.puts(c"Hello, world!"); + \\const c = @cImport({ + \\ // See https://github.com/ziglang/zig/issues/515 + \\ @cDefine("_NO_CRT_STDIO_INLINE", "1"); + \\ @cInclude("stdio.h"); + \\}); + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\ _ = c.puts("Hello, world!"); \\ return 0; \\} , "Hello, world!" ++ std.cstr.line_sep); cases.addCase(x: { var tc = cases.create("multiple files with private function", - \\use @import("std").io; - \\use @import("foo.zig"); + \\usingnamespace @import("std").io; + \\usingnamespace @import("foo.zig"); \\ \\pub fn main() void { \\ privateFunction(); - \\ const stdout = &(getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("OK 2\n") catch unreachable; + \\ const stdout = &getStdOut().outStream().stream; + \\ stdout.print("OK 2\n", .{}) catch unreachable; \\} \\ \\fn privateFunction() void { @@ -29,13 +33,13 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , "OK 1\nOK 2\n"); tc.addSourceFile("foo.zig", - \\use @import("std").io; + \\usingnamespace @import("std").io; \\ \\// purposefully conflicting function with main.zig \\// but it's private so it should be OK \\fn privateFunction() void { - \\ const stdout = &(getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("OK 1\n") catch unreachable; + \\ const stdout = &getStdOut().outStream().stream; + \\ stdout.print("OK 1\n", .{}) catch unreachable; \\} \\ \\pub fn printText() void { @@ -48,8 +52,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addCase(x: { var tc = cases.create("import segregation", - \\use @import("foo.zig"); - \\use @import("bar.zig"); + \\usingnamespace @import("foo.zig"); + \\usingnamespace @import("bar.zig"); \\ \\pub fn main() void { \\ foo_function(); @@ -58,21 +62,21 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , "OK\nOK\n"); tc.addSourceFile("foo.zig", - \\use @import("std").io; + \\usingnamespace @import("std").io; \\pub fn foo_function() void { - \\ const stdout = &(getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("OK\n") catch unreachable; + \\ const stdout = &getStdOut().outStream().stream; + \\ stdout.print("OK\n", .{}) catch unreachable; \\} ); tc.addSourceFile("bar.zig", - \\use @import("other.zig"); - \\use @import("std").io; + \\usingnamespace @import("other.zig"); + \\usingnamespace @import("std").io; \\ \\pub fn bar_function() void { \\ if (foo_function()) { - \\ const stdout = &(getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("OK\n") catch unreachable; + \\ const stdout = &getStdOut().outStream().stream; + \\ stdout.print("OK\n", .{}) catch unreachable; \\ } \\} ); @@ -88,8 +92,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { }); cases.addCase(x: { - var tc = cases.create("two files use import each other", - \\use @import("a.zig"); + var tc = cases.create("two files usingnamespace import each other", + \\usingnamespace @import("a.zig"); \\ \\pub fn main() void { \\ ok(); @@ -97,19 +101,19 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { , "OK\n"); tc.addSourceFile("a.zig", - \\use @import("b.zig"); + \\usingnamespace @import("b.zig"); \\const io = @import("std").io; \\ \\pub const a_text = "OK\n"; \\ \\pub fn ok() void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print(b_text) catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print(b_text, .{}) catch unreachable; \\} ); tc.addSourceFile("b.zig", - \\use @import("a.zig"); + \\usingnamespace @import("a.zig"); \\ \\pub const b_text = a_text; ); @@ -121,8 +125,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\ \\pub fn main() void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable; \\} , "Hello, world!\n 12 12 a\n"); @@ -139,81 +143,81 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); \\ } - \\ _ = c.printf(c"0: %llu\n", - \\ u64(0)); - \\ _ = c.printf(c"320402575052271: %llu\n", - \\ u64(320402575052271)); - \\ _ = c.printf(c"0x01236789abcdef: %llu\n", - \\ u64(0x01236789abcdef)); - \\ _ = c.printf(c"0xffffffffffffffff: %llu\n", - \\ u64(0xffffffffffffffff)); - \\ _ = c.printf(c"0x000000ffffffffffffffff: %llu\n", - \\ u64(0x000000ffffffffffffffff)); - \\ _ = c.printf(c"0o1777777777777777777777: %llu\n", - \\ u64(0o1777777777777777777777)); - \\ _ = c.printf(c"0o0000001777777777777777777777: %llu\n", - \\ u64(0o0000001777777777777777777777)); - \\ _ = c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", - \\ u64(0b1111111111111111111111111111111111111111111111111111111111111111)); - \\ _ = c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", - \\ u64(0b0000001111111111111111111111111111111111111111111111111111111111111111)); + \\ _ = c.printf("0: %llu\n", + \\ @as(u64, 0)); + \\ _ = c.printf("320402575052271: %llu\n", + \\ @as(u64, 320402575052271)); + \\ _ = c.printf("0x01236789abcdef: %llu\n", + \\ @as(u64, 0x01236789abcdef)); + \\ _ = c.printf("0xffffffffffffffff: %llu\n", + \\ @as(u64, 0xffffffffffffffff)); + \\ _ = c.printf("0x000000ffffffffffffffff: %llu\n", + \\ @as(u64, 0x000000ffffffffffffffff)); + \\ _ = c.printf("0o1777777777777777777777: %llu\n", + \\ @as(u64, 0o1777777777777777777777)); + \\ _ = c.printf("0o0000001777777777777777777777: %llu\n", + \\ @as(u64, 0o0000001777777777777777777777)); + \\ _ = c.printf("0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", + \\ @as(u64, 0b1111111111111111111111111111111111111111111111111111111111111111)); + \\ _ = c.printf("0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", + \\ @as(u64, 0b0000001111111111111111111111111111111111111111111111111111111111111111)); \\ - \\ _ = c.printf(c"\n"); + \\ _ = c.printf("\n"); \\ - \\ _ = c.printf(c"0.0: %.013a\n", - \\ f64(0.0)); - \\ _ = c.printf(c"0e0: %.013a\n", - \\ f64(0e0)); - \\ _ = c.printf(c"0.0e0: %.013a\n", - \\ f64(0.0e0)); - \\ _ = c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %.013a\n", - \\ f64(000000000000000000000000000000000000000000000000000000000.0e0)); - \\ _ = c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %.013a\n", - \\ f64(0.000000000000000000000000000000000000000000000000000000000e0)); - \\ _ = c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %.013a\n", - \\ f64(0.0e000000000000000000000000000000000000000000000000000000000)); - \\ _ = c.printf(c"1.0: %.013a\n", - \\ f64(1.0)); - \\ _ = c.printf(c"10.0: %.013a\n", - \\ f64(10.0)); - \\ _ = c.printf(c"10.5: %.013a\n", - \\ f64(10.5)); - \\ _ = c.printf(c"10.5e5: %.013a\n", - \\ f64(10.5e5)); - \\ _ = c.printf(c"10.5e+5: %.013a\n", - \\ f64(10.5e+5)); - \\ _ = c.printf(c"50.0e-2: %.013a\n", - \\ f64(50.0e-2)); - \\ _ = c.printf(c"50e-2: %.013a\n", - \\ f64(50e-2)); + \\ _ = c.printf("0.0: %.013a\n", + \\ @as(f64, 0.0)); + \\ _ = c.printf("0e0: %.013a\n", + \\ @as(f64, 0e0)); + \\ _ = c.printf("0.0e0: %.013a\n", + \\ @as(f64, 0.0e0)); + \\ _ = c.printf("000000000000000000000000000000000000000000000000000000000.0e0: %.013a\n", + \\ @as(f64, 000000000000000000000000000000000000000000000000000000000.0e0)); + \\ _ = c.printf("0.000000000000000000000000000000000000000000000000000000000e0: %.013a\n", + \\ @as(f64, 0.000000000000000000000000000000000000000000000000000000000e0)); + \\ _ = c.printf("0.0e000000000000000000000000000000000000000000000000000000000: %.013a\n", + \\ @as(f64, 0.0e000000000000000000000000000000000000000000000000000000000)); + \\ _ = c.printf("1.0: %.013a\n", + \\ @as(f64, 1.0)); + \\ _ = c.printf("10.0: %.013a\n", + \\ @as(f64, 10.0)); + \\ _ = c.printf("10.5: %.013a\n", + \\ @as(f64, 10.5)); + \\ _ = c.printf("10.5e5: %.013a\n", + \\ @as(f64, 10.5e5)); + \\ _ = c.printf("10.5e+5: %.013a\n", + \\ @as(f64, 10.5e+5)); + \\ _ = c.printf("50.0e-2: %.013a\n", + \\ @as(f64, 50.0e-2)); + \\ _ = c.printf("50e-2: %.013a\n", + \\ @as(f64, 50e-2)); \\ - \\ _ = c.printf(c"\n"); + \\ _ = c.printf("\n"); \\ - \\ _ = c.printf(c"0x1.0: %.013a\n", - \\ f64(0x1.0)); - \\ _ = c.printf(c"0x10.0: %.013a\n", - \\ f64(0x10.0)); - \\ _ = c.printf(c"0x100.0: %.013a\n", - \\ f64(0x100.0)); - \\ _ = c.printf(c"0x103.0: %.013a\n", - \\ f64(0x103.0)); - \\ _ = c.printf(c"0x103.7: %.013a\n", - \\ f64(0x103.7)); - \\ _ = c.printf(c"0x103.70: %.013a\n", - \\ f64(0x103.70)); - \\ _ = c.printf(c"0x103.70p4: %.013a\n", - \\ f64(0x103.70p4)); - \\ _ = c.printf(c"0x103.70p5: %.013a\n", - \\ f64(0x103.70p5)); - \\ _ = c.printf(c"0x103.70p+5: %.013a\n", - \\ f64(0x103.70p+5)); - \\ _ = c.printf(c"0x103.70p-5: %.013a\n", - \\ f64(0x103.70p-5)); + \\ _ = c.printf("0x1.0: %.013a\n", + \\ @as(f64, 0x1.0)); + \\ _ = c.printf("0x10.0: %.013a\n", + \\ @as(f64, 0x10.0)); + \\ _ = c.printf("0x100.0: %.013a\n", + \\ @as(f64, 0x100.0)); + \\ _ = c.printf("0x103.0: %.013a\n", + \\ @as(f64, 0x103.0)); + \\ _ = c.printf("0x103.7: %.013a\n", + \\ @as(f64, 0x103.7)); + \\ _ = c.printf("0x103.70: %.013a\n", + \\ @as(f64, 0x103.70)); + \\ _ = c.printf("0x103.70p4: %.013a\n", + \\ @as(f64, 0x103.70p4)); + \\ _ = c.printf("0x103.70p5: %.013a\n", + \\ @as(f64, 0x103.70p5)); + \\ _ = c.printf("0x103.70p+5: %.013a\n", + \\ @as(f64, 0x103.70p+5)); + \\ _ = c.printf("0x103.70p-5: %.013a\n", + \\ @as(f64, 0x103.70p-5)); \\ \\ return 0; \\} @@ -258,14 +262,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.add("order-independent declarations", \\const io = @import("std").io; \\const z = io.stdin_fileno; - \\const x : @typeOf(y) = 1234; + \\const x : @TypeOf(y) = 1234; \\const y : u16 = 5678; \\pub fn main() void { \\ var x_local : i32 = print_ok(x); \\} - \\fn print_ok(val: @typeOf(x)) @typeOf(foo) { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("OK\n") catch unreachable; + \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) { + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("OK\n", .{}) catch unreachable; \\ return 0; \\} \\const foo : i32 = 0; @@ -286,7 +290,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ } \\} \\ - \\export fn main() c_int { + \\pub export fn main() c_int { \\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); @@ -314,7 +318,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -323,7 +327,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ const x: f64 = small; \\ const y = @floatToInt(i32, x); \\ const z = @intToFloat(f64, y); - \\ _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); + \\ _ = c.printf("%.2f\n%d\n%.2f\n%.2f\n", x, y, z, @as(f64, -0.4)); \\ return 0; \\} , "3.25\n3\n3.00\n-0.40\n"); @@ -346,26 +350,26 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub fn main() void { \\ const bar = Bar {.field2 = 13,}; \\ const foo = Foo {.field1 = bar,}; - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; + \\ const stdout = &io.getStdOut().outStream().stream; \\ if (!foo.method()) { - \\ stdout.print("BAD\n") catch unreachable; + \\ stdout.print("BAD\n", .{}) catch unreachable; \\ } \\ if (!bar.method()) { - \\ stdout.print("BAD\n") catch unreachable; + \\ stdout.print("BAD\n", .{}) catch unreachable; \\ } - \\ stdout.print("OK\n") catch unreachable; + \\ stdout.print("OK\n", .{}) catch unreachable; \\} , "OK\n"); cases.add("defer with only fallthrough", \\const io = @import("std").io; \\pub fn main() void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("before\n") catch unreachable; - \\ defer stdout.print("defer1\n") catch unreachable; - \\ defer stdout.print("defer2\n") catch unreachable; - \\ defer stdout.print("defer3\n") catch unreachable; - \\ stdout.print("after\n") catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("before\n", .{}) catch unreachable; + \\ defer stdout.print("defer1\n", .{}) catch unreachable; + \\ defer stdout.print("defer2\n", .{}) catch unreachable; + \\ defer stdout.print("defer3\n", .{}) catch unreachable; + \\ stdout.print("after\n", .{}) catch unreachable; \\} , "before\nafter\ndefer3\ndefer2\ndefer1\n"); @@ -373,14 +377,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\const os = @import("std").os; \\pub fn main() void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("before\n") catch unreachable; - \\ defer stdout.print("defer1\n") catch unreachable; - \\ defer stdout.print("defer2\n") catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("before\n", .{}) catch unreachable; + \\ defer stdout.print("defer1\n", .{}) catch unreachable; + \\ defer stdout.print("defer2\n", .{}) catch unreachable; \\ var args_it = @import("std").process.args(); \\ if (args_it.skip() and !args_it.skip()) return; - \\ defer stdout.print("defer3\n") catch unreachable; - \\ stdout.print("after\n") catch unreachable; + \\ defer stdout.print("defer3\n", .{}) catch unreachable; + \\ stdout.print("after\n", .{}) catch unreachable; \\} , "before\ndefer2\ndefer1\n"); @@ -390,13 +394,13 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ do_test() catch return; \\} \\fn do_test() !void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("before\n") catch unreachable; - \\ defer stdout.print("defer1\n") catch unreachable; - \\ errdefer stdout.print("deferErr\n") catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("before\n", .{}) catch unreachable; + \\ defer stdout.print("defer1\n", .{}) catch unreachable; + \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; \\ try its_gonna_fail(); - \\ defer stdout.print("defer3\n") catch unreachable; - \\ stdout.print("after\n") catch unreachable; + \\ defer stdout.print("defer3\n", .{}) catch unreachable; + \\ stdout.print("after\n", .{}) catch unreachable; \\} \\fn its_gonna_fail() !void { \\ return error.IToldYouItWouldFail; @@ -409,13 +413,13 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ do_test() catch return; \\} \\fn do_test() !void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print("before\n") catch unreachable; - \\ defer stdout.print("defer1\n") catch unreachable; - \\ errdefer stdout.print("deferErr\n") catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print("before\n", .{}) catch unreachable; + \\ defer stdout.print("defer1\n", .{}) catch unreachable; + \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; \\ try its_gonna_pass(); - \\ defer stdout.print("defer3\n") catch unreachable; - \\ stdout.print("after\n") catch unreachable; + \\ defer stdout.print("defer3\n", .{}) catch unreachable; + \\ stdout.print("after\n", .{}) catch unreachable; \\} \\fn its_gonna_pass() anyerror!void { } , "before\nafter\ndefer3\ndefer1\n"); @@ -426,8 +430,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\ \\pub fn main() void { - \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; - \\ stdout.print(foo_txt) catch unreachable; + \\ const stdout = &io.getStdOut().outStream().stream; + \\ stdout.print(foo_txt, .{}) catch unreachable; \\} , "1234\nabcd\n"); @@ -445,14 +449,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn main() !void { \\ var args_it = std.process.args(); - \\ var stdout_file = try io.getStdOut(); + \\ var stdout_file = io.getStdOut(); \\ var stdout_adapter = stdout_file.outStream(); \\ const stdout = &stdout_adapter.stream; \\ var index: usize = 0; \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", index, arg); + \\ try stdout.print("{}: {}\n", .{index, arg}); \\ } \\} , @@ -465,7 +469,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ ); - tc.setCommandLineArgs([_][]const u8{ + tc.setCommandLineArgs(&[_][]const u8{ "first arg", "'a' 'b' \\", "bare", @@ -486,14 +490,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn main() !void { \\ var args_it = std.process.args(); - \\ var stdout_file = try io.getStdOut(); + \\ var stdout_file = io.getStdOut(); \\ var stdout_adapter = stdout_file.outStream(); \\ const stdout = &stdout_adapter.stream; \\ var index: usize = 0; \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", index, arg); + \\ try stdout.print("{}: {}\n", .{index, arg}); \\ } \\} , @@ -506,7 +510,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ ); - tc.setCommandLineArgs([_][]const u8{ + tc.setCommandLineArgs(&[_][]const u8{ "first arg", "'a' 'b' \\", "bare", diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9a25bcce9..3ce5bd880 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,8 +2,481 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { - cases.add( - "switch with overlapping case ranges", + cases.addTest("@export with empty name string", + \\pub export fn entry() void { } + \\comptime { + \\ @export(entry, .{ .name = "" }); + \\} + , &[_][]const u8{ + "tmp.zig:3:5: error: exported symbol name cannot be empty", + }); + + cases.addTest("switch ranges endpoints are validated", + \\pub export fn entry() void { + \\ var x: i32 = 0; + \\ switch (x) { + \\ 6...1 => {}, + \\ -1...-5 => {}, + \\ else => unreachable, + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:4:9: error: range start value is greater than the end value", + "tmp.zig:5:9: error: range start value is greater than the end value", + }); + + cases.addTest("errors in for loop bodies are propagated", + \\pub export fn entry() void { + \\ var arr: [100]u8 = undefined; + \\ for (arr) |bits| _ = @popCount(bits); + \\} + , &[_][]const u8{ + "tmp.zig:3:26: error: expected 2 arguments, found 1", + }); + + cases.addTest("@call rejects non comptime-known fn - always_inline", + \\pub export fn entry() void { + \\ var call_me: fn () void = undefined; + \\ @call(.{ .modifier = .always_inline }, call_me, .{}); + \\} + , &[_][]const u8{ + "tmp.zig:3:5: error: the specified modifier requires a comptime-known function", + }); + + cases.addTest("@call rejects non comptime-known fn - compile_time", + \\pub export fn entry() void { + \\ var call_me: fn () void = undefined; + \\ @call(.{ .modifier = .compile_time }, call_me, .{}); + \\} + , &[_][]const u8{ + "tmp.zig:3:5: error: the specified modifier requires a comptime-known function", + }); + + cases.addTest("error in struct initializer doesn't crash the compiler", + \\pub export fn entry() void { + \\ const bitfield = struct { + \\ e: u8, + \\ e: u8, + \\ }; + \\ var a = .{@sizeOf(bitfield)}; + \\} + , &[_][]const u8{ + "tmp.zig:4:9: error: duplicate struct field: 'e'", + }); + + cases.addTest("repeated invalid field access to generic function returning type crashes compiler. #2655", + \\pub fn A() type { + \\ return Q; + \\} + \\test "1" { + \\ _ = A().a; + \\ _ = A().a; + \\} + , &[_][]const u8{ + "tmp.zig:2:12: error: use of undeclared identifier 'Q'", + }); + + cases.add("bitCast to enum type", + \\export fn entry() void { + \\ const y = @bitCast(enum(u32) { a, b }, @as(u32, 3)); + \\} + , &[_][]const u8{ + "tmp.zig:2:24: error: cannot cast a value of type 'y'", + }); + + cases.add("comparing against undefined produces undefined value", + \\export fn entry() void { + \\ if (2 == undefined) {} + \\} + , &[_][]const u8{ + "tmp.zig:2:11: error: use of undefined value here causes undefined behavior", + }); + + cases.add("comptime ptrcast of zero-sized type", + \\fn foo() void { + \\ const node: struct {} = undefined; + \\ const vla_ptr = @ptrCast([*]const u8, &node); + \\} + \\comptime { foo(); } + , &[_][]const u8{ + "tmp.zig:3:21: error: '*const struct:2:17' and '[*]const u8' do not have the same in-memory representation", + }); + + cases.add("slice sentinel mismatch", + \\fn foo() [:0]u8 { + \\ var x: []u8 = undefined; + \\ return x; + \\} + \\comptime { _ = foo; } + , &[_][]const u8{ + "tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'", + "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel", + }); + + cases.add("cmpxchg with float", + \\export fn entry() void { + \\ var x: f32 = 0; + \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); + \\} + , &[_][]const u8{ + "tmp.zig:3:22: error: expected integer, enum or pointer type, found 'f32'", + }); + + cases.add("atomicrmw with float op not .Xchg, .Add or .Sub", + \\export fn entry() void { + \\ var x: f32 = 0; + \\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); + \\} + , &[_][]const u8{ + "tmp.zig:3:29: error: @atomicRmw with float only works with .Xchg, .Add and .Sub", + }); + + cases.add("intToPtr with misaligned address", + \\pub fn main() void { + \\ var y = @intToPtr([*]align(4) u8, 5); + \\} + , &[_][]const u8{ + "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address", + }); + + cases.add("switch on extern enum missing else prong", + \\const i = extern enum { + \\ n = 0, + \\ o = 2, + \\ p = 4, + \\ q = 4, + \\}; + \\pub fn main() void { + \\ var x = @intToEnum(i, 52); + \\ switch (x) { + \\ .n, + \\ .o, + \\ .p => unreachable, + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:9:5: error: switch on an extern enum must have an else prong", + }); + + cases.add("invalid float literal", + \\const std = @import("std"); + \\ + \\pub fn main() void { + \\ var bad_float :f32 = 0.0; + \\ bad_float = bad_float + .20; + \\ std.debug.assert(bad_float < 1.0); + \\}) + , &[_][]const u8{ + "tmp.zig:5:29: error: invalid token: '.'", + }); + + cases.add("var args without c calling conv", + \\fn foo(args: ...) void {} + \\comptime { + \\ _ = foo; + \\} + , &[_][]const u8{ + "tmp.zig:1:8: error: var args only allowed in functions with C calling convention", + }); + + cases.add("comptime struct field, no init value", + \\const Foo = struct { + \\ comptime b: i32, + \\}; + \\export fn entry() void { + \\ var f: Foo = undefined; + \\} + , &[_][]const u8{ + "tmp.zig:2:5: error: comptime struct field missing initialization value", + }); + + cases.add("bad usage of @call", + \\export fn entry1() void { + \\ @call(.{}, foo, {}); + \\} + \\export fn entry2() void { + \\ comptime @call(.{ .modifier = .never_inline }, foo, .{}); + \\} + \\export fn entry3() void { + \\ comptime @call(.{ .modifier = .never_tail }, foo, .{}); + \\} + \\export fn entry4() void { + \\ @call(.{ .modifier = .never_inline }, bar, .{}); + \\} + \\export fn entry5(c: bool) void { + \\ var baz = if (c) baz1 else baz2; + \\ @call(.{ .modifier = .compile_time }, baz, .{}); + \\} + \\fn foo() void {} + \\inline fn bar() void {} + \\fn baz1() void {} + \\fn baz2() void {} + , &[_][]const u8{ + "tmp.zig:2:21: error: expected tuple or struct, found 'void'", + "tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time", + "tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time", + "tmp.zig:11:5: error: no-inline call of inline function", + "tmp.zig:15:5: error: the specified modifier requires a comptime-known function", + }); + + cases.add("exported async function", + \\export async fn foo() void {} + , &[_][]const u8{ + "tmp.zig:1:1: error: exported function cannot be async", + }); + + cases.addExe("main missing name", + \\pub fn (main) void {} + , &[_][]const u8{ + "tmp.zig:1:5: error: missing function name", + }); + + cases.addCase(x: { + var tc = cases.create("call with new stack on unsupported target", + \\var buf: [10]u8 align(16) = undefined; + \\export fn entry() void { + \\ @call(.{.stack = &buf}, foo, .{}); + \\} + \\fn foo() void {} + , &[_][]const u8{ + "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", + }); + tc.target = tests.Target{ + .Cross = tests.CrossTarget{ + .arch = .wasm32, + .os = .wasi, + .abi = .none, + }, + }; + break :x tc; + }); + + // Note: One of the error messages here is backwards. It would be nice to fix, but that's not + // going to stop me from merging this branch which fixes a bunch of other stuff. + cases.add("incompatible sentinels", + \\export fn entry1(ptr: [*:255]u8) [*:0]u8 { + \\ return ptr; + \\} + \\export fn entry2(ptr: [*]u8) [*:0]u8 { + \\ return ptr; + \\} + \\export fn entry3() void { + \\ var array: [2:0]u8 = [_:255]u8{1, 2}; + \\} + \\export fn entry4() void { + \\ var array: [2:0]u8 = [_]u8{1, 2}; + \\} + , &[_][]const u8{ + "tmp.zig:2:12: error: expected type '[*:0]u8', found '[*:255]u8'", + "tmp.zig:2:12: note: destination pointer requires a terminating '0' sentinel, but source pointer has a terminating '255' sentinel", + "tmp.zig:5:12: error: expected type '[*:0]u8', found '[*]u8'", + "tmp.zig:5:12: note: destination pointer requires a terminating '0' sentinel", + + "tmp.zig:8:35: error: expected type '[2:255]u8', found '[2:0]u8'", + "tmp.zig:8:35: note: destination array requires a terminating '255' sentinel, but source array has a terminating '0' sentinel", + "tmp.zig:11:31: error: expected type '[2:0]u8', found '[2]u8'", + "tmp.zig:11:31: note: destination array requires a terminating '0' sentinel", + }); + + cases.add("empty switch on an integer", + \\export fn entry() void { + \\ var x: u32 = 0; + \\ switch(x) {} + \\} + , &[_][]const u8{ + "tmp.zig:3:5: error: switch must handle all possibilities", + }); + + cases.add("incorrect return type", + \\ pub export fn entry() void{ + \\ _ = foo(); + \\ } + \\ const A = struct { + \\ a: u32, + \\ }; + \\ fn foo() A { + \\ return bar(); + \\ } + \\ const B = struct { + \\ a: u32, + \\ }; + \\ fn bar() B { + \\ unreachable; + \\ } + , &[_][]const u8{ + "tmp.zig:8:16: error: expected type 'A', found 'B'", + }); + + cases.add("regression test #2980: base type u32 is not type checked properly when assigning a value within a struct", + \\const Foo = struct { + \\ ptr: ?*usize, + \\ uval: u32, + \\}; + \\fn get_uval(x: u32) !u32 { + \\ return error.NotFound; + \\} + \\export fn entry() void { + \\ const afoo = Foo{ + \\ .ptr = null, + \\ .uval = get_uval(42), + \\ }; + \\} + , &[_][]const u8{ + "tmp.zig:11:25: error: expected type 'u32', found '@TypeOf(get_uval).ReturnType.ErrorSet!u32'", + }); + + cases.add("asigning to struct or union fields that are not optionals with a function that returns an optional", + \\fn maybe(is: bool) ?u8 { + \\ if (is) return @as(u8, 10) else return null; + \\} + \\const U = union { + \\ Ye: u8, + \\}; + \\const S = struct { + \\ num: u8, + \\}; + \\export fn entry() void { + \\ var u = U{ .Ye = maybe(false) }; + \\ var s = S{ .num = maybe(false) }; + \\} + , &[_][]const u8{ + "tmp.zig:11:27: error: expected type 'u8', found '?u8'", + }); + + cases.add("missing result type for phi node", + \\fn foo() !void { + \\ return anyerror.Foo; + \\} + \\export fn entry() void { + \\ foo() catch 0; + \\} + , &[_][]const u8{ + "tmp.zig:5:17: error: integer value 0 cannot be coerced to type 'void'", + }); + + cases.add("atomicrmw with enum op not .Xchg", + \\export fn entry() void { + \\ const E = enum(u8) { + \\ a, + \\ b, + \\ c, + \\ d, + \\ }; + \\ var x: E = .a; + \\ _ = @atomicRmw(E, &x, .Add, .b, .SeqCst); + \\} + , &[_][]const u8{ + "tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg", + }); + + cases.add("disallow coercion from non-null-terminated pointer to null-terminated pointer", + \\extern fn puts(s: [*:0]const u8) c_int; + \\pub fn main() void { + \\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; + \\ const no_zero_ptr: [*]const u8 = &no_zero_array; + \\ _ = puts(no_zero_ptr); + \\} + , &[_][]const u8{ + "tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8'", + }); + + cases.add("atomic orderings of atomicStore Acquire or AcqRel", + \\export fn entry() void { + \\ var x: u32 = 0; + \\ @atomicStore(u32, &x, 1, .Acquire); + \\} + , &[_][]const u8{ + "tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel", + }); + + cases.add("missing const in slice with nested array type", + \\const Geo3DTex2D = struct { vertices: [][2]f32 }; + \\pub fn getGeo3DTex2D() Geo3DTex2D { + \\ return Geo3DTex2D{ + \\ .vertices = [_][2]f32{ + \\ [_]f32{ -0.5, -0.5}, + \\ }, + \\ }; + \\} + \\export fn entry() void { + \\ var geo_data = getGeo3DTex2D(); + \\} + , &[_][]const u8{ + "tmp.zig:4:30: error: array literal requires address-of operator to coerce to slice type '[][2]f32'", + }); + + cases.add("slicing of global undefined pointer", + \\var buf: *[1]u8 = undefined; + \\export fn entry() void { + \\ _ = buf[0..1]; + \\} + , &[_][]const u8{ + "tmp.zig:3:12: error: non-zero length slice of undefined pointer", + }); + + cases.add("using invalid types in function call raises an error", + \\const MenuEffect = enum {}; + \\fn func(effect: MenuEffect) void {} + \\export fn entry() void { + \\ func(MenuEffect.ThisDoesNotExist); + \\} + , &[_][]const u8{ + "tmp.zig:1:20: error: enums must have 1 or more fields", + "tmp.zig:4:20: note: referenced here", + }); + + cases.add("store vector pointer with unknown runtime index", + \\export fn entry() void { + \\ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + \\ + \\ var i: u32 = 0; + \\ storev(&v[i], 42); + \\} + \\ + \\fn storev(ptr: var, val: i32) void { + \\ ptr.* = val; + \\} + , &[_][]const u8{ + "tmp.zig:9:8: error: unable to determine vector element index of type '*align(16:0:4:?) i32", + }); + + cases.add("load vector pointer with unknown runtime index", + \\export fn entry() void { + \\ var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + \\ + \\ var i: u32 = 0; + \\ var x = loadv(&v[i]); + \\} + \\ + \\fn loadv(ptr: var) i32 { + \\ return ptr.*; + \\} + , &[_][]const u8{ + "tmp.zig:9:12: error: unable to determine vector element index of type '*align(16:0:4:?) i32", + }); + + cases.add("using an unknown len ptr type instead of array", + \\const resolutions = [*][*]const u8{ + \\ "[320 240 ]", + \\ null, + \\}; + \\comptime { + \\ _ = resolutions; + \\} + , &[_][]const u8{ + "tmp.zig:1:21: error: expected array type or [_], found '[*][*]const u8'", + }); + + cases.add("comparison with error union and error value", + \\export fn entry() void { + \\ var number_or_error: anyerror!i32 = error.SomethingAwful; + \\ _ = number_or_error == error.SomethingAwful; + \\} + , &[_][]const u8{ + "tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32'", + }); + + cases.add("switch with overlapping case ranges", \\export fn entry() void { \\ var q: u8 = 0; \\ switch (q) { @@ -11,22 +484,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ 0...255 => {}, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:9: error: duplicate switch value", - ); + }); - cases.add( - "invalid optional type in extern struct", + cases.add("invalid optional type in extern struct", \\const stroo = extern struct { \\ moo: ?[*c]u8, \\}; \\export fn testf(fluff: *stroo) void {} - , + , &[_][]const u8{ "tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8'", - ); + }); - cases.add( - "attempt to negate a non-integer, non-float or non-vector type", + cases.add("attempt to negate a non-integer, non-float or non-vector type", \\fn foo() anyerror!u32 { \\ return 1; \\} @@ -34,42 +505,38 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const x = -foo(); \\} - , + , &[_][]const u8{ "tmp.zig:6:15: error: negation of type 'anyerror!u32'", - ); + }); - cases.add( - "attempt to create 17 bit float type", + cases.add("attempt to create 17 bit float type", \\const builtin = @import("builtin"); \\comptime { \\ _ = @Type(builtin.TypeInfo { .Float = builtin.TypeInfo.Float { .bits = 17 } }); \\} - , + , &[_][]const u8{ "tmp.zig:3:32: error: 17-bit float unsupported", - ); + }); - cases.add( - "wrong type for @Type", + cases.add("wrong type for @Type", \\export fn entry() void { \\ _ = @Type(0); \\} - , - "tmp.zig:2:15: error: expected type 'builtin.TypeInfo', found 'comptime_int'", - ); + , &[_][]const u8{ + "tmp.zig:2:15: error: expected type 'std.builtin.TypeInfo', found 'comptime_int'", + }); - cases.add( - "@Type with non-constant expression", + cases.add("@Type with non-constant expression", \\const builtin = @import("builtin"); \\var globalTypeInfo : builtin.TypeInfo = undefined; \\export fn entry() void { \\ _ = @Type(globalTypeInfo); \\} - , + , &[_][]const u8{ "tmp.zig:4:15: error: unable to evaluate constant expression", - ); + }); - cases.add( - "@Type with TypeInfo.Int", + cases.add("@Type with TypeInfo.Int", \\const builtin = @import("builtin"); \\export fn entry() void { \\ _ = @Type(builtin.TypeInfo.Int { @@ -77,21 +544,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .bits = 8, \\ }); \\} - , - "tmp.zig:3:36: error: expected type 'builtin.TypeInfo', found 'builtin.Int'", - ); + , &[_][]const u8{ + "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'", + }); - cases.add( - "Struct unavailable for @Type", + cases.add("Struct unavailable for @Type", \\export fn entry() void { \\ _ = @Type(@typeInfo(struct { })); \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: @Type not availble for 'TypeInfo.Struct'", - ); + }); - cases.add( - "wrong type for result ptr to @asyncCall", + cases.add("wrong type for result ptr to @asyncCall", \\export fn entry() void { \\ _ = async amain(); \\} @@ -102,32 +567,29 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() i32 { \\ return 1234; \\} - , + , &[_][]const u8{ "tmp.zig:6:37: error: expected type '*i32', found 'bool'", - ); + }); - cases.add( - "shift amount has to be an integer type", + cases.add("shift amount has to be an integer type", \\export fn entry() void { - \\ const x = 1 << &u8(10); + \\ const x = 1 << &@as(u8, 10); \\} - , - "tmp.zig:2:23: error: shift amount has to be an integer type, but found '*u8'", + , &[_][]const u8{ + "tmp.zig:2:21: error: shift amount has to be an integer type, but found '*u8'", "tmp.zig:2:17: note: referenced here", - ); + }); - cases.add( - "bit shifting only works on integer types", + cases.add("bit shifting only works on integer types", \\export fn entry() void { - \\ const x = &u8(1) << 10; + \\ const x = &@as(u8, 1) << 10; \\} - , - "tmp.zig:2:18: error: bit shifting operation expected integer type, found '*u8'", - "tmp.zig:2:22: note: referenced here", - ); + , &[_][]const u8{ + "tmp.zig:2:16: error: bit shifting operation expected integer type, found '*u8'", + "tmp.zig:2:27: note: referenced here", + }); - cases.add( - "struct depends on itself via optional field", + cases.add("struct depends on itself via optional field", \\const LhsExpr = struct { \\ rhsExpr: ?AstObject, \\}; @@ -138,14 +600,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const lhsExpr = LhsExpr{ .rhsExpr = null }; \\ const obj = AstObject{ .lhsExpr = lhsExpr }; \\} - , + , &[_][]const u8{ "tmp.zig:1:17: error: struct 'LhsExpr' depends on itself", "tmp.zig:5:5: note: while checking this field", "tmp.zig:2:5: note: while checking this field", - ); + }); - cases.add( - "alignment of enum field specified", + cases.add("alignment of enum field specified", \\const Number = enum { \\ a, \\ b align(i32), @@ -153,23 +614,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry1() void { \\ var x: Number = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:13: error: structs and unions, not enums, support field alignment", "tmp.zig:1:16: note: consider 'union(enum)' here", - ); + }); - cases.add( - "bad alignment type", + cases.add("bad alignment type", \\export fn entry1() void { \\ var x: []align(true) i32 = undefined; \\} \\export fn entry2() void { - \\ var x: *align(f64(12.34)) i32 = undefined; + \\ var x: *align(@as(f64, 12.34)) i32 = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:20: error: expected type 'u29', found 'bool'", - "tmp.zig:5:22: error: fractional component prevents float value 12.340000 from being casted to type 'u29'", - ); + "tmp.zig:5:19: error: fractional component prevents float value 12.340000 from being casted to type 'u29'", + }); cases.addCase(x: { var tc = cases.create("variable in inline assembly template cannot be found", @@ -179,7 +639,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ : [bar] "=r" (-> usize) \\ ); \\} - , "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs"); + , &[_][]const u8{ + "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", + }); tc.target = tests.Target{ .Cross = tests.CrossTarget{ .arch = .x86_64, @@ -190,8 +652,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "indirect recursion of async functions detected", + cases.add("indirect recursion of async functions detected", \\var frame: ?anyframe = null; \\ \\export fn a() void { @@ -220,14 +681,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var child = rangeSum(x - 1); \\ return child + 1; \\} - , + , &[_][]const u8{ "tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself", "tmp.zig:15:33: note: when analyzing type '@Frame(rangeSum)' here", "tmp.zig:26:25: note: when analyzing type '@Frame(rangeSumIndirect)' here", - ); + }); - cases.add( - "non-async function pointer eventually is inferred to become async", + cases.add("non-async function pointer eventually is inferred to become async", \\export fn a() void { \\ var non_async_fn: fn () void = undefined; \\ non_async_fn = func; @@ -235,45 +695,41 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn func() void { \\ suspend; \\} - , + , &[_][]const u8{ "tmp.zig:5:1: error: 'func' cannot be async", "tmp.zig:3:20: note: required to be non-async here", "tmp.zig:6:5: note: suspends here", - ); + }); - cases.add( - "bad alignment in @asyncCall", + cases.add("bad alignment in @asyncCall", \\export fn entry() void { \\ var ptr: async fn () void = func; \\ var bytes: [64]u8 = undefined; \\ _ = @asyncCall(&bytes, {}, ptr); \\} \\async fn func() void {} - , + , &[_][]const u8{ "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'", - ); + }); - cases.add( - "atomic orderings of fence Acquire or stricter", + cases.add("atomic orderings of fence Acquire or stricter", \\export fn entry() void { \\ @fence(.Monotonic); \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: atomic ordering must be Acquire or stricter", - ); + }); - cases.add( - "bad alignment in implicit cast from array pointer to slice", + cases.add("bad alignment in implicit cast from array pointer to slice", \\export fn a() void { \\ var x: [10]u8 = undefined; \\ var y: []align(16) u8 = &x; \\} - , + , &[_][]const u8{ "tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'", - ); + }); - cases.add( - "result location incompatibility mismatching handle_is_ptr (generic call)", + cases.add("result location incompatibility mismatching handle_is_ptr (generic call)", \\export fn entry() void { \\ var damn = Container{ \\ .not_optional = getOptional(i32), @@ -285,12 +741,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub const Container = struct { \\ not_optional: i32, \\}; - , + , &[_][]const u8{ "tmp.zig:3:36: error: expected type 'i32', found '?i32'", - ); + }); - cases.add( - "result location incompatibility mismatching handle_is_ptr", + cases.add("result location incompatibility mismatching handle_is_ptr", \\export fn entry() void { \\ var damn = Container{ \\ .not_optional = getOptional(), @@ -302,12 +757,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub const Container = struct { \\ not_optional: i32, \\}; - , + , &[_][]const u8{ "tmp.zig:3:36: error: expected type 'i32', found '?i32'", - ); + }); - cases.add( - "const frame cast to anyframe", + cases.add("const frame cast to anyframe", \\export fn a() void { \\ const f = async func(); \\ resume f; @@ -319,13 +773,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn func() void { \\ suspend; \\} - , + , &[_][]const u8{ "tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'", "tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)'", - ); + }); - cases.add( - "prevent bad implicit casting of anyframe types", + cases.add("prevent bad implicit casting of anyframe types", \\export fn a() void { \\ var x: anyframe = undefined; \\ var y: anyframe->i32 = x; @@ -339,14 +792,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var y: anyframe->i32 = &x; \\} \\fn func() void {} - , + , &[_][]const u8{ "tmp.zig:3:28: error: expected type 'anyframe->i32', found 'anyframe'", "tmp.zig:7:28: error: expected type 'anyframe->i32', found 'i32'", "tmp.zig:11:29: error: expected type 'anyframe->i32', found '*@Frame(func)'", - ); + }); - cases.add( - "wrong frame type used for async call", + cases.add("wrong frame type used for async call", \\export fn entry() void { \\ var frame: @Frame(foo) = undefined; \\ frame = async bar(); @@ -357,37 +809,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bar() void { \\ suspend; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: expected type '*@Frame(bar)', found '*@Frame(foo)'", - ); + }); - cases.add( - "@Frame() of generic function", + cases.add("@Frame() of generic function", \\export fn entry() void { \\ var frame: @Frame(func) = undefined; \\} \\fn func(comptime T: type) void { \\ var x: T = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:16: error: @Frame() of generic function", - ); + }); - cases.add( - "@frame() causes function to be async", + cases.add("@frame() causes function to be async", \\export fn entry() void { \\ func(); \\} \\fn func() void { \\ _ = @frame(); \\} - , - "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async", + , &[_][]const u8{ + "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", "tmp.zig:5:9: note: @frame() causes function to be async", - ); + }); - cases.add( - "invalid suspend in exported function", + cases.add("invalid suspend in exported function", \\export fn entry() void { \\ var frame = async func(); \\ var result = await frame; @@ -395,13 +844,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn func() void { \\ suspend; \\} - , - "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async", + , &[_][]const u8{ + "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", "tmp.zig:3:18: note: await here is a suspend point", - ); + }); - cases.add( - "async function indirectly depends on its own frame", + cases.add("async function indirectly depends on its own frame", \\export fn entry() void { \\ _ = async amain(); \\} @@ -411,39 +859,36 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn other() void { \\ var x: [@sizeOf(@Frame(amain))]u8 = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:4:1: error: unable to determine async function frame of 'amain'", "tmp.zig:5:10: note: analysis of function 'other' depends on the frame", "tmp.zig:8:13: note: referenced here", - ); + }); - cases.add( - "async function depends on its own frame", + cases.add("async function depends on its own frame", \\export fn entry() void { \\ _ = async amain(); \\} \\async fn amain() void { \\ var x: [@sizeOf(@Frame(amain))]u8 = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet", "tmp.zig:5:13: note: referenced here", - ); + }); - cases.add( - "non async function pointer passed to @asyncCall", + cases.add("non async function pointer passed to @asyncCall", \\export fn entry() void { \\ var ptr = afunc; \\ var bytes: [100]u8 align(16) = undefined; \\ _ = @asyncCall(&bytes, {}, ptr); \\} \\fn afunc() void { } - , + , &[_][]const u8{ "tmp.zig:4:32: error: expected async function, found 'fn() void'", - ); + }); - cases.add( - "runtime-known async function called", + cases.add("runtime-known async function called", \\export fn entry() void { \\ _ = async amain(); \\} @@ -452,24 +897,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = ptr(); \\} \\async fn afunc() void {} - , + , &[_][]const u8{ "tmp.zig:6:12: error: function is not comptime-known; @asyncCall required", - ); + }); - cases.add( - "runtime-known function called with async keyword", + cases.add("runtime-known function called with async keyword", \\export fn entry() void { \\ var ptr = afunc; \\ _ = async ptr(); \\} \\ \\async fn afunc() void { } - , + , &[_][]const u8{ "tmp.zig:3:15: error: function is not comptime-known; @asyncCall required", - ); + }); - cases.add( - "function with ccc indirectly calling async function", + cases.add("function with ccc indirectly calling async function", \\export fn entry() void { \\ foo(); \\} @@ -479,15 +922,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bar() void { \\ suspend; \\} - , - "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async", + , &[_][]const u8{ + "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", "tmp.zig:2:8: note: async function call here", "tmp.zig:5:8: note: async function call here", "tmp.zig:8:5: note: suspends here", - ); + }); - cases.add( - "capture group on switch prong with incompatible payload types", + cases.add("capture group on switch prong with incompatible payload types", \\const Union = union(enum) { \\ A: usize, \\ B: isize, @@ -498,59 +940,53 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .A, .B => |e| unreachable, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:8:20: error: capture group with incompatible types", "tmp.zig:8:9: note: type 'usize' here", "tmp.zig:8:13: note: type 'isize' here", - ); + }); - cases.add( - "wrong type to @hasField", + cases.add("wrong type to @hasField", \\export fn entry() bool { \\ return @hasField(i32, "hi"); \\} - , + , &[_][]const u8{ "tmp.zig:2:22: error: type 'i32' does not support @hasField", - ); + }); - cases.add( - "slice passed as array init type with elems", + cases.add("slice passed as array init type with elems", \\export fn entry() void { \\ const x = []u8{1, 2}; \\} - , - "tmp.zig:2:15: error: expected array type or [_], found slice", - ); + , &[_][]const u8{ + "tmp.zig:2:15: error: array literal requires address-of operator to coerce to slice type '[]u8'", + }); - cases.add( - "slice passed as array init type", + cases.add("slice passed as array init type", \\export fn entry() void { \\ const x = []u8{}; \\} - , - "tmp.zig:2:15: error: expected array type or [_], found slice", - ); + , &[_][]const u8{ + "tmp.zig:2:15: error: array literal requires address-of operator to coerce to slice type '[]u8'", + }); - cases.add( - "inferred array size invalid here", + cases.add("inferred array size invalid here", \\export fn entry() void { \\ const x = [_]u8; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: inferred array size invalid here", - ); + }); - cases.add( - "initializing array with struct syntax", + cases.add("initializing array with struct syntax", \\export fn entry() void { \\ const x = [_]u8{ .y = 2 }; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: initializing array with struct syntax", - ); + }); - cases.add( - "compile error in struct init expression", + cases.add("compile error in struct init expression", \\const Foo = struct { \\ a: i32 = crap, \\ b: i32, @@ -560,53 +996,49 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .b = 5, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:2:14: error: use of undeclared identifier 'crap'", - ); + }); - cases.add( - "undefined as field type is rejected", + cases.add("undefined as field type is rejected", \\const Foo = struct { \\ a: undefined, \\}; \\export fn entry1() void { \\ const foo: Foo = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:8: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "@hasDecl with non-container", + cases.add("@hasDecl with non-container", \\export fn entry() void { \\ _ = @hasDecl(i32, "hi"); \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: expected struct, enum, or union; found 'i32'", - ); + }); - cases.add( - "field access of slices", + cases.add("field access of slices", \\export fn entry() void { \\ var slice: []i32 = undefined; - \\ const info = @typeOf(slice).unknown; + \\ const info = @TypeOf(slice).unknown; \\} - , + , &[_][]const u8{ "tmp.zig:3:32: error: type '[]i32' does not support field access", - ); + }); - cases.add( - "peer cast then implicit cast const pointer to mutable C pointer", + cases.add("peer cast then implicit cast const pointer to mutable C pointer", \\export fn func() void { \\ var strValue: [*c]u8 = undefined; - \\ strValue = strValue orelse c""; + \\ strValue = strValue orelse ""; \\} - , - "tmp.zig:3:32: error: cast discards const qualifier", - ); + , &[_][]const u8{ + "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8'", + "tmp.zig:3:32: note: cast discards const qualifier", + }); - cases.add( - "overflow in enum value allocation", + cases.add("overflow in enum value allocation", \\const Moo = enum(u8) { \\ Last = 255, \\ Over, @@ -614,32 +1046,29 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub fn main() void { \\ var y = Moo.Last; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: enumeration value 256 too large for type 'u8'", - ); + }); - cases.add( - "attempt to cast enum literal to error", + cases.add("attempt to cast enum literal to error", \\export fn entry() void { \\ switch (error.Hi) { \\ .Hi => {}, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: expected type 'error{Hi}', found '(enum literal)'", - ); + }); - cases.add( - "@sizeOf bad type", + cases.add("@sizeOf bad type", \\export fn entry() usize { - \\ return @sizeOf(@typeOf(null)); + \\ return @sizeOf(@TypeOf(null)); \\} - , + , &[_][]const u8{ "tmp.zig:2:20: error: no size available for type '(null)'", - ); + }); - cases.add( - "generic function where return type is self-referenced", + cases.add("generic function where return type is self-referenced", \\fn Foo(comptime T: type) Foo(T) { \\ return struct{ x: T }; \\} @@ -648,22 +1077,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .x = 1 \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:1:29: error: evaluation exceeded 1000 backwards branches", "tmp.zig:5:18: note: referenced here", - ); + }); - cases.add( - "@ptrToInt 0 to non optional pointer", + cases.add("@ptrToInt 0 to non optional pointer", \\export fn entry() void { \\ var b = @intToPtr(*i32, 0); \\} - , + , &[_][]const u8{ "tmp.zig:2:13: error: pointer type '*i32' does not allow address zero", - ); + }); - cases.add( - "cast enum literal to enum but it doesn't match", + cases.add("cast enum literal to enum but it doesn't match", \\const Foo = enum { \\ a, \\ b, @@ -671,34 +1098,31 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const x: Foo = .c; \\} - , + , &[_][]const u8{ "tmp.zig:6:20: error: enum 'Foo' has no field named 'c'", "tmp.zig:1:13: note: 'Foo' declared here", - ); + }); - cases.add( - "discarding error value", + cases.add("discarding error value", \\export fn entry() void { \\ _ = foo(); \\} \\fn foo() !void { \\ return error.OutOfMemory; \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: error is discarded", - ); + }); - cases.add( - "volatile on global assembly", + cases.add("volatile on global assembly", \\comptime { \\ asm volatile (""); \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: volatile is meaningless on global assembly", - ); + }); - cases.add( - "invalid multiple dereferences", + cases.add("invalid multiple dereferences", \\export fn a() void { \\ var box = Box{ .field = 0 }; \\ box.*.field = 1; @@ -711,20 +1135,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub const Box = struct { \\ field: i32, \\}; - , + , &[_][]const u8{ "tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box'", "tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box'", - ); + }); - cases.add( - "usingnamespace with wrong type", + cases.add("usingnamespace with wrong type", \\usingnamespace void; - , + , &[_][]const u8{ "tmp.zig:1:1: error: expected struct, enum, or union; found 'void'", - ); + }); - cases.add( - "ignored expression in while continuation", + cases.add("ignored expression in while continuation", \\export fn a() void { \\ while (true) : (bad()) {} \\} @@ -739,96 +1161,86 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bad() anyerror!void { \\ return error.Bad; \\} - , + , &[_][]const u8{ "tmp.zig:2:24: error: expression value is ignored", "tmp.zig:6:25: error: expression value is ignored", "tmp.zig:10:25: error: expression value is ignored", - ); + }); - cases.add( - "empty while loop body", + cases.add("empty while loop body", \\export fn a() void { \\ while(true); \\} - , + , &[_][]const u8{ "tmp.zig:2:16: error: expected loop body, found ';'", - ); + }); - cases.add( - "empty for loop body", + cases.add("empty for loop body", \\export fn a() void { \\ for(undefined) |x|; \\} - , + , &[_][]const u8{ "tmp.zig:2:23: error: expected loop body, found ';'", - ); + }); - cases.add( - "empty if body", + cases.add("empty if body", \\export fn a() void { \\ if(true); \\} - , + , &[_][]const u8{ "tmp.zig:2:13: error: expected if body, found ';'", - ); + }); - cases.add( - "import outside package path", + cases.add("import outside package path", \\comptime{ \\ _ = @import("../a.zig"); \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: import of file outside package path: '../a.zig'", - ); + }); - cases.add( - "bogus compile var", + cases.add("bogus compile var", \\const x = @import("builtin").bogus; - \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } + , &[_][]const u8{ "tmp.zig:1:29: error: container 'builtin' has no member called 'bogus'", - ); + }); - cases.add( - "wrong panic signature, runtime function", + cases.add("wrong panic signature, runtime function", \\test "" {} \\ \\pub fn panic() void {} \\ - , - "tmp.zig:3:5: error: expected type 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'", - ); + , &[_][]const u8{ + "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn() void'", + }); - cases.add( - "wrong panic signature, generic function", + cases.add("wrong panic signature, generic function", \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { \\ while (true) {} \\} - , - "tmp.zig:1:5: error: expected type 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn([]const u8,var)var'", - "tmp.zig:1:5: note: only one of the functions is generic", - ); + , &[_][]const u8{ + "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,var) var'", + "note: only one of the functions is generic", + }); - cases.add( - "direct struct loop", + cases.add("direct struct loop", \\const A = struct { a : A, }; \\export fn entry() usize { return @sizeOf(A); } - , + , &[_][]const u8{ "tmp.zig:1:11: error: struct 'A' depends on itself", - ); + }); - cases.add( - "indirect struct loop", + cases.add("indirect struct loop", \\const A = struct { b : B, }; \\const B = struct { c : C, }; \\const C = struct { a : A, }; \\export fn entry() usize { return @sizeOf(A); } - , + , &[_][]const u8{ "tmp.zig:1:11: error: struct 'A' depends on itself", - ); + }); - cases.add( - "instantiating an undefined value for an invalid struct that contains itself", + cases.add("instantiating an undefined value for an invalid struct that contains itself", \\const Foo = struct { \\ x: Foo, \\}; @@ -836,15 +1248,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\var foo: Foo = undefined; \\ \\export fn entry() usize { - \\ return @sizeOf(@typeOf(foo.x)); + \\ return @sizeOf(@TypeOf(foo.x)); \\} - , + , &[_][]const u8{ "tmp.zig:1:13: error: struct 'Foo' depends on itself", "tmp.zig:8:28: note: referenced here", - ); + }); - cases.add( - "@typeInfo causing depend on itself compile error", + cases.add("@typeInfo causing depend on itself compile error", \\const start = struct { \\ fn crash() bug() { \\ return bug; @@ -856,14 +1267,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var boom = start.crash(); \\} - , + , &[_][]const u8{ "tmp.zig:7:9: error: dependency loop detected", "tmp.zig:2:19: note: referenced here", "tmp.zig:10:21: note: referenced here", - ); + }); - cases.add( - "enum field value references enum", + cases.add("enum field value references enum", \\pub const Foo = extern enum { \\ A = Foo.B, \\ C = D, @@ -871,25 +1281,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var s: Foo = Foo.E; \\} - , + , &[_][]const u8{ "tmp.zig:1:17: error: enum 'Foo' depends on itself", - ); + }); - cases.add( - "top level decl dependency loop", - \\const a : @typeOf(b) = 0; - \\const b : @typeOf(a) = 0; + cases.add("top level decl dependency loop", + \\const a : @TypeOf(b) = 0; + \\const b : @TypeOf(a) = 0; \\export fn entry() void { \\ const c = a + b; \\} - , + , &[_][]const u8{ "tmp.zig:2:19: error: dependency loop detected", "tmp.zig:1:19: note: referenced here", "tmp.zig:4:15: note: referenced here", - ); + }); - cases.addTest( - "not an enum type", + cases.addTest("not an enum type", \\export fn entry() void { \\ var self: Error = undefined; \\ switch (self) { @@ -903,58 +1311,53 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\}; \\const InvalidToken = struct {}; \\const ExpectedVarDeclOrFn = struct {}; - , + , &[_][]const u8{ "tmp.zig:4:9: error: expected type '@TagType(Error)', found 'type'", - ); + }); - cases.addTest( - "binary OR operator on error sets", + cases.addTest("binary OR operator on error sets", \\pub const A = error.A; \\pub const AB = A | error.B; \\export fn entry() void { \\ var x: AB = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", - ); + }); if (builtin.os == builtin.Os.linux) { - cases.addTest( - "implicit dependency on libc", + cases.addTest("implicit dependency on libc", \\extern "c" fn exit(u8) void; \\export fn entry() void { \\ exit(0); \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: dependency on library c must be explicitly specified in the build command", - ); + }); - cases.addTest( - "libc headers note", + cases.addTest("libc headers note", \\const c = @cImport(@cInclude("stdio.h")); \\export fn entry() void { - \\ _ = c.printf(c"hello, world!\n"); + \\ _ = c.printf("hello, world!\n"); \\} - , + , &[_][]const u8{ "tmp.zig:1:11: error: C import failed", "tmp.zig:1:11: note: libc headers not available; compilation does not link against libc", - ); + }); } - cases.addTest( - "comptime vector overflow shows the index", + cases.addTest("comptime vector overflow shows the index", \\comptime { \\ var a: @Vector(4, u8) = [_]u8{ 1, 2, 255, 4 }; \\ var b: @Vector(4, u8) = [_]u8{ 5, 6, 1, 8 }; \\ var x = a + b; \\} - , + , &[_][]const u8{ "tmp.zig:4:15: error: operation caused overflow", "tmp.zig:4:15: note: when computing vector element at index 2", - ); + }); - cases.addTest( - "packed struct with fields of not allowed types", + cases.addTest("packed struct with fields of not allowed types", \\const A = packed struct { \\ x: anyerror, \\}; @@ -1008,7 +1411,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ A, \\ B, \\}; - , + , &[_][]const u8{ "tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", "tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits", "tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", @@ -1017,45 +1420,41 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation", "tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation", "tmp.zig:50:14: note: enum declaration does not specify an integer tag type", - ); + }); cases.addCase(x: { - var tc = cases.create( - "deduplicate undeclared identifier", + var tc = cases.create("deduplicate undeclared identifier", \\export fn a() void { \\ x += 1; \\} \\export fn b() void { \\ x += 1; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: use of undeclared identifier 'x'", - ); + }); tc.expect_exact = true; break :x tc; }); - cases.add( - "export generic function", + cases.add("export generic function", \\export fn foo(num: var) i32 { \\ return 0; \\} - , - "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'C'", + }); - cases.add( - "C pointer to c_void", + cases.add("C pointer to c_void", \\export fn a() void { \\ var x: *c_void = undefined; \\ var y: [*c]c_void = x; \\} - , + , &[_][]const u8{ "tmp.zig:3:16: error: C pointers cannot point opaque types", - ); + }); - cases.add( - "directly embedding opaque type in struct and union", + cases.add("directly embedding opaque type in struct and union", \\const O = @OpaqueType(); \\const Foo = struct { \\ o: O, @@ -1070,13 +1469,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn b() void { \\ var bar: Bar = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs", "tmp.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions", - ); + }); - cases.add( - "implicit cast between C pointer and Zig pointer - bad const/align/child", + cases.add("implicit cast between C pointer and Zig pointer - bad const/align/child", \\export fn a() void { \\ var x: [*c]u8 = undefined; \\ var y: *align(4) u8 = x; @@ -1101,7 +1499,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var y: *u8 = undefined; \\ var x: [*c]u32 = y; \\} - , + , &[_][]const u8{ "tmp.zig:3:27: error: cast increases pointer alignment", "tmp.zig:7:18: error: cast discards const qualifier", "tmp.zig:11:19: error: expected type '*u32', found '[*c]u8'", @@ -1109,30 +1507,27 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:15:22: error: cast increases pointer alignment", "tmp.zig:19:21: error: cast discards const qualifier", "tmp.zig:23:22: error: expected type '[*c]u32', found '*u8'", - ); + }); - cases.add( - "implicit casting null c pointer to zig pointer", + cases.add("implicit casting null c pointer to zig pointer", \\comptime { \\ var c_ptr: [*c]u8 = 0; \\ var zig_ptr: *u8 = c_ptr; \\} - , + , &[_][]const u8{ "tmp.zig:3:24: error: null pointer casted to type '*u8'", - ); + }); - cases.add( - "implicit casting undefined c pointer to zig pointer", + cases.add("implicit casting undefined c pointer to zig pointer", \\comptime { \\ var c_ptr: [*c]u8 = undefined; \\ var zig_ptr: *u8 = c_ptr; \\} - , + , &[_][]const u8{ "tmp.zig:3:24: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "implicit casting C pointers which would mess up null semantics", + cases.add("implicit casting C pointers which would mess up null semantics", \\export fn entry() void { \\ var slice: []const u8 = "aoeu"; \\ const opt_many_ptr: [*]const u8 = slice.ptr; @@ -1141,23 +1536,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ ptr_opt_many_ptr = c_ptr; \\} \\export fn entry2() void { - \\ var buf: [4]u8 = "aoeu"; + \\ var buf: [4]u8 = "aoeu".*; \\ var slice: []u8 = &buf; \\ var opt_many_ptr: [*]u8 = slice.ptr; \\ var ptr_opt_many_ptr = &opt_many_ptr; \\ var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr; \\} - , + , &[_][]const u8{ "tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'", "tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'", "tmp.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'", "tmp.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'", "tmp.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'", "tmp.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'", - ); + }); - cases.add( - "implicit casting too big integers to C pointers", + cases.add("implicit casting too big integers to C pointers", \\export fn a() void { \\ var ptr: [*c]u8 = (1 << 64) + 1; \\} @@ -1165,25 +1559,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: @IntType(false, 65) = 0x1234; \\ var ptr: [*c]u8 = x; \\} - , - "tmp.zig:2:33: error: integer value 18446744073709551617 cannot be implicitly casted to type 'usize'", + , &[_][]const u8{ + "tmp.zig:2:33: error: integer value 18446744073709551617 cannot be coerced to type 'usize'", "tmp.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'", - ); + }); - cases.add( - "C pointer pointing to non C ABI compatible type or has align attr", + cases.add("C pointer pointing to non C ABI compatible type or has align attr", \\const Foo = struct {}; \\export fn a() void { \\ const T = [*c]Foo; \\ var t: T = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", - ); + }); cases.addCase(x: { - var tc = cases.create( - "compile log statement warning deduplication in generic fn", + var tc = cases.create("compile log statement warning deduplication in generic fn", \\export fn entry() void { \\ inner(1); \\ inner(2); @@ -1192,111 +1584,100 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ comptime var i = 0; \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } \\} - , + , &[_][]const u8{ "tmp.zig:7:39: error: found compile log statement", - ); + }); tc.expect_exact = true; break :x tc; }); - cases.add( - "assign to invalid dereference", + cases.add("assign to invalid dereference", \\export fn entry() void { \\ 'a'.* = 1; \\} - , + , &[_][]const u8{ "tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int'", - ); + }); - cases.add( - "take slice of invalid dereference", + cases.add("take slice of invalid dereference", \\export fn entry() void { \\ const x = 'a'.*[0..]; \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int'", - ); + }); - cases.add( - "@truncate undefined value", + cases.add("@truncate undefined value", \\export fn entry() void { - \\ var z = @truncate(u8, u16(undefined)); + \\ var z = @truncate(u8, @as(u16, undefined)); \\} - , - "tmp.zig:2:30: error: use of undefined value here causes undefined behavior", - ); + , &[_][]const u8{ + "tmp.zig:2:27: error: use of undefined value here causes undefined behavior", + }); - cases.addTest( - "return invalid type from test", + cases.addTest("return invalid type from test", \\test "example" { return 1; } - , - "tmp.zig:1:25: error: integer value 1 cannot be implicitly casted to type 'void'", - ); + , &[_][]const u8{ + "tmp.zig:1:25: error: integer value 1 cannot be coerced to type 'void'", + }); - cases.add( - "threadlocal qualifier on const", + cases.add("threadlocal qualifier on const", \\threadlocal const x: i32 = 1234; \\export fn entry() i32 { \\ return x; \\} - , + , &[_][]const u8{ "tmp.zig:1:13: error: threadlocal variable cannot be constant", - ); + }); - cases.add( - "@bitCast same size but bit count mismatch", + cases.add("@bitCast same size but bit count mismatch", \\export fn entry(byte: u8) void { \\ var oops = @bitCast(u7, byte); \\} - , + , &[_][]const u8{ "tmp.zig:2:25: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", - ); + }); - cases.add( - "@bitCast with different sizes inside an expression", + cases.add("@bitCast with different sizes inside an expression", \\export fn entry() void { - \\ var foo = (@bitCast(u8, f32(1.0)) == 0xf); + \\ var foo = (@bitCast(u8, @as(f32, 1.0)) == 0xf); \\} - , + , &[_][]const u8{ "tmp.zig:2:25: error: destination type 'u8' has size 1 but source type 'f32' has size 4", - ); + }); - cases.add( - "attempted `&&`", + cases.add("attempted `&&`", \\export fn entry(a: bool, b: bool) i32 { \\ if (a && b) { \\ return 1234; \\ } \\ return 5678; \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND", - ); + }); - cases.add( - "attempted `||` on boolean values", + cases.add("attempted `||` on boolean values", \\export fn entry(a: bool, b: bool) i32 { \\ if (a || b) { \\ return 1234; \\ } \\ return 5678; \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: expected error set type, found 'bool'", "tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", - ); + }); - cases.add( - "compile log a pointer to an opaque value", + cases.add("compile log a pointer to an opaque value", \\export fn entry() void { \\ @compileLog(@ptrCast(*const c_void, &entry)); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: found compile log statement", - ); + }); - cases.add( - "duplicate boolean switch value", + cases.add("duplicate boolean switch value", \\comptime { \\ const x = switch (true) { \\ true => false, @@ -1311,13 +1692,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ false => true, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:5:9: error: duplicate switch value", "tmp.zig:12:9: error: duplicate switch value", - ); + }); - cases.add( - "missing boolean switch value", + cases.add("missing boolean switch value", \\comptime { \\ const x = switch (true) { \\ true => false, @@ -1328,37 +1708,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ false => true, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: switch must handle all possibilities", "tmp.zig:7:15: error: switch must handle all possibilities", - ); + }); - cases.add( - "reading past end of pointer casted array", + cases.add("reading past end of pointer casted array", \\comptime { - \\ const array = "aoeu"; + \\ const array: [4]u8 = "aoeu".*; \\ const slice = array[1..]; \\ const int_ptr = @ptrCast(*const u24, slice.ptr); \\ const deref = int_ptr.*; \\} - , + , &[_][]const u8{ "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", - ); + }); - cases.add( - "error note for function parameter incompatibility", + cases.add("error note for function parameter incompatibility", \\fn do_the_thing(func: fn (arg: i32) void) void {} \\fn bar(arg: bool) void {} \\export fn entry() void { \\ do_the_thing(bar); \\} - , + , &[_][]const u8{ "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", - ); + }); - cases.add( - "cast negative value to unsigned integer", + cases.add("cast negative value to unsigned integer", \\comptime { \\ const value: i32 = -1; \\ const unsigned = @intCast(u32, value); @@ -1367,13 +1744,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const value: i32 = -1; \\ const unsigned: u32 = value; \\} - , + , &[_][]const u8{ "tmp.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'", "tmp.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'", - ); + }); - cases.add( - "integer cast truncates bits", + cases.add("integer cast truncates bits", \\export fn entry1() void { \\ const spartan_count: u16 = 300; \\ const byte = @intCast(u8, spartan_count); @@ -1386,47 +1762,50 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var spartan_count: u16 = 300; \\ var byte: u8 = spartan_count; \\} - , - "tmp.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", - "tmp.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", + \\export fn entry4() void { + \\ var signed: i8 = -1; + \\ var unsigned: u64 = signed; + \\} + , &[_][]const u8{ + "tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'", + "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'", "tmp.zig:11:20: error: expected type 'u8', found 'u16'", - ); + "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values", + "tmp.zig:15:25: error: expected type 'u64', found 'i8'", + "tmp.zig:15:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values", + }); - cases.add( - "comptime implicit cast f64 to f32", + cases.add("comptime implicit cast f64 to f32", \\export fn entry() void { \\ const x: f64 = 16777217; \\ const y: f32 = x; \\} - , + , &[_][]const u8{ "tmp.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information", - ); + }); - cases.add( - "implicit cast from f64 to f32", + cases.add("implicit cast from f64 to f32", \\var x: f64 = 1.0; \\var y: f32 = x; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:2:14: error: expected type 'f32', found 'f64'", - ); + }); - cases.add( - "exceeded maximum bit width of integer", + cases.add("exceeded maximum bit width of integer", \\export fn entry1() void { \\ const T = @IntType(false, 65536); \\} \\export fn entry2() void { \\ var x: i65536 = 1; \\} - , - "tmp.zig:2:31: error: integer value 65536 cannot be implicitly casted to type 'u16'", + , &[_][]const u8{ + "tmp.zig:2:31: error: integer value 65536 cannot be coerced to type 'u16'", "tmp.zig:5:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535", - ); + }); - cases.add( - "compile error when evaluating return type of inferred error set", + cases.add("compile error when evaluating return type of inferred error set", \\const Car = struct { \\ foo: *SymbolThatDoesNotExist, \\ pub fn init() !Car {} @@ -1434,24 +1813,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const car = Car.init(); \\} - , + , &[_][]const u8{ "tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist'", - ); + }); - cases.add( - "don't implicit cast double pointer to *c_void", + cases.add("don't implicit cast double pointer to *c_void", \\export fn entry() void { \\ var a: u32 = 1; \\ var ptr: *c_void = &a; \\ var b: *u32 = @ptrCast(*u32, ptr); \\ var ptr2: *c_void = &b; \\} - , + , &[_][]const u8{ "tmp.zig:5:26: error: expected type '*c_void', found '**u32'", - ); + }); - cases.add( - "runtime index into comptime type slice", + cases.add("runtime index into comptime type slice", \\const Struct = struct { \\ a: u32, \\}; @@ -1462,12 +1839,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const index = getIndex(); \\ const field = @typeInfo(Struct).Struct.fields[index]; \\} - , - "tmp.zig:9:51: error: values of type 'builtin.StructField' must be comptime known, but index value is runtime known", - ); + , &[_][]const u8{ + "tmp.zig:9:51: error: values of type 'std.builtin.StructField' must be comptime known, but index value is runtime known", + }); - cases.add( - "compile log statement inside function which must be comptime evaluated", + cases.add("compile log statement inside function which must be comptime evaluated", \\fn Foo(comptime T: type) type { \\ @compileLog(@typeName(T)); \\ return T; @@ -1476,80 +1852,73 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = Foo(i32); \\ _ = @typeName(Foo(i32)); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: found compile log statement", - ); + }); - cases.add( - "comptime slice of an undefined slice", + cases.add("comptime slice of an undefined slice", \\comptime { \\ var a: []u8 = undefined; \\ var b = a[0..10]; \\} - , + , &[_][]const u8{ "tmp.zig:3:14: error: slice of undefined", - ); + }); - cases.add( - "implicit cast const array to mutable slice", + cases.add("implicit cast const array to mutable slice", \\export fn entry() void { \\ const buffer: [1]u8 = [_]u8{8}; \\ const sliceA: []u8 = &buffer; \\} - , + , &[_][]const u8{ "tmp.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", - ); + }); - cases.add( - "deref slice and get len field", + cases.add("deref slice and get len field", \\export fn entry() void { \\ var a: []u8 = undefined; \\ _ = a.*.len; \\} - , + , &[_][]const u8{ "tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8'", - ); + }); - cases.add( - "@ptrCast a 0 bit type to a non- 0 bit type", + cases.add("@ptrCast a 0 bit type to a non- 0 bit type", \\export fn entry() bool { \\ var x: u0 = 0; \\ const p = @ptrCast(?*u0, &x); \\ return p == null; \\} - , + , &[_][]const u8{ "tmp.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation", "tmp.zig:3:31: note: '*u0' has no in-memory bits", "tmp.zig:3:24: note: '?*u0' has in-memory bits", - ); + }); - cases.add( - "comparing a non-optional pointer against null", + cases.add("comparing a non-optional pointer against null", \\export fn entry() void { \\ var x: i32 = 1; \\ _ = &x == null; \\} - , + , &[_][]const u8{ "tmp.zig:3:12: error: comparison of '*i32' with null", - ); + }); - cases.add( - "non error sets used in merge error sets operator", + cases.add("non error sets used in merge error sets operator", \\export fn foo() void { \\ const Errors = u8 || u16; \\} \\export fn bar() void { \\ const Errors = error{} || u16; \\} - , + , &[_][]const u8{ "tmp.zig:2:20: error: expected error set type, found type 'u8'", "tmp.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", "tmp.zig:5:31: error: expected error set type, found type 'u16'", "tmp.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", - ); + }); - cases.add( - "variable initialization compile error then referenced", + cases.add("variable initialization compile error then referenced", \\fn Undeclared() type { \\ return T; \\} @@ -1562,23 +1931,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const S = Gen(); \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: use of undeclared identifier 'T'", - ); + }); - cases.add( - "refer to the type of a generic function", + cases.add("refer to the type of a generic function", \\export fn entry() void { \\ const Func = fn (type) void; \\ const f: Func = undefined; \\ f(i32); \\} - , + , &[_][]const u8{ "tmp.zig:4:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "accessing runtime parameter from outer function", + cases.add("accessing runtime parameter from outer function", \\fn outer(y: u32) fn (u32) u32 { \\ const st = struct { \\ fn get(z: u32) u32 { @@ -1591,53 +1958,48 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var func = outer(10); \\ var x = func(3); \\} - , + , &[_][]const u8{ "tmp.zig:4:24: error: 'y' not accessible from inner function", "tmp.zig:3:28: note: crossed function definition here", "tmp.zig:1:10: note: declared here", - ); + }); - cases.add( - "non int passed to @intToFloat", + cases.add("non int passed to @intToFloat", \\export fn entry() void { \\ const x = @intToFloat(f32, 1.1); \\} - , + , &[_][]const u8{ "tmp.zig:2:32: error: expected int type, found 'comptime_float'", - ); + }); - cases.add( - "non float passed to @floatToInt", + cases.add("non float passed to @floatToInt", \\export fn entry() void { - \\ const x = @floatToInt(i32, i32(54)); + \\ const x = @floatToInt(i32, @as(i32, 54)); \\} - , - "tmp.zig:2:35: error: expected float type, found 'i32'", - ); + , &[_][]const u8{ + "tmp.zig:2:32: error: expected float type, found 'i32'", + }); - cases.add( - "out of range comptime_int passed to @floatToInt", + cases.add("out of range comptime_int passed to @floatToInt", \\export fn entry() void { \\ const x = @floatToInt(i8, 200); \\} - , - "tmp.zig:2:31: error: integer value 200 cannot be implicitly casted to type 'i8'", - ); + , &[_][]const u8{ + "tmp.zig:2:31: error: integer value 200 cannot be coerced to type 'i8'", + }); - cases.add( - "load too many bytes from comptime reinterpreted pointer", + cases.add("load too many bytes from comptime reinterpreted pointer", \\export fn entry() void { \\ const float: f32 = 5.99999999999994648725e-01; \\ const float_ptr = &float; \\ const int_ptr = @ptrCast(*const i64, float_ptr); \\ const int_val = int_ptr.*; \\} - , + , &[_][]const u8{ "tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes", - ); + }); - cases.add( - "invalid type used in array type", + cases.add("invalid type used in array type", \\const Item = struct { \\ field: SomeNonexistentType, \\}; @@ -1645,23 +2007,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const a = items[0]; \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", - ); + }); - cases.add( - "@noInlineCall on an inline function", - \\inline fn foo() void {} - \\ - \\export fn entry() void { - \\ @noInlineCall(foo); - \\} - , - "tmp.zig:4:5: error: no-inline call of inline function", - ); - - cases.add( - "comptime continue inside runtime catch", + cases.add("comptime continue inside runtime catch", \\export fn entry(c: bool) void { \\ const ints = [_]u8{ 1, 2 }; \\ inline for (ints) |_| { @@ -1671,13 +2021,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bad() !void { \\ return error.Bad; \\} - , + , &[_][]const u8{ "tmp.zig:4:25: error: comptime control flow inside runtime block", "tmp.zig:4:15: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime switch", + cases.add("comptime continue inside runtime switch", \\export fn entry() void { \\ var p: i32 = undefined; \\ comptime var q = true; @@ -1689,13 +2038,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:6:19: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime while error", + cases.add("comptime continue inside runtime while error", \\export fn entry() void { \\ var p: anyerror!usize = undefined; \\ comptime var q = true; @@ -1706,13 +2054,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:6:13: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime while optional", + cases.add("comptime continue inside runtime while optional", \\export fn entry() void { \\ var p: ?usize = undefined; \\ comptime var q = true; @@ -1721,13 +2068,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:23: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime while bool", + cases.add("comptime continue inside runtime while bool", \\export fn entry() void { \\ var p: usize = undefined; \\ comptime var q = true; @@ -1736,13 +2082,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:25: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime if error", + cases.add("comptime continue inside runtime if error", \\export fn entry() void { \\ var p: anyerror!i32 = undefined; \\ comptime var q = true; @@ -1751,13 +2096,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:20: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime if optional", + cases.add("comptime continue inside runtime if optional", \\export fn entry() void { \\ var p: ?i32 = undefined; \\ comptime var q = true; @@ -1766,13 +2110,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:20: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "comptime continue inside runtime if bool", + cases.add("comptime continue inside runtime if bool", \\export fn entry() void { \\ var p: usize = undefined; \\ comptime var q = true; @@ -1781,13 +2124,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ q = false; \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:22: error: comptime control flow inside runtime block", "tmp.zig:5:9: note: runtime block created here", - ); + }); - cases.add( - "switch with invalid expression parameter", + cases.add("switch with invalid expression parameter", \\export fn entry() void { \\ Test(i32); \\} @@ -1798,43 +2140,39 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ else => unreachable, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:7:17: error: switch on type 'type' provides no expression parameter", - ); + }); - cases.add( - "function protoype with no body", + cases.add("function protoype with no body", \\fn foo() void; \\export fn entry() void { \\ foo(); \\} - , + , &[_][]const u8{ "tmp.zig:1:1: error: non-extern function has no body", - ); + }); - cases.add( - "@frame() called outside of function definition", + cases.add("@frame() called outside of function definition", \\var handle_undef: anyframe = undefined; \\var handle_dummy: anyframe = @frame(); \\export fn entry() bool { \\ return handle_undef == handle_dummy; \\} - , + , &[_][]const u8{ "tmp.zig:2:30: error: @frame() called outside of function definition", - ); + }); - cases.add( - "`_` is not a declarable symbol", + cases.add("`_` is not a declarable symbol", \\export fn f1() usize { \\ var _: usize = 2; \\ return _; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: `_` is not a declarable symbol", - ); + }); - cases.add( - "`_` should not be usable inside for", + cases.add("`_` should not be usable inside for", \\export fn returns() void { \\ for ([_]void{}) |_, i| { \\ for ([_]void{}) |_, j| { @@ -1842,12 +2180,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:4:20: error: `_` may only be used to assign things to", - ); + }); - cases.add( - "`_` should not be usable inside while", + cases.add("`_` should not be usable inside while", \\export fn returns() void { \\ while (optionalReturn()) |_| { \\ while (optionalReturn()) |_| { @@ -1858,12 +2195,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn optionalReturn() ?u32 { \\ return 1; \\} - , + , &[_][]const u8{ "tmp.zig:4:20: error: `_` may only be used to assign things to", - ); + }); - cases.add( - "`_` should not be usable inside while else", + cases.add("`_` should not be usable inside while else", \\export fn returns() void { \\ while (optionalReturnError()) |_| { \\ while (optionalReturnError()) |_| { @@ -1876,12 +2212,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn optionalReturnError() !?u32 { \\ return error.optionalReturnError; \\} - , + , &[_][]const u8{ "tmp.zig:6:17: error: `_` may only be used to assign things to", - ); + }); - cases.add( - "while loop body expression ignored", + cases.add("while loop body expression ignored", \\fn returns() usize { \\ return 2; \\} @@ -1896,25 +2231,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: anyerror!i32 = error.Bad; \\ while (x) |_| returns() else |_| unreachable; \\} - , + , &[_][]const u8{ "tmp.zig:5:25: error: expression value is ignored", "tmp.zig:9:26: error: expression value is ignored", "tmp.zig:13:26: error: expression value is ignored", - ); + }); - cases.add( - "missing parameter name of generic function", + cases.add("missing parameter name of generic function", \\fn dump(var) void {} \\export fn entry() void { \\ var a: u8 = 9; \\ dump(a); \\} - , + , &[_][]const u8{ "tmp.zig:1:9: error: missing parameter name", - ); + }); - cases.add( - "non-inline for loop on a type that requires comptime", + cases.add("non-inline for loop on a type that requires comptime", \\const Foo = struct { \\ name: []const u8, \\ T: type, @@ -1923,23 +2256,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const xx: [2]Foo = undefined; \\ for (xx) |f| {} \\} - , + , &[_][]const u8{ "tmp.zig:7:5: error: values of type 'Foo' must be comptime known, but index value is runtime known", - ); + }); - cases.add( - "generic fn as parameter without comptime keyword", + cases.add("generic fn as parameter without comptime keyword", \\fn f(_: fn (var) void) void {} \\fn g(_: var) void {} \\export fn entry() void { \\ f(g); \\} - , - "tmp.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", - ); + , &[_][]const u8{ + "tmp.zig:1:9: error: parameter of type 'fn(var) var' must be declared comptime", + }); - cases.add( - "optional pointer to void in extern struct", + cases.add("optional pointer to void in extern struct", \\const Foo = extern struct { \\ x: ?*const void, \\}; @@ -1948,12 +2279,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ y: i32, \\}; \\export fn entry(bar: *Bar) void {} - , + , &[_][]const u8{ "tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", - ); + }); - cases.add( - "use of comptime-known undefined function value", + cases.add("use of comptime-known undefined function value", \\const Cmd = struct { \\ exec: fn () void, \\}; @@ -1961,12 +2291,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const command = Cmd{ .exec = undefined }; \\ command.exec(); \\} - , + , &[_][]const u8{ "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "use of comptime-known undefined function value", + cases.add("use of comptime-known undefined function value", \\const Cmd = struct { \\ exec: fn () void, \\}; @@ -1974,41 +2303,37 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const command = Cmd{ .exec = undefined }; \\ command.exec(); \\} - , + , &[_][]const u8{ "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bad @alignCast at comptime", + cases.add("bad @alignCast at comptime", \\comptime { - \\ const ptr = @intToPtr(*i32, 0x1); + \\ const ptr = @intToPtr(*align(1) i32, 0x1); \\ const aligned = @alignCast(4, ptr); \\} - , + , &[_][]const u8{ "tmp.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", - ); + }); - cases.add( - "@ptrToInt on *void", + cases.add("@ptrToInt on *void", \\export fn entry() bool { \\ return @ptrToInt(&{}) == @ptrToInt(&{}); \\} - , + , &[_][]const u8{ "tmp.zig:2:23: error: pointer to size 0 type has no address", - ); + }); - cases.add( - "@popCount - non-integer", + cases.add("@popCount - non-integer", \\export fn entry(x: f32) u32 { \\ return @popCount(f32, x); \\} - , + , &[_][]const u8{ "tmp.zig:2:22: error: expected integer type, found 'f32'", - ); + }); cases.addCase(x: { - const tc = cases.create( - "wrong same named struct", + const tc = cases.create("wrong same named struct", \\const a = @import("a.zig"); \\const b = @import("b.zig"); \\ @@ -2018,12 +2343,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\ \\fn bar(x: *b.Foo) void {} - , + , &[_][]const u8{ "tmp.zig:6:10: error: expected type '*b.Foo', found '*a.Foo'", "tmp.zig:6:10: note: pointer type child 'a.Foo' cannot cast into pointer type child 'b.Foo'", "a.zig:1:17: note: a.Foo declared here", "b.zig:1:17: note: b.Foo declared here", - ); + }); tc.addSourceFile("a.zig", \\pub const Foo = struct { @@ -2040,34 +2365,31 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "@floatToInt comptime safety", + cases.add("@floatToInt comptime safety", \\comptime { - \\ _ = @floatToInt(i8, f32(-129.1)); + \\ _ = @floatToInt(i8, @as(f32, -129.1)); \\} \\comptime { - \\ _ = @floatToInt(u8, f32(-1.1)); + \\ _ = @floatToInt(u8, @as(f32, -1.1)); \\} \\comptime { - \\ _ = @floatToInt(u8, f32(256.1)); + \\ _ = @floatToInt(u8, @as(f32, 256.1)); \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", "tmp.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", "tmp.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", - ); + }); - cases.add( - "use c_void as return type of fn ptr", + cases.add("use c_void as return type of fn ptr", \\export fn entry() void { \\ const a: fn () c_void = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:20: error: return type cannot be opaque", - ); + }); - cases.add( - "use implicit casts to assign null to non-nullable pointer", + cases.add("use implicit casts to assign null to non-nullable pointer", \\export fn entry() void { \\ var x: i32 = 1234; \\ var p: *i32 = &x; @@ -2075,30 +2397,27 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ pp.* = null; \\ var y = p.*; \\} - , + , &[_][]const u8{ "tmp.zig:4:23: error: expected type '*?*i32', found '**i32'", - ); + }); - cases.add( - "attempted implicit cast from T to [*]const T", + cases.add("attempted implicit cast from T to [*]const T", \\export fn entry() void { \\ const x: [*]const bool = true; \\} - , + , &[_][]const u8{ "tmp.zig:2:30: error: expected type '[*]const bool', found 'bool'", - ); + }); - cases.add( - "dereference unknown length pointer", + cases.add("dereference unknown length pointer", \\export fn entry(x: [*]i32) i32 { \\ return x.*; \\} - , + , &[_][]const u8{ "tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", - ); + }); - cases.add( - "field access of unknown length pointer", + cases.add("field access of unknown length pointer", \\const Foo = extern struct { \\ a: i32, \\}; @@ -2106,21 +2425,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry(foo: [*]Foo) void { \\ foo.a += 1; \\} - , + , &[_][]const u8{ "tmp.zig:6:8: error: type '[*]Foo' does not support field access", - ); + }); - cases.add( - "unknown length pointer to opaque", + cases.add("unknown length pointer to opaque", \\export const T = [*]@OpaqueType(); - , + , &[_][]const u8{ "tmp.zig:1:21: error: unknown-length pointer to opaque", - ); + }); - cases.add( - "error when evaluating return type", + cases.add("error when evaluating return type", \\const Foo = struct { - \\ map: i32(i32), + \\ map: @as(i32, i32), \\ \\ fn init() Foo { \\ return undefined; @@ -2129,30 +2446,27 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var rule_set = try Foo.init(); \\} - , - "tmp.zig:2:13: error: expected type 'i32', found 'type'", - ); + , &[_][]const u8{ + "tmp.zig:2:10: error: expected type 'i32', found 'type'", + }); - cases.add( - "slicing single-item pointer", + cases.add("slicing single-item pointer", \\export fn entry(ptr: *i32) void { \\ const slice = ptr[0..2]; \\} - , + , &[_][]const u8{ "tmp.zig:2:22: error: slice of single-item pointer", - ); + }); - cases.add( - "indexing single-item pointer", + cases.add("indexing single-item pointer", \\export fn entry(ptr: *i32) i32 { \\ return ptr[1]; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: index of single-item pointer", - ); + }); - cases.add( - "nested error set mismatch", + cases.add("nested error set mismatch", \\const NextError = error{NextError}; \\const OtherError = error{OutOfMemory}; \\ @@ -2163,15 +2477,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() ?OtherError!i32 { \\ return null; \\} - , + , &[_][]const u8{ "tmp.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", "tmp.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", "tmp.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", "tmp.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", - ); + }); - cases.add( - "invalid deref on switch target", + cases.add("invalid deref on switch target", \\comptime { \\ var tile = Tile.Empty; \\ switch (tile.*) { @@ -2183,19 +2496,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ Empty, \\ Filled, \\}; - , + , &[_][]const u8{ "tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile'", - ); + }); - cases.add( - "invalid field access in comptime", + cases.add("invalid field access in comptime", \\comptime { var x = doesnt_exist.whatever; } - , + , &[_][]const u8{ "tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist'", - ); + }); - cases.add( - "suspend inside suspend block", + cases.add("suspend inside suspend block", \\export fn entry() void { \\ _ = async foo(); \\} @@ -2205,34 +2516,31 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:6:9: error: cannot suspend inside suspend block", "tmp.zig:5:5: note: other suspend block here", - ); + }); - cases.add( - "assign inline fn to non-comptime var", + cases.add("assign inline fn to non-comptime var", \\export fn entry() void { \\ var a = b; \\} \\inline fn b() void { } - , + , &[_][]const u8{ "tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var", "tmp.zig:4:1: note: declared here", - ); + }); - cases.add( - "wrong type passed to @panic", + cases.add("wrong type passed to @panic", \\export fn entry() void { \\ var e = error.Foo; \\ @panic(e); \\} - , + , &[_][]const u8{ "tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", - ); + }); - cases.add( - "@tagName used on union with no associated enum tag", + cases.add("@tagName used on union with no associated enum tag", \\const FloatInt = extern union { \\ Float: f32, \\ Int: i32, @@ -2241,68 +2549,61 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var fi = FloatInt{.Float = 123.45}; \\ var tagName = @tagName(fi); \\} - , + , &[_][]const u8{ "tmp.zig:7:19: error: union has no associated enum", "tmp.zig:1:18: note: declared here", - ); + }); - cases.add( - "returning error from void async function", + cases.add("returning error from void async function", \\export fn entry() void { \\ _ = async amain(); \\} \\async fn amain() void { \\ return error.ShouldBeCompileError; \\} - , + , &[_][]const u8{ "tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", - ); + }); - cases.add( - "var not allowed in structs", + cases.add("var makes structs required to be comptime known", \\export fn entry() void { - \\ var s = (struct{v: var}){.v=i32(10)}; + \\ const S = struct{v: var}; + \\ var s = S{.v=@as(i32, 10)}; \\} - , - "tmp.zig:2:23: error: invalid token: 'var'", - ); + , &[_][]const u8{ + "tmp.zig:3:4: error: variable of type 'S' must be const or comptime", + }); - cases.add( - "@ptrCast discards const qualifier", + cases.add("@ptrCast discards const qualifier", \\export fn entry() void { \\ const x: i32 = 1234; \\ const y = @ptrCast(*i32, &x); \\} - , + , &[_][]const u8{ "tmp.zig:3:15: error: cast discards const qualifier", - ); + }); - cases.add( - "comptime slice of undefined pointer non-zero len", + cases.add("comptime slice of undefined pointer non-zero len", \\export fn entry() void { - \\ const slice = ([*]i32)(undefined)[0..1]; + \\ const slice = @as([*]i32, undefined)[0..1]; \\} - , - "tmp.zig:2:38: error: non-zero length slice of undefined pointer", - ); + , &[_][]const u8{ + "tmp.zig:2:41: error: non-zero length slice of undefined pointer", + }); - cases.add( - "type checking function pointers", + cases.add("type checking function pointers", \\fn a(b: fn (*const u8) void) void { \\ b('a'); \\} - \\fn c(d: u8) void { - \\ @import("std").debug.warn("{c}\n", d); - \\} + \\fn c(d: u8) void {} \\export fn entry() void { \\ a(c); \\} - , - "tmp.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", - ); + , &[_][]const u8{ + "tmp.zig:6:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", + }); - cases.add( - "no else prong on switch on global error set", + cases.add("no else prong on switch on global error set", \\export fn entry() void { \\ foo(error.A); \\} @@ -2311,23 +2612,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ error.A => {}, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: else prong required when switching on type 'anyerror'", - ); + }); - cases.add( - "inferred error set with no returned error", + cases.add("inferred error set with no returned error", \\export fn entry() void { \\ foo() catch unreachable; \\} \\fn foo() !void { \\} - , + , &[_][]const u8{ "tmp.zig:4:11: error: function with inferred error set must return at least one possible error", - ); + }); - cases.add( - "error not handled in switch", + cases.add("error not handled in switch", \\export fn entry() void { \\ foo(452) catch |err| switch (err) { \\ error.Foo => {}, @@ -2341,13 +2640,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ else => {}, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:2:26: error: error.Baz not handled in switch", "tmp.zig:2:26: error: error.Bar not handled in switch", - ); + }); - cases.add( - "duplicate error in switch", + cases.add("duplicate error in switch", \\export fn entry() void { \\ foo(452) catch |err| switch (err) { \\ error.Foo => {}, @@ -2363,10 +2661,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ else => {}, \\ } \\} - , - "tmp.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'", + , &[_][]const u8{ + "tmp.zig:5:14: error: duplicate switch value: '@TypeOf(foo).ReturnType.ErrorSet.Foo'", "tmp.zig:3:14: note: other value is here", - ); + }); cases.add("invalid cast from integral type to enum", \\const E = enum(usize) { One, Two }; @@ -2380,10 +2678,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , "tmp.zig:9:10: error: expected type 'usize', found 'E'"); + , &[_][]const u8{ + "tmp.zig:9:10: error: expected type 'usize', found 'E'", + }); - cases.add( - "range operator in switch used on error set", + cases.add("range operator in switch used on error set", \\export fn entry() void { \\ try foo(452) catch |err| switch (err) { \\ error.A ... error.B => {}, @@ -2397,41 +2696,37 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ else => {}, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:3:17: error: operator not allowed for errors", - ); + }); - cases.add( - "inferring error set of function pointer", + cases.add("inferring error set of function pointer", \\comptime { \\ const z: ?fn()!void = null; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: inferring error set of return type valid only for function definitions", - ); + }); - cases.add( - "access non-existent member of error set", + cases.add("access non-existent member of error set", \\const Foo = error{A}; \\comptime { \\ const z = Foo.Bar; \\} - , + , &[_][]const u8{ "tmp.zig:3:18: error: no error named 'Bar' in 'Foo'", - ); + }); - cases.add( - "error union operator with non error set LHS", + cases.add("error union operator with non error set LHS", \\comptime { \\ const z = i32!i32; \\ var x: z = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected error set type, found type 'i32'", - ); + }); - cases.add( - "error equality but sets have no common members", + cases.add("error equality but sets have no common members", \\const Set1 = error{A, C}; \\const Set2 = error{B, D}; \\export fn entry() void { @@ -2442,33 +2737,30 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\ } \\} - , + , &[_][]const u8{ "tmp.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors", - ); + }); - cases.add( - "only equality binary operator allowed for error sets", + cases.add("only equality binary operator allowed for error sets", \\comptime { \\ const z = error.A > error.B; \\} - , + , &[_][]const u8{ "tmp.zig:2:23: error: operator not allowed for errors", - ); + }); - cases.add( - "explicit error set cast known at comptime violates error sets", + cases.add("explicit error set cast known at comptime violates error sets", \\const Set1 = error {A, B}; \\const Set2 = error {A, C}; \\comptime { \\ var x = Set1.B; \\ var y = @errSetCast(Set2, x); \\} - , + , &[_][]const u8{ "tmp.zig:5:13: error: error.B not a member of error set 'Set2'", - ); + }); - cases.add( - "cast error union of global error set to error union of smaller error set", + cases.add("cast error union of global error set to error union of smaller error set", \\const SmallErrorSet = error{A}; \\export fn entry() void { \\ var x: SmallErrorSet!i32 = foo(); @@ -2476,14 +2768,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() anyerror!i32 { \\ return error.B; \\} - , + , &[_][]const u8{ "tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", "tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", "tmp.zig:3:35: note: cannot cast global error set into smaller set", - ); + }); - cases.add( - "cast global error set to error set", + cases.add("cast global error set to error set", \\const SmallErrorSet = error{A}; \\export fn entry() void { \\ var x: SmallErrorSet = foo(); @@ -2491,24 +2782,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() anyerror { \\ return error.B; \\} - , + , &[_][]const u8{ "tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", "tmp.zig:3:31: note: cannot cast global error set into smaller set", - ); - cases.add( - "recursive inferred error set", + }); + cases.add("recursive inferred error set", \\export fn entry() void { \\ foo() catch unreachable; \\} \\fn foo() !void { \\ try foo(); \\} - , - "tmp.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet", - ); + , &[_][]const u8{ + "tmp.zig:5:5: error: cannot resolve inferred error set '@TypeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet", + }); - cases.add( - "implicit cast of error set not a subset", + cases.add("implicit cast of error set not a subset", \\const Set1 = error{A, B}; \\const Set2 = error{A, C}; \\export fn entry() void { @@ -2517,13 +2806,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo(set1: Set1) void { \\ var x: Set2 = set1; \\} - , + , &[_][]const u8{ "tmp.zig:7:19: error: expected type 'Set2', found 'Set1'", "tmp.zig:1:23: note: 'error.B' not a member of destination error set", - ); + }); - cases.add( - "int to err global invalid number", + cases.add("int to err global invalid number", \\const Set1 = error{ \\ A, \\ B, @@ -2532,12 +2820,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u16 = 3; \\ var y = @intToError(x); \\} - , + , &[_][]const u8{ "tmp.zig:7:13: error: integer value 3 represents no error", - ); + }); - cases.add( - "int to err non global invalid number", + cases.add("int to err non global invalid number", \\const Set1 = error{ \\ A, \\ B, @@ -2550,21 +2837,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = @errorToInt(Set1.B); \\ var y = @errSetCast(Set2, @intToError(x)); \\} - , + , &[_][]const u8{ "tmp.zig:11:13: error: error.B not a member of error set 'Set2'", - ); + }); - cases.add( - "@memberCount of error", + cases.add("@memberCount of error", \\comptime { \\ _ = @memberCount(anyerror); \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: global error set member count not available at comptime", - ); + }); - cases.add( - "duplicate error value in error set", + cases.add("duplicate error value in error set", \\const Foo = error { \\ Bar, \\ Bar, @@ -2572,32 +2857,29 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const a: Foo = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: duplicate error: 'Bar'", "tmp.zig:2:5: note: other error here", - ); + }); - cases.add( - "cast negative integer literal to usize", + cases.add("cast negative integer literal to usize", \\export fn entry() void { - \\ const x = usize(-10); + \\ const x = @as(usize, -10); \\} - , - "tmp.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'", - ); + , &[_][]const u8{ + "tmp.zig:2:26: error: cannot cast negative value -10 to unsigned integer type 'usize'", + }); - cases.add( - "use invalid number literal as array index", + cases.add("use invalid number literal as array index", \\var v = 25; \\export fn entry() void { \\ var arr: [v]u8 = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:1:1: error: unable to infer variable type", - ); + }); - cases.add( - "duplicate struct field", + cases.add("duplicate struct field", \\const Foo = struct { \\ Bar: i32, \\ Bar: usize, @@ -2605,13 +2887,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const a: Foo = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: duplicate struct field: 'Bar'", "tmp.zig:2:5: note: other field here", - ); + }); - cases.add( - "duplicate union field", + cases.add("duplicate union field", \\const Foo = union { \\ Bar: i32, \\ Bar: usize, @@ -2619,13 +2900,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const a: Foo = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: duplicate union field: 'Bar'", "tmp.zig:2:5: note: other field here", - ); + }); - cases.add( - "duplicate enum field", + cases.add("duplicate enum field", \\const Foo = enum { \\ Bar, \\ Bar, @@ -2634,110 +2914,99 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ const a: Foo = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: duplicate enum field: 'Bar'", "tmp.zig:2:5: note: other field here", - ); + }); - cases.add( - "calling function with naked calling convention", + cases.add("calling function with naked calling convention", \\export fn entry() void { \\ foo(); \\} - \\nakedcc fn foo() void { } - , + \\fn foo() callconv(.Naked) void { } + , &[_][]const u8{ "tmp.zig:2:5: error: unable to call function with naked calling convention", "tmp.zig:4:1: note: declared here", - ); + }); - cases.add( - "function with invalid return type", + cases.add("function with invalid return type", \\export fn foo() boid {} - , + , &[_][]const u8{ "tmp.zig:1:17: error: use of undeclared identifier 'boid'", - ); + }); - cases.add( - "function with non-extern non-packed enum parameter", + cases.add("function with non-extern non-packed enum parameter", \\const Foo = enum { A, B, C }; \\export fn entry(foo: Foo) void { } - , - "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", + }); - cases.add( - "function with non-extern non-packed struct parameter", + cases.add("function with non-extern non-packed struct parameter", \\const Foo = struct { \\ A: i32, \\ B: f32, \\ C: bool, \\}; \\export fn entry(foo: Foo) void { } - , - "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", + }); - cases.add( - "function with non-extern non-packed union parameter", + cases.add("function with non-extern non-packed union parameter", \\const Foo = union { \\ A: i32, \\ B: f32, \\ C: bool, \\}; \\export fn entry(foo: Foo) void { } - , - "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", + }); - cases.add( - "switch on enum with 1 field with no prongs", + cases.add("switch on enum with 1 field with no prongs", \\const Foo = enum { M }; \\ \\export fn entry() void { \\ var f = Foo.M; \\ switch (f) {} \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch", - ); + }); - cases.add( - "shift by negative comptime integer", + cases.add("shift by negative comptime integer", \\comptime { \\ var a = 1 >> -1; \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: shift by negative value -1", - ); + }); - cases.add( - "@panic called at compile time", + cases.add("@panic called at compile time", \\export fn entry() void { \\ comptime { \\ @panic("aoeu",); \\ } \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: encountered @panic at compile-time", - ); + }); - cases.add( - "wrong return type for main", + cases.add("wrong return type for main", \\pub fn main() f32 { } - , + , &[_][]const u8{ "error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'", - ); + }); - cases.add( - "double ?? on main return value", + cases.add("double ?? on main return value", \\pub fn main() ??void { \\} - , + , &[_][]const u8{ "error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'", - ); + }); - cases.add( - "bad identifier in function with struct defined inside function which references local const", + cases.add("bad identifier in function with struct defined inside function which references local const", \\export fn entry() void { \\ const BlockKind = u32; \\ @@ -2747,12 +3016,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\ bogus; \\} - , + , &[_][]const u8{ "tmp.zig:8:5: error: use of undeclared identifier 'bogus'", - ); + }); - cases.add( - "labeled break not found", + cases.add("labeled break not found", \\export fn entry() void { \\ blah: while (true) { \\ while (true) { @@ -2760,12 +3028,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:4:13: error: label not found: 'outer'", - ); + }); - cases.add( - "labeled continue not found", + cases.add("labeled continue not found", \\export fn entry() void { \\ var i: usize = 0; \\ blah: while (i < 10) : (i += 1) { @@ -2774,492 +3041,446 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:5:13: error: labeled loop not found: 'outer'", - ); + }); - cases.add( - "attempt to use 0 bit type in extern fn", + cases.add("attempt to use 0 bit type in extern fn", \\extern fn foo(ptr: extern fn(*void) void) void; \\ \\export fn entry() void { \\ foo(bar); \\} \\ - \\extern fn bar(x: *void) void { } + \\fn bar(x: *void) callconv(.C) void { } \\export fn entry2() void { \\ bar(&{}); \\} - , - "tmp.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", - "tmp.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'", + "tmp.zig:7:11: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'", + }); - cases.add( - "implicit semicolon - block statement", + cases.add("implicit semicolon - block statement", \\export fn entry() void { \\ {} \\ var good = {}; \\ ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - block expr", + cases.add("implicit semicolon - block expr", \\export fn entry() void { \\ _ = {}; \\ var good = {}; \\ _ = {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - comptime statement", + cases.add("implicit semicolon - comptime statement", \\export fn entry() void { \\ comptime {} \\ var good = {}; \\ comptime ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - comptime expression", + cases.add("implicit semicolon - comptime expression", \\export fn entry() void { \\ _ = comptime {}; \\ var good = {}; \\ _ = comptime {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - defer", + cases.add("implicit semicolon - defer", \\export fn entry() void { \\ defer {} \\ var good = {}; \\ defer ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if statement", + cases.add("implicit semicolon - if statement", \\export fn entry() void { \\ if(true) {} \\ var good = {}; \\ if(true) ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if expression", + cases.add("implicit semicolon - if expression", \\export fn entry() void { \\ _ = if(true) {}; \\ var good = {}; \\ _ = if(true) {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else statement", + cases.add("implicit semicolon - if-else statement", \\export fn entry() void { \\ if(true) {} else {} \\ var good = {}; \\ if(true) ({}) else ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else expression", + cases.add("implicit semicolon - if-else expression", \\export fn entry() void { \\ _ = if(true) {} else {}; \\ var good = {}; \\ _ = if(true) {} else {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else-if statement", + cases.add("implicit semicolon - if-else-if statement", \\export fn entry() void { \\ if(true) {} else if(true) {} \\ var good = {}; \\ if(true) ({}) else if(true) ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else-if expression", + cases.add("implicit semicolon - if-else-if expression", \\export fn entry() void { \\ _ = if(true) {} else if(true) {}; \\ var good = {}; \\ _ = if(true) {} else if(true) {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else-if-else statement", + cases.add("implicit semicolon - if-else-if-else statement", \\export fn entry() void { \\ if(true) {} else if(true) {} else {} \\ var good = {}; \\ if(true) ({}) else if(true) ({}) else ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - if-else-if-else expression", + cases.add("implicit semicolon - if-else-if-else expression", \\export fn entry() void { \\ _ = if(true) {} else if(true) {} else {}; \\ var good = {}; \\ _ = if(true) {} else if(true) {} else {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - test statement", + cases.add("implicit semicolon - test statement", \\export fn entry() void { \\ if (foo()) |_| {} \\ var good = {}; \\ if (foo()) |_| ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - test expression", + cases.add("implicit semicolon - test expression", \\export fn entry() void { \\ _ = if (foo()) |_| {}; \\ var good = {}; \\ _ = if (foo()) |_| {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - while statement", + cases.add("implicit semicolon - while statement", \\export fn entry() void { \\ while(true) {} \\ var good = {}; \\ while(true) ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - while expression", + cases.add("implicit semicolon - while expression", \\export fn entry() void { \\ _ = while(true) {}; \\ var good = {}; \\ _ = while(true) {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - while-continue statement", + cases.add("implicit semicolon - while-continue statement", \\export fn entry() void { \\ while(true):({}) {} \\ var good = {}; \\ while(true):({}) ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - while-continue expression", + cases.add("implicit semicolon - while-continue expression", \\export fn entry() void { \\ _ = while(true):({}) {}; \\ var good = {}; \\ _ = while(true):({}) {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - for statement", + cases.add("implicit semicolon - for statement", \\export fn entry() void { \\ for(foo()) |_| {} \\ var good = {}; \\ for(foo()) |_| ({}) \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "implicit semicolon - for expression", + cases.add("implicit semicolon - for expression", \\export fn entry() void { \\ _ = for(foo()) |_| {}; \\ var good = {}; \\ _ = for(foo()) |_| {} \\ var bad = {}; \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: expected token ';', found 'var'", - ); + }); - cases.add( - "multiple function definitions", + cases.add("multiple function definitions", \\fn a() void {} \\fn a() void {} \\export fn entry() void { a(); } - , + , &[_][]const u8{ "tmp.zig:2:1: error: redefinition of 'a'", - ); + }); - cases.add( - "unreachable with return", + cases.add("unreachable with return", \\fn a() noreturn {return;} \\export fn entry() void { a(); } - , + , &[_][]const u8{ "tmp.zig:1:18: error: expected type 'noreturn', found 'void'", - ); + }); - cases.add( - "control reaches end of non-void function", + cases.add("control reaches end of non-void function", \\fn a() i32 {} \\export fn entry() void { _ = a(); } - , + , &[_][]const u8{ "tmp.zig:1:12: error: expected type 'i32', found 'void'", - ); + }); - cases.add( - "undefined function call", + cases.add("undefined function call", \\export fn a() void { \\ b(); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: use of undeclared identifier 'b'", - ); + }); - cases.add( - "wrong number of arguments", + cases.add("wrong number of arguments", \\export fn a() void { \\ b(1); \\} \\fn b(a: i32, b: i32, c: i32) void { } - , + , &[_][]const u8{ "tmp.zig:2:6: error: expected 3 arguments, found 1", - ); + }); - cases.add( - "invalid type", + cases.add("invalid type", \\fn a() bogus {} \\export fn entry() void { _ = a(); } - , + , &[_][]const u8{ "tmp.zig:1:8: error: use of undeclared identifier 'bogus'", - ); + }); - cases.add( - "pointer to noreturn", + cases.add("pointer to noreturn", \\fn a() *noreturn {} \\export fn entry() void { _ = a(); } - , + , &[_][]const u8{ "tmp.zig:1:9: error: pointer to noreturn not allowed", - ); + }); - cases.add( - "unreachable code", + cases.add("unreachable code", \\export fn a() void { \\ return; \\ b(); \\} \\ \\fn b() void {} - , + , &[_][]const u8{ "tmp.zig:3:6: error: unreachable code", - ); + }); - cases.add( - "bad import", + cases.add("bad import", \\const bogus = @import("bogus-does-not-exist.zig",); \\export fn entry() void { bogus.bogo(); } - , + , &[_][]const u8{ "tmp.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'", - ); + }); - cases.add( - "undeclared identifier", + cases.add("undeclared identifier", \\export fn a() void { \\ return \\ b + \\ c; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undeclared identifier 'b'", - ); + }); - cases.add( - "parameter redeclaration", + cases.add("parameter redeclaration", \\fn f(a : i32, a : i32) void { \\} \\export fn entry() void { f(1, 2); } - , + , &[_][]const u8{ "tmp.zig:1:15: error: redeclaration of variable 'a'", - ); + }); - cases.add( - "local variable redeclaration", + cases.add("local variable redeclaration", \\export fn f() void { \\ const a : i32 = 0; \\ const a = 0; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: redeclaration of variable 'a'", - ); + }); - cases.add( - "local variable redeclares parameter", + cases.add("local variable redeclares parameter", \\fn f(a : i32) void { \\ const a = 0; \\} \\export fn entry() void { f(1); } - , + , &[_][]const u8{ "tmp.zig:2:5: error: redeclaration of variable 'a'", - ); + }); - cases.add( - "variable has wrong type", + cases.add("variable has wrong type", \\export fn f() i32 { - \\ const a = c"a"; + \\ const a = "a"; \\ return a; \\} - , - "tmp.zig:3:12: error: expected type 'i32', found '[*]const u8'", - ); + , &[_][]const u8{ + "tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8'", + }); - cases.add( - "if condition is bool, not int", + cases.add("if condition is bool, not int", \\export fn f() void { \\ if (0) {} \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: expected type 'bool', found 'comptime_int'", - ); + }); - cases.add( - "assign unreachable", + cases.add("assign unreachable", \\export fn f() void { \\ const a = return; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: unreachable code", - ); + }); - cases.add( - "unreachable variable", + cases.add("unreachable variable", \\export fn f() void { \\ const a: noreturn = {}; \\} - , + , &[_][]const u8{ "tmp.zig:2:25: error: expected type 'noreturn', found 'void'", - ); + }); - cases.add( - "unreachable parameter", + cases.add("unreachable parameter", \\fn f(a: noreturn) void {} \\export fn entry() void { f(); } - , + , &[_][]const u8{ "tmp.zig:1:9: error: parameter of type 'noreturn' not allowed", - ); + }); - cases.add( - "bad assignment target", + cases.add("bad assignment target", \\export fn f() void { \\ 3 = 3; \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: cannot assign to constant", - ); + }); - cases.add( - "assign to constant variable", + cases.add("assign to constant variable", \\export fn f() void { \\ const a = 3; \\ a = 4; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: cannot assign to constant", - ); + }); - cases.add( - "use of undeclared identifier", + cases.add("use of undeclared identifier", \\export fn f() void { \\ b = 3; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: use of undeclared identifier 'b'", - ); + }); - cases.add( - "const is a statement, not an expression", + cases.add("const is a statement, not an expression", \\export fn f() void { \\ (const a = 0); \\} - , + , &[_][]const u8{ "tmp.zig:2:6: error: invalid token: 'const'", - ); + }); - cases.add( - "array access of undeclared identifier", + cases.add("array access of undeclared identifier", \\export fn f() void { \\ i[i] = i[i]; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: use of undeclared identifier 'i'", - ); + }); - cases.add( - "array access of non array", + cases.add("array access of non array", \\export fn f() void { \\ var bad : bool = undefined; \\ bad[bad] = bad[bad]; @@ -3268,13 +3489,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var bad : bool = undefined; \\ _ = bad[bad]; \\} - , + , &[_][]const u8{ "tmp.zig:3:8: error: array access of non-array type 'bool'", "tmp.zig:7:12: error: array access of non-array type 'bool'", - ); + }); - cases.add( - "array access with non integer index", + cases.add("array access with non integer index", \\export fn f() void { \\ var array = "aoeu"; \\ var bad = false; @@ -3285,38 +3505,35 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var bad = false; \\ _ = array[bad]; \\} - , + , &[_][]const u8{ "tmp.zig:4:11: error: expected type 'usize', found 'bool'", "tmp.zig:9:15: error: expected type 'usize', found 'bool'", - ); + }); - cases.add( - "write to const global variable", + cases.add("write to const global variable", \\const x : i32 = 99; \\fn f() void { \\ x = 1; \\} \\export fn entry() void { f(); } - , + , &[_][]const u8{ "tmp.zig:3:9: error: cannot assign to constant", - ); + }); - cases.add( - "missing else clause", + cases.add("missing else clause", \\fn f(b: bool) void { \\ const x : i32 = if (b) h: { break :h 1; }; \\} \\fn g(b: bool) void { - \\ const y = if (b) h: { break :h i32(1); }; + \\ const y = if (b) h: { break :h @as(i32, 1); }; \\} \\export fn entry() void { f(true); g(true); } - , - "tmp.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'", + , &[_][]const u8{ + "tmp.zig:2:21: error: expected type 'i32', found 'void'", "tmp.zig:5:15: error: incompatible types: 'i32' and 'void'", - ); + }); - cases.add( - "invalid struct field", + cases.add("invalid struct field", \\const A = struct { x : i32, }; \\export fn f() void { \\ var a : A = undefined; @@ -3327,38 +3544,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var a : A = undefined; \\ const y = a.bar; \\} - , + , &[_][]const u8{ "tmp.zig:4:6: error: no member named 'foo' in struct 'A'", "tmp.zig:9:16: error: no member named 'bar' in struct 'A'", - ); + }); - cases.add( - "redefinition of struct", + cases.add("redefinition of struct", \\const A = struct { x : i32, }; \\const A = struct { y : i32, }; - , + , &[_][]const u8{ "tmp.zig:2:1: error: redefinition of 'A'", - ); + }); - cases.add( - "redefinition of enums", + cases.add("redefinition of enums", \\const A = enum {}; \\const A = enum {}; - , + , &[_][]const u8{ "tmp.zig:2:1: error: redefinition of 'A'", - ); + }); - cases.add( - "redefinition of global variables", + cases.add("redefinition of global variables", \\var a : i32 = 1; \\var a : i32 = 2; - , + , &[_][]const u8{ "tmp.zig:2:1: error: redefinition of 'a'", "tmp.zig:1:1: note: previous definition is here", - ); + }); - cases.add( - "duplicate field in struct value expression", + cases.add("duplicate field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -3372,12 +3585,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .z = 4, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:11:9: error: duplicate field", - ); + }); - cases.add( - "missing field in struct value expression", + cases.add("missing field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -3391,12 +3603,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .y = 2, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:9:17: error: missing field: 'x'", - ); + }); - cases.add( - "invalid field in struct value expression", + cases.add("invalid field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -3409,86 +3620,77 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .foo = 42, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:10:9: error: no member named 'foo' in struct 'A'", - ); + }); - cases.add( - "invalid break expression", + cases.add("invalid break expression", \\export fn f() void { \\ break; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: break expression outside loop", - ); + }); - cases.add( - "invalid continue expression", + cases.add("invalid continue expression", \\export fn f() void { \\ continue; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: continue expression outside loop", - ); + }); - cases.add( - "invalid maybe type", + cases.add("invalid maybe type", \\export fn f() void { \\ if (true) |x| { } \\} - , + , &[_][]const u8{ "tmp.zig:2:9: error: expected optional type, found 'bool'", - ); + }); - cases.add( - "cast unreachable", + cases.add("cast unreachable", \\fn f() i32 { - \\ return i32(return 1); + \\ return @as(i32, return 1); \\} \\export fn entry() void { _ = f(); } - , - "tmp.zig:2:15: error: unreachable code", - ); + , &[_][]const u8{ + "tmp.zig:2:12: error: unreachable code", + }); - cases.add( - "invalid builtin fn", + cases.add("invalid builtin fn", \\fn f() @bogus(foo) { \\} \\export fn entry() void { _ = f(); } - , + , &[_][]const u8{ "tmp.zig:1:8: error: invalid builtin function: 'bogus'", - ); + }); - cases.add( - "noalias on non pointer param", + cases.add("noalias on non pointer param", \\fn f(noalias x: i32) void {} \\export fn entry() void { f(1234); } - , + , &[_][]const u8{ "tmp.zig:1:6: error: noalias on non-pointer parameter", - ); + }); - cases.add( - "struct init syntax for array", + cases.add("struct init syntax for array", \\const foo = [3]u16{ .x = 1024 }; \\comptime { \\ _ = foo; \\} - , + , &[_][]const u8{ "tmp.zig:1:21: error: type '[3]u16' does not support struct initialization syntax", - ); + }); - cases.add( - "type variables must be constant", + cases.add("type variables must be constant", \\var foo = u8; \\export fn entry() foo { \\ return 1; \\} - , + , &[_][]const u8{ "tmp.zig:1:1: error: variable of type 'type' must be constant", - ); + }); - cases.add( - "variables shadowing types", + cases.add("variables shadowing types", \\const Foo = struct {}; \\const Bar = struct {}; \\ @@ -3499,15 +3701,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ f(1234); \\} - , + , &[_][]const u8{ "tmp.zig:4:6: error: redefinition of 'Foo'", "tmp.zig:1:1: note: previous definition is here", "tmp.zig:5:5: error: redefinition of 'Bar'", "tmp.zig:2:1: note: previous definition is here", - ); + }); - cases.add( - "switch expression - missing enumeration prong", + cases.add("switch expression - missing enumeration prong", \\const Number = enum { \\ One, \\ Two, @@ -3518,17 +3719,16 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ switch (n) { \\ Number.One => 1, \\ Number.Two => 2, - \\ Number.Three => i32(3), + \\ Number.Three => @as(i32, 3), \\ } \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch", - ); + }); - cases.add( - "switch expression - duplicate enumeration prong", + cases.add("switch expression - duplicate enumeration prong", \\const Number = enum { \\ One, \\ Two, @@ -3539,20 +3739,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ switch (n) { \\ Number.One => 1, \\ Number.Two => 2, - \\ Number.Three => i32(3), + \\ Number.Three => @as(i32, 3), \\ Number.Four => 4, \\ Number.Two => 2, \\ } \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:13:15: error: duplicate switch value", "tmp.zig:10:15: note: other value is here", - ); + }); - cases.add( - "switch expression - duplicate enumeration prong when else present", + cases.add("switch expression - duplicate enumeration prong when else present", \\const Number = enum { \\ One, \\ Two, @@ -3563,21 +3762,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ switch (n) { \\ Number.One => 1, \\ Number.Two => 2, - \\ Number.Three => i32(3), + \\ Number.Three => @as(i32, 3), \\ Number.Four => 4, \\ Number.Two => 2, \\ else => 10, \\ } \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:13:15: error: duplicate switch value", "tmp.zig:10:15: note: other value is here", - ); + }); - cases.add( - "switch expression - multiple else prongs", + cases.add("switch expression - multiple else prongs", \\fn f(x: u32) void { \\ const value: bool = switch (x) { \\ 1234 => false, @@ -3588,123 +3786,112 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ f(1234); \\} - , + , &[_][]const u8{ "tmp.zig:5:9: error: multiple else prongs in switch expression", - ); + }); - cases.add( - "switch expression - non exhaustive integer prongs", + cases.add("switch expression - non exhaustive integer prongs", \\fn foo(x: u8) void { \\ switch (x) { \\ 0 => {}, \\ } \\} - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:2:5: error: switch must handle all possibilities", - ); + }); - cases.add( - "switch expression - duplicate or overlapping integer value", + cases.add("switch expression - duplicate or overlapping integer value", \\fn foo(x: u8) u8 { \\ return switch (x) { - \\ 0 ... 100 => u8(0), + \\ 0 ... 100 => @as(u8, 0), \\ 101 ... 200 => 1, \\ 201, 203 ... 207 => 2, \\ 206 ... 255 => 3, \\ }; \\} - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:6:9: error: duplicate switch value", "tmp.zig:5:14: note: previous value is here", - ); + }); - cases.add( - "switch expression - switch on pointer type with no else", + cases.add("switch expression - switch on pointer type with no else", \\fn foo(x: *u8) void { \\ switch (x) { \\ &y => {}, \\ } \\} \\const y: u8 = 100; - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:2:5: error: else prong required when switching on type '*u8'", - ); + }); - cases.add( - "global variable initializer must be constant expression", + cases.add("global variable initializer must be constant expression", \\extern fn foo() i32; \\const x = foo(); \\export fn entry() i32 { return x; } - , + , &[_][]const u8{ "tmp.zig:2:11: error: unable to evaluate constant expression", - ); + }); - cases.add( - "array concatenation with wrong type", + cases.add("array concatenation with wrong type", \\const src = "aoeu"; - \\const derp = usize(1234); + \\const derp: usize = 1234; \\const a = derp ++ "foo"; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , - "tmp.zig:3:11: error: expected array or C string literal, found 'usize'", - ); + \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } + , &[_][]const u8{ + "tmp.zig:3:11: error: expected array, found 'usize'", + }); - cases.add( - "non compile time array concatenation", + cases.add("non compile time array concatenation", \\fn f() []u8 { \\ return s ++ "foo"; \\} \\var s: [10]u8 = undefined; - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:2:12: error: unable to evaluate constant expression", - ); + }); - cases.add( - "@cImport with bogus include", + cases.add("@cImport with bogus include", \\const c = @cImport(@cInclude("bogus.h")); - \\export fn entry() usize { return @sizeOf(@typeOf(c.bogo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(c.bogo)); } + , &[_][]const u8{ "tmp.zig:1:11: error: C import failed", ".h:1:10: note: 'bogus.h' file not found", - ); + }); - cases.add( - "address of number literal", + cases.add("address of number literal", \\const x = 3; \\const y = &x; \\fn foo() *const i32 { return y; } - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", - ); + }); - cases.add( - "integer overflow error", + cases.add("integer overflow error", \\const x : u8 = 300; - \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , - "tmp.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", - ); + \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } + , &[_][]const u8{ + "tmp.zig:1:16: error: integer value 300 cannot be coerced to type 'u8'", + }); - cases.add( - "invalid shift amount error", + cases.add("invalid shift amount error", \\const x : u8 = 2; \\fn f() u16 { \\ return x << 8; \\} \\export fn entry() u16 { return f(); } - , + , &[_][]const u8{ "tmp.zig:3:14: error: RHS of shift is too large for LHS type", "tmp.zig:3:17: note: value 8 cannot fit into type u3", - ); + }); - cases.add( - "missing function call param", + cases.add("missing function call param", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -3717,7 +3904,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\}; \\ - \\const member_fn_type = @typeOf(Foo.member_a); + \\const member_fn_type = @TypeOf(Foo.member_a); \\const members = [_]member_fn_type { \\ Foo.member_a, \\ Foo.member_b, @@ -3727,620 +3914,557 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const result = members[index](); \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:20:34: error: expected 1 arguments, found 0", - ); + }); - cases.add( - "missing function name and param name", + cases.add("missing function name", \\fn () void {} - \\fn f(i32) void {} - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:1:1: error: missing function name", - "tmp.zig:2:6: error: missing parameter name", - ); + }); - cases.add( - "wrong function type", + cases.add("missing param name", + \\fn f(i32) void {} + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ + "tmp.zig:1:6: error: missing parameter name", + }); + + cases.add("wrong function type", \\const fns = [_]fn() void { a, b, c }; \\fn a() i32 {return 0;} \\fn b() i32 {return 1;} \\fn c() i32 {return 2;} - \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(fns)); } + , &[_][]const u8{ "tmp.zig:1:28: error: expected type 'fn() void', found 'fn() i32'", - ); + }); - cases.add( - "extern function pointer mismatch", + cases.add("extern function pointer mismatch", \\const fns = [_](fn(i32)i32) { a, b, c }; \\pub fn a(x: i32) i32 {return x + 0;} \\pub fn b(x: i32) i32 {return x + 1;} \\export fn c(x: i32) i32 {return x + 2;} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } - , - "tmp.zig:1:37: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", - ); + \\export fn entry() usize { return @sizeOf(@TypeOf(fns)); } + , &[_][]const u8{ + "tmp.zig:1:37: error: expected type 'fn(i32) i32', found 'fn(i32) callconv(.C) i32'", + }); - cases.add( - "colliding invalid top level functions", + cases.add("colliding invalid top level functions", \\fn func() bogus {} \\fn func() bogus {} - \\export fn entry() usize { return @sizeOf(@typeOf(func)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(func)); } + , &[_][]const u8{ "tmp.zig:2:1: error: redefinition of 'func'", - "tmp.zig:1:11: error: use of undeclared identifier 'bogus'", - ); + }); - cases.add( - "non constant expression in array size", + cases.add("non constant expression in array size", \\const Foo = struct { \\ y: [get()]u8, \\}; \\var global_var: usize = 1; \\fn get() usize { return global_var; } \\ - \\export fn entry() usize { return @sizeOf(@typeOf(Foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(Foo)); } + , &[_][]const u8{ "tmp.zig:5:25: error: unable to evaluate constant expression", "tmp.zig:2:12: note: referenced here", - ); + }); - cases.add( - "addition with non numbers", + cases.add("addition with non numbers", \\const Foo = struct { \\ field: i32, \\}; \\const x = Foo {.field = 1} + Foo {.field = 2}; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } + , &[_][]const u8{ "tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'", - ); + }); - cases.add( - "division by zero", + cases.add("division by zero", \\const lit_int_x = 1 / 0; \\const lit_float_x = 1.0 / 0.0; - \\const int_x = u32(1) / u32(0); - \\const float_x = f32(1.0) / f32(0.0); + \\const int_x = @as(u32, 1) / @as(u32, 0); + \\const float_x = @as(f32, 1.0) / @as(f32, 0.0); \\ - \\export fn entry1() usize { return @sizeOf(@typeOf(lit_int_x)); } - \\export fn entry2() usize { return @sizeOf(@typeOf(lit_float_x)); } - \\export fn entry3() usize { return @sizeOf(@typeOf(int_x)); } - \\export fn entry4() usize { return @sizeOf(@typeOf(float_x)); } - , + \\export fn entry1() usize { return @sizeOf(@TypeOf(lit_int_x)); } + \\export fn entry2() usize { return @sizeOf(@TypeOf(lit_float_x)); } + \\export fn entry3() usize { return @sizeOf(@TypeOf(int_x)); } + \\export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } + , &[_][]const u8{ "tmp.zig:1:21: error: division by zero", "tmp.zig:2:25: error: division by zero", - "tmp.zig:3:22: error: division by zero", - "tmp.zig:4:26: error: division by zero", - ); + "tmp.zig:3:27: error: division by zero", + "tmp.zig:4:31: error: division by zero", + }); - cases.add( - "normal string with newline", + cases.add("normal string with newline", \\const foo = "a \\b"; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:1:15: error: newline not allowed in string literal", - ); + }); - cases.add( - "invalid comparison for function pointers", + cases.add("invalid comparison for function pointers", \\fn foo() void {} \\const invalid = foo > foo; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(invalid)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(invalid)); } + , &[_][]const u8{ "tmp.zig:2:21: error: operator not allowed for type 'fn() void'", - ); + }); - cases.add( - "generic function instance with non-constant expression", + cases.add("generic function instance with non-constant expression", \\fn foo(comptime x: i32, y: i32) i32 { return x + y; } \\fn test1(a: i32, b: i32) i32 { \\ return foo(a, b); \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(test1)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(test1)); } + , &[_][]const u8{ "tmp.zig:3:16: error: unable to evaluate constant expression", - ); + }); - cases.add( - "assign null to non-optional pointer", + cases.add("assign null to non-optional pointer", \\const a: *u8 = null; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } + , &[_][]const u8{ "tmp.zig:1:16: error: expected type '*u8', found '(null)'", - ); + }); - cases.add( - "indexing an array of size zero", + cases.add("indexing an array of size zero", \\const array = [_]u8{}; \\export fn foo() void { \\ const pointer = &array[0]; \\} - , + , &[_][]const u8{ "tmp.zig:3:27: error: index 0 outside array of size 0", - ); + }); - cases.add( - "compile time division by zero", + cases.add("compile time division by zero", \\const y = foo(0); \\fn foo(x: u32) u32 { \\ return 1 / x; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:3:14: error: division by zero", "tmp.zig:1:14: note: referenced here", - ); + }); - cases.add( - "branch on undefined value", + cases.add("branch on undefined value", \\const x = if (undefined) true else false; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } + , &[_][]const u8{ "tmp.zig:1:15: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "div on undefined value", + cases.add("div on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a / a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "div assign on undefined value", + cases.add("div assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a /= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mod on undefined value", + cases.add("mod on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a % a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mod assign on undefined value", + cases.add("mod assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a %= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "add on undefined value", + cases.add("add on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a + a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "add assign on undefined value", + cases.add("add assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a += a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "add wrap on undefined value", + cases.add("add wrap on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a +% a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "add wrap assign on undefined value", + cases.add("add wrap assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a +%= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "sub on undefined value", + cases.add("sub on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a - a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "sub assign on undefined value", + cases.add("sub assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a -= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "sub wrap on undefined value", + cases.add("sub wrap on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a -% a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "sub wrap assign on undefined value", + cases.add("sub wrap assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a -%= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mult on undefined value", + cases.add("mult on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a * a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mult assign on undefined value", + cases.add("mult assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a *= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mult wrap on undefined value", + cases.add("mult wrap on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a *% a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "mult wrap assign on undefined value", + cases.add("mult wrap assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a *%= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "shift left on undefined value", + cases.add("shift left on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a << 2; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "shift left assign on undefined value", + cases.add("shift left assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a <<= 2; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "shift right on undefined value", + cases.add("shift right on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a >> 2; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "shift left assign on undefined value", + cases.add("shift left assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a >>= 2; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin and on undefined value", + cases.add("bin and on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a & a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin and assign on undefined value", + cases.add("bin and assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a &= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin or on undefined value", + cases.add("bin or on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a | a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin or assign on undefined value", + cases.add("bin or assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a |= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin xor on undefined value", + cases.add("bin xor on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = a ^ a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin xor assign on undefined value", + cases.add("bin xor assign on undefined value", \\comptime { \\ var a: i64 = undefined; \\ a ^= a; \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "equal on undefined value", + cases.add("comparison operators with undefined value", + \\// operator == \\comptime { \\ var a: i64 = undefined; - \\ _ = a == a; + \\ var x: i32 = 0; + \\ if (a == a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); - - cases.add( - "not equal on undefined value", + \\// operator != \\comptime { \\ var a: i64 = undefined; - \\ _ = a != a; + \\ var x: i32 = 0; + \\ if (a != a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); - - cases.add( - "greater than on undefined value", + \\// operator > \\comptime { \\ var a: i64 = undefined; - \\ _ = a > a; + \\ var x: i32 = 0; + \\ if (a > a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); - - cases.add( - "greater than equal on undefined value", + \\// operator < \\comptime { \\ var a: i64 = undefined; - \\ _ = a >= a; + \\ var x: i32 = 0; + \\ if (a < a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); - - cases.add( - "less than on undefined value", + \\// operator >= \\comptime { \\ var a: i64 = undefined; - \\ _ = a < a; + \\ var x: i32 = 0; + \\ if (a >= a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); - - cases.add( - "less than equal on undefined value", + \\// operator <= \\comptime { \\ var a: i64 = undefined; - \\ _ = a <= a; + \\ var x: i32 = 0; + \\ if (a <= a) x += 1; \\} - , - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + , &[_][]const u8{ + "tmp.zig:5:11: error: use of undefined value here causes undefined behavior", + "tmp.zig:11:11: error: use of undefined value here causes undefined behavior", + "tmp.zig:17:11: error: use of undefined value here causes undefined behavior", + "tmp.zig:23:11: error: use of undefined value here causes undefined behavior", + "tmp.zig:29:11: error: use of undefined value here causes undefined behavior", + "tmp.zig:35:11: error: use of undefined value here causes undefined behavior", + }); - cases.add( - "and on undefined value", + cases.add("and on undefined value", \\comptime { \\ var a: bool = undefined; \\ _ = a and a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "or on undefined value", + cases.add("or on undefined value", \\comptime { \\ var a: bool = undefined; \\ _ = a or a; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "negate on undefined value", + cases.add("negate on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = -a; \\} - , + , &[_][]const u8{ "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "negate wrap on undefined value", + cases.add("negate wrap on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = -%a; \\} - , + , &[_][]const u8{ "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bin not on undefined value", + cases.add("bin not on undefined value", \\comptime { \\ var a: i64 = undefined; \\ _ = ~a; \\} - , + , &[_][]const u8{ "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "bool not on undefined value", + cases.add("bool not on undefined value", \\comptime { \\ var a: bool = undefined; \\ _ = !a; \\} - , + , &[_][]const u8{ "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "orelse on undefined value", + cases.add("orelse on undefined value", \\comptime { \\ var a: ?bool = undefined; \\ _ = a orelse false; \\} - , + , &[_][]const u8{ "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "catch on undefined value", + cases.add("catch on undefined value", \\comptime { \\ var a: anyerror!bool = undefined; \\ _ = a catch |err| false; \\} - , + , &[_][]const u8{ "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - ); + }); - cases.add( - "deref on undefined value", + cases.add("deref on undefined value", \\comptime { \\ var a: *u8 = undefined; \\ _ = a.*; \\} - , + , &[_][]const u8{ "tmp.zig:3:9: error: attempt to dereference undefined value", - ); + }); - cases.add( - "endless loop in function evaluation", + cases.add("endless loop in function evaluation", \\const seventh_fib_number = fibbonaci(7); \\fn fibbonaci(x: i32) i32 { \\ return fibbonaci(x - 1) + fibbonaci(x - 2); \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(seventh_fib_number)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } + , &[_][]const u8{ "tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches", "tmp.zig:1:37: note: referenced here", "tmp.zig:6:50: note: referenced here", - ); + }); - cases.add( - "@embedFile with bogus file", + cases.add("@embedFile with bogus file", \\const resource = @embedFile("bogus.txt",); \\ - \\export fn entry() usize { return @sizeOf(@typeOf(resource)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(resource)); } + , &[_][]const u8{ "tmp.zig:1:29: error: unable to find '", "bogus.txt'", - ); + }); - cases.add( - "non-const expression in struct literal outside function", + cases.add("non-const expression in struct literal outside function", \\const Foo = struct { \\ x: i32, \\}; \\const a = Foo {.x = get_it()}; \\extern fn get_it() i32; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } + , &[_][]const u8{ "tmp.zig:4:21: error: unable to evaluate constant expression", - ); + }); - cases.add( - "non-const expression function call with struct return value outside function", + cases.add("non-const expression function call with struct return value outside function", \\const Foo = struct { \\ x: i32, \\}; @@ -4351,26 +4475,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\var global_side_effect = false; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } + , &[_][]const u8{ "tmp.zig:6:26: error: unable to evaluate constant expression", "tmp.zig:4:17: note: referenced here", - ); + }); - cases.add( - "undeclared identifier error should mark fn as impure", + cases.add("undeclared identifier error should mark fn as impure", \\export fn foo() void { \\ test_a_thing(); \\} \\fn test_a_thing() void { \\ bad_fn_call(); \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call'", - ); + }); - cases.add( - "illegal comparison of types", + cases.add("illegal comparison of types", \\fn bad_eql_1(a: []u8, b: []u8) bool { \\ return a == b; \\} @@ -4382,15 +4504,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a.* == b.*; \\} \\ - \\export fn entry1() usize { return @sizeOf(@typeOf(bad_eql_1)); } - \\export fn entry2() usize { return @sizeOf(@typeOf(bad_eql_2)); } - , + \\export fn entry1() usize { return @sizeOf(@TypeOf(bad_eql_1)); } + \\export fn entry2() usize { return @sizeOf(@TypeOf(bad_eql_2)); } + , &[_][]const u8{ "tmp.zig:2:14: error: operator not allowed for type '[]u8'", "tmp.zig:9:16: error: operator not allowed for type 'EnumWithData'", - ); + }); - cases.add( - "non-const switch number literal", + cases.add("non-const switch number literal", \\export fn foo() void { \\ const x = switch (bar()) { \\ 1, 2 => 1, @@ -4401,153 +4522,140 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bar() i32 { \\ return 2; \\} - , + , &[_][]const u8{ "tmp.zig:5:17: error: cannot store runtime value in type 'comptime_int'", - ); + }); - cases.add( - "atomic orderings of cmpxchg - failure stricter than success", + cases.add("atomic orderings of cmpxchg - failure stricter than success", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} \\} - , + , &[_][]const u8{ "tmp.zig:4:81: error: failure atomic ordering must be no stricter than success", - ); + }); - cases.add( - "atomic orderings of cmpxchg - success Monotonic or stricter", + cases.add("atomic orderings of cmpxchg - success Monotonic or stricter", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} \\} - , + , &[_][]const u8{ "tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter", - ); + }); - cases.add( - "negation overflow in function evaluation", + cases.add("negation overflow in function evaluation", \\const y = neg(-128); \\fn neg(x: i8) i8 { \\ return -x; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:3:12: error: negation caused overflow", "tmp.zig:1:14: note: referenced here", - ); + }); - cases.add( - "add overflow in function evaluation", + cases.add("add overflow in function evaluation", \\const y = add(65530, 10); \\fn add(a: u16, b: u16) u16 { \\ return a + b; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:3:14: error: operation caused overflow", "tmp.zig:1:14: note: referenced here", - ); + }); - cases.add( - "sub overflow in function evaluation", + cases.add("sub overflow in function evaluation", \\const y = sub(10, 20); \\fn sub(a: u16, b: u16) u16 { \\ return a - b; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:3:14: error: operation caused overflow", "tmp.zig:1:14: note: referenced here", - ); + }); - cases.add( - "mul overflow in function evaluation", + cases.add("mul overflow in function evaluation", \\const y = mul(300, 6000); \\fn mul(a: u16, b: u16) u16 { \\ return a * b; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } + , &[_][]const u8{ "tmp.zig:3:14: error: operation caused overflow", "tmp.zig:1:14: note: referenced here", - ); + }); - cases.add( - "truncate sign mismatch", + cases.add("truncate sign mismatch", \\fn f() i8 { \\ var x: u32 = 10; \\ return @truncate(i8, x); \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:3:26: error: expected signed integer type, found 'u32'", - ); + }); - cases.add( - "try in function with non error return type", + cases.add("try in function with non error return type", \\export fn f() void { \\ try something(); \\} \\fn something() anyerror!void { } - , + , &[_][]const u8{ "tmp.zig:2:5: error: expected type 'void', found 'anyerror'", "tmp.zig:1:15: note: return type declared here", - ); + }); - cases.add( - "invalid pointer for var type", + cases.add("invalid pointer for var type", \\extern fn ext() usize; \\var bytes: [ext()]u8 = undefined; \\export fn f() void { \\ for (bytes) |*b, i| { - \\ b.* = u8(i); + \\ b.* = @as(u8, i); \\ } \\} - , + , &[_][]const u8{ "tmp.zig:2:13: error: unable to evaluate constant expression", - ); + }); - cases.add( - "export function with comptime parameter", + cases.add("export function with comptime parameter", \\export fn foo(comptime x: i32, y: i32) i32{ \\ return x + y; \\} - , - "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", - ); + , &[_][]const u8{ + "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'", + }); - cases.add( - "extern function with comptime parameter", + cases.add("extern function with comptime parameter", \\extern fn foo(comptime x: i32, y: i32) i32; \\fn f() i32 { \\ return foo(1, 2); \\} - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , - "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", - ); + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ + "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'", + }); - cases.add( - "convert fixed size array to slice with invalid size", + cases.add("convert fixed size array to slice with invalid size", \\export fn f() void { \\ var array: [5]u8 = undefined; - \\ var foo = @bytesToSlice(u32, array)[0]; + \\ var foo = @bytesToSlice(u32, &array)[0]; \\} - , - "tmp.zig:3:15: error: unable to convert [5]u8 to []align(1) const u32: size mismatch", + , &[_][]const u8{ + "tmp.zig:3:15: error: unable to convert [5]u8 to []align(1) u32: size mismatch", "tmp.zig:3:29: note: u32 has size 4; remaining bytes: 1", - ); + }); - cases.add( - "non-pure function returns type", + cases.add("non-pure function returns type", \\var a: u32 = 0; \\pub fn List(comptime T: type) type { \\ a += 1; @@ -4566,24 +4674,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var list: List(i32) = undefined; \\ list.length = 10; \\} - , + , &[_][]const u8{ "tmp.zig:3:7: error: unable to evaluate constant expression", "tmp.zig:16:19: note: referenced here", - ); + }); - cases.add( - "bogus method call on slice", + cases.add("bogus method call on slice", \\var self = "aoeu"; \\fn f(m: []const u8) void { \\ m.copy(u8, self[0..], m); \\} - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:3:6: error: no member named 'copy' in '[]const u8'", - ); + }); - cases.add( - "wrong number of arguments for method fn call", + cases.add("wrong number of arguments for method fn call", \\const Foo = struct { \\ fn method(self: *const Foo, a: i32) void {} \\}; @@ -4591,40 +4697,36 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\ foo.method(1, 2); \\} - \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } + , &[_][]const u8{ "tmp.zig:6:15: error: expected 2 arguments, found 3", - ); + }); - cases.add( - "assign through constant pointer", + cases.add("assign through constant pointer", \\export fn f() void { - \\ var cstr = c"Hat"; + \\ var cstr = "Hat"; \\ cstr[0] = 'W'; \\} - , + , &[_][]const u8{ "tmp.zig:3:13: error: cannot assign to constant", - ); + }); - cases.add( - "assign through constant slice", + cases.add("assign through constant slice", \\export fn f() void { \\ var cstr: []const u8 = "Hat"; \\ cstr[0] = 'W'; \\} - , + , &[_][]const u8{ "tmp.zig:3:13: error: cannot assign to constant", - ); + }); - cases.add( - "main function with bogus args type", + cases.add("main function with bogus args type", \\pub fn main(args: [][]bogus) !void {} - , + , &[_][]const u8{ "tmp.zig:1:23: error: use of undeclared identifier 'bogus'", - ); + }); - cases.add( - "misspelled type with pointer only reference", + cases.add("misspelled type with pointer only reference", \\const JasonHM = u8; \\const JasonList = *JsonNode; \\ @@ -4654,13 +4756,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var jd = JsonNode {.kind = JsonType.JSONArray , .jobject = JsonOA.JSONArray {jll} }; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:5:16: error: use of undeclared identifier 'JsonList'", - ); + }); - cases.add( - "method call with first arg type primitive", + cases.add("method call with first arg type primitive", \\const Foo = struct { \\ x: i32, \\ @@ -4676,12 +4777,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\ derp.init(); \\} - , + , &[_][]const u8{ "tmp.zig:14:5: error: expected type 'i32', found 'Foo'", - ); + }); - cases.add( - "method call with first arg type wrong container", + cases.add("method call with first arg type wrong container", \\pub const List = struct { \\ len: usize, \\ allocator: *Allocator, @@ -4706,33 +4806,31 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = List.init(&global_allocator); \\ x.init(); \\} - , + , &[_][]const u8{ "tmp.zig:23:5: error: expected type '*Allocator', found '*List'", - ); + }); - cases.add( - "binary not on number literal", + cases.add("binary not on number literal", \\const TINY_QUANTUM_SHIFT = 4; \\const TINY_QUANTUM_SIZE = 1 << TINY_QUANTUM_SHIFT; \\var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - 1); \\ - \\export fn entry() usize { return @sizeOf(@typeOf(block_aligned_stuff)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(block_aligned_stuff)); } + , &[_][]const u8{ "tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", - ); + }); cases.addCase(x: { - const tc = cases.create( - "multiple files with private function error", + const tc = cases.create("multiple files with private function error", \\const foo = @import("foo.zig",); \\ \\export fn callPrivFunction() void { \\ foo.privateFunction(); \\} - , + , &[_][]const u8{ "tmp.zig:4:8: error: 'privateFunction' is private", "foo.zig:1:1: note: declared here", - ); + }); tc.addSourceFile("foo.zig", \\fn privateFunction() void { } @@ -4741,18 +4839,16 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "container init with non-type", + cases.add("container init with non-type", \\const zero: i32 = 0; \\const a = zero{1}; \\ - \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } + , &[_][]const u8{ "tmp.zig:2:11: error: expected type 'type', found 'i32'", - ); + }); - cases.add( - "assign to constant field", + cases.add("assign to constant field", \\const Foo = struct { \\ field: i32, \\}; @@ -4760,12 +4856,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const f = Foo {.field = 1234,}; \\ f.field = 0; \\} - , + , &[_][]const u8{ "tmp.zig:6:15: error: cannot assign to constant", - ); + }); - cases.add( - "return from defer expression", + cases.add("return from defer expression", \\pub fn testTrickyDefer() !void { \\ defer canFail() catch {}; \\ @@ -4780,73 +4875,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return 0; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(testTrickyDefer)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); } + , &[_][]const u8{ "tmp.zig:4:11: error: cannot return from defer expression", - ); + }); - cases.add( - "attempt to access var args out of bounds", - \\fn add(args: ...) i32 { - \\ return args[0] + args[1]; - \\} - \\ - \\fn foo() i32 { - \\ return add(i32(1234)); - \\} - \\ - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , - "tmp.zig:2:26: error: index 1 outside argument list of size 1", - "tmp.zig:6:15: note: called from here", - ); - - cases.add( - "pass integer literal to var args", - \\fn add(args: ...) i32 { - \\ var sum = i32(0); - \\ {comptime var i: usize = 0; inline while (i < args.len) : (i += 1) { - \\ sum += args[i]; - \\ }} - \\ return sum; - \\} - \\ - \\fn bar() i32 { - \\ return add(1, 2, 3, 4); - \\} - \\ - \\export fn entry() usize { return @sizeOf(@typeOf(bar)); } - , - "tmp.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted", - ); - - cases.add( - "assign too big number to u16", + cases.add("assign too big number to u16", \\export fn foo() void { \\ var vga_mem: u16 = 0xB8000; \\} - , - "tmp.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'", - ); + , &[_][]const u8{ + "tmp.zig:2:24: error: integer value 753664 cannot be coerced to type 'u16'", + }); - cases.add( - "global variable alignment non power of 2", + cases.add("global variable alignment non power of 2", \\const some_data: [100]u8 align(3) = undefined; - \\export fn entry() usize { return @sizeOf(@typeOf(some_data)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(some_data)); } + , &[_][]const u8{ "tmp.zig:1:32: error: alignment value 3 is not a power of 2", - ); + }); - cases.add( - "function alignment non power of 2", + cases.add("function alignment non power of 2", \\extern fn foo() align(3) void; \\export fn entry() void { return foo(); } - , + , &[_][]const u8{ "tmp.zig:1:23: error: alignment value 3 is not a power of 2", - ); + }); - cases.add( - "compile log", + cases.add("compile log", \\export fn foo() void { \\ comptime bar(12, "hi",); \\} @@ -4855,14 +4911,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @compileLog("a", a, "b", b); \\ @compileLog("end",); \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: found compile log statement", "tmp.zig:6:5: error: found compile log statement", "tmp.zig:7:5: error: found compile log statement", - ); + }); - cases.add( - "casting bit offset pointer to regular pointer", + cases.add("casting bit offset pointer to regular pointer", \\const BitField = packed struct { \\ a: u3, \\ b: u3, @@ -4877,13 +4932,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x.*; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", - ); + }); - cases.add( - "referring to a struct that is invalid", + cases.add("referring to a struct that is invalid", \\const UsbDeviceRequest = struct { \\ Type: u8, \\}; @@ -4895,13 +4949,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn assert(ok: bool) void { \\ if (!ok) unreachable; \\} - , + , &[_][]const u8{ "tmp.zig:10:14: error: unable to evaluate constant expression", "tmp.zig:6:20: note: referenced here", - ); + }); - cases.add( - "control flow uses comptime var at runtime", + cases.add("control flow uses comptime var at runtime", \\export fn foo() void { \\ comptime var i = 0; \\ while (i < 5) : (i += 1) { @@ -4910,79 +4963,71 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\ \\fn bar() void { } - , + , &[_][]const u8{ "tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime", "tmp.zig:3:24: note: compile-time variable assigned here", - ); + }); - cases.add( - "ignored return value", + cases.add("ignored return value", \\export fn foo() void { \\ bar(); \\} \\fn bar() i32 { return 0; } - , + , &[_][]const u8{ "tmp.zig:2:8: error: expression value is ignored", - ); + }); - cases.add( - "ignored assert-err-ok return value", + cases.add("ignored assert-err-ok return value", \\export fn foo() void { \\ bar() catch unreachable; \\} \\fn bar() anyerror!i32 { return 0; } - , + , &[_][]const u8{ "tmp.zig:2:11: error: expression value is ignored", - ); + }); - cases.add( - "ignored statement value", + cases.add("ignored statement value", \\export fn foo() void { \\ 1; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: expression value is ignored", - ); + }); - cases.add( - "ignored comptime statement value", + cases.add("ignored comptime statement value", \\export fn foo() void { \\ comptime {1;} \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: expression value is ignored", - ); + }); - cases.add( - "ignored comptime value", + cases.add("ignored comptime value", \\export fn foo() void { \\ comptime 1; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: expression value is ignored", - ); + }); - cases.add( - "ignored defered statement value", + cases.add("ignored defered statement value", \\export fn foo() void { \\ defer {1;} \\} - , + , &[_][]const u8{ "tmp.zig:2:12: error: expression value is ignored", - ); + }); - cases.add( - "ignored defered function call", + cases.add("ignored defered function call", \\export fn foo() void { \\ defer bar(); \\} \\fn bar() anyerror!i32 { return 0; } - , + , &[_][]const u8{ "tmp.zig:2:14: error: expression value is ignored", - ); + }); - cases.add( - "dereference an array", + cases.add("dereference an array", \\var s_buffer: [10]u8 = undefined; \\pub fn pass(in: []u8) []u8 { \\ var out = &s_buffer; @@ -4990,15 +5035,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return out.*[0..1]; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(pass)); } + , &[_][]const u8{ "tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8'", - ); + }); - cases.add( - "pass const ptr to mutable ptr fn", + cases.add("pass const ptr to mutable ptr fn", \\fn foo() bool { - \\ const a = ([]const u8)("a",); + \\ const a = @as([]const u8, "a",); \\ const b = &a; \\ return ptrEql(b, b); \\} @@ -5006,23 +5050,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return true; \\} \\ - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ "tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", - ); + }); cases.addCase(x: { - const tc = cases.create( - "export collision", + const tc = cases.create("export collision", \\const foo = @import("foo.zig",); \\ \\export fn bar() usize { \\ return foo.baz; \\} - , + , &[_][]const u8{ "foo.zig:1:1: error: exported symbol collision: 'bar'", "tmp.zig:3:1: note: other symbol here", - ); + }); tc.addSourceFile("foo.zig", \\export fn bar() void {} @@ -5032,28 +5075,25 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "implicit cast from array to mutable slice", + cases.add("implicit cast from array to mutable slice", \\var global_array: [10]i32 = undefined; \\fn foo(param: []i32) void {} \\export fn entry() void { \\ foo(global_array); \\} - , + , &[_][]const u8{ "tmp.zig:4:9: error: expected type '[]i32', found '[10]i32'", - ); + }); - cases.add( - "ptrcast to non-pointer", + cases.add("ptrcast to non-pointer", \\export fn entry(a: *i32) usize { \\ return @ptrCast(usize, a); \\} - , + , &[_][]const u8{ "tmp.zig:2:21: error: expected pointer, found 'usize'", - ); + }); - cases.add( - "asm at compile time", + cases.add("asm at compile time", \\comptime { \\ doSomeAsm(); \\} @@ -5065,66 +5105,60 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\.set aoeu, derp; \\ ); \\} - , + , &[_][]const u8{ "tmp.zig:6:5: error: unable to evaluate constant expression", - ); + }); - cases.add( - "invalid member of builtin enum", + cases.add("invalid member of builtin enum", \\const builtin = @import("builtin",); \\export fn entry() void { \\ const foo = builtin.Arch.x86; \\} - , - "tmp.zig:3:29: error: container 'builtin.Arch' has no member called 'x86'", - ); + , &[_][]const u8{ + "tmp.zig:3:29: error: container 'std.target.Arch' has no member called 'x86'", + }); - cases.add( - "int to ptr of 0 bits", + cases.add("int to ptr of 0 bits", \\export fn foo() void { \\ var x: usize = 0x1000; \\ var y: *void = @intToPtr(*void, x); \\} - , + , &[_][]const u8{ "tmp.zig:3:30: error: type '*void' has 0 bits and cannot store information", - ); + }); - cases.add( - "@fieldParentPtr - non struct", + cases.add("@fieldParentPtr - non struct", \\const Foo = i32; \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , + , &[_][]const u8{ "tmp.zig:3:28: error: expected struct type, found 'i32'", - ); + }); - cases.add( - "@fieldParentPtr - bad field name", + cases.add("@fieldParentPtr - bad field name", \\const Foo = extern struct { \\ derp: i32, \\}; \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , + , &[_][]const u8{ "tmp.zig:5:33: error: struct 'Foo' has no field 'a'", - ); + }); - cases.add( - "@fieldParentPtr - field pointer is not pointer", + cases.add("@fieldParentPtr - field pointer is not pointer", \\const Foo = extern struct { \\ a: i32, \\}; \\export fn foo(a: i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , + , &[_][]const u8{ "tmp.zig:5:38: error: expected pointer, found 'i32'", - ); + }); - cases.add( - "@fieldParentPtr - comptime field ptr not based on struct", + cases.add("@fieldParentPtr - comptime field ptr not based on struct", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -5135,12 +5169,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const field_ptr = @intToPtr(*i32, 0x1234); \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); \\} - , + , &[_][]const u8{ "tmp.zig:9:55: error: pointer value not based on parent struct", - ); + }); - cases.add( - "@fieldParentPtr - comptime wrong field index", + cases.add("@fieldParentPtr - comptime wrong field index", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -5150,80 +5183,72 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\comptime { \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a); \\} - , + , &[_][]const u8{ "tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'", - ); + }); - cases.add( - "@byteOffsetOf - non struct", + cases.add("@byteOffsetOf - non struct", \\const Foo = i32; \\export fn foo() usize { \\ return @byteOffsetOf(Foo, "a",); \\} - , + , &[_][]const u8{ "tmp.zig:3:26: error: expected struct type, found 'i32'", - ); + }); - cases.add( - "@byteOffsetOf - bad field name", + cases.add("@byteOffsetOf - bad field name", \\const Foo = struct { \\ derp: i32, \\}; \\export fn foo() usize { \\ return @byteOffsetOf(Foo, "a",); \\} - , + , &[_][]const u8{ "tmp.zig:5:31: error: struct 'Foo' has no field 'a'", - ); + }); - cases.addExe( - "missing main fn in executable", + cases.addExe("missing main fn in executable", \\ - , + , &[_][]const u8{ "error: root source file has no member called 'main'", - ); + }); - cases.addExe( - "private main fn", + cases.addExe("private main fn", \\fn main() void {} - , + , &[_][]const u8{ "error: 'main' is private", "tmp.zig:1:1: note: declared here", - ); + }); - cases.add( - "setting a section on a local variable", + cases.add("setting a section on a local variable", \\export fn entry() i32 { \\ var foo: i32 linksection(".text2") = 1234; \\ return foo; \\} - , + , &[_][]const u8{ "tmp.zig:2:30: error: cannot set section of local variable 'foo'", - ); + }); - cases.add( - "returning address of local variable - simple", + cases.add("returning address of local variable - simple", \\export fn foo() *i32 { \\ var a: i32 = undefined; \\ return &a; \\} - , + , &[_][]const u8{ "tmp.zig:3:13: error: function returns address of local variable", - ); + }); - cases.add( - "returning address of local variable - phi", + cases.add("returning address of local variable - phi", \\export fn foo(c: bool) *i32 { \\ var a: i32 = undefined; \\ var b: i32 = undefined; \\ return if (c) &a else &b; \\} - , + , &[_][]const u8{ "tmp.zig:4:12: error: function returns address of local variable", - ); + }); - cases.add( - "inner struct member shadowing outer struct member", + cases.add("inner struct member shadowing outer struct member", \\fn A() type { \\ return struct { \\ b: B(), @@ -5243,73 +5268,66 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn assert(ok: bool) void { \\ if (!ok) unreachable; \\} - , + , &[_][]const u8{ "tmp.zig:9:17: error: redefinition of 'Self'", "tmp.zig:5:9: note: previous definition is here", - ); + }); - cases.add( - "while expected bool, got optional", + cases.add("while expected bool, got optional", \\export fn foo() void { \\ while (bar()) {} \\} \\fn bar() ?i32 { return 1; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected type 'bool', found '?i32'", - ); + }); - cases.add( - "while expected bool, got error union", + cases.add("while expected bool, got error union", \\export fn foo() void { \\ while (bar()) {} \\} \\fn bar() anyerror!i32 { return 1; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", - ); + }); - cases.add( - "while expected optional, got bool", + cases.add("while expected optional, got bool", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() bool { return true; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected optional type, found 'bool'", - ); + }); - cases.add( - "while expected optional, got error union", + cases.add("while expected optional, got error union", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() anyerror!i32 { return 1; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected optional type, found 'anyerror!i32'", - ); + }); - cases.add( - "while expected error union, got bool", + cases.add("while expected error union, got bool", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} \\fn bar() bool { return true; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected error union type, found 'bool'", - ); + }); - cases.add( - "while expected error union, got optional", + cases.add("while expected error union, got optional", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} \\fn bar() ?i32 { return 1; } - , + , &[_][]const u8{ "tmp.zig:2:15: error: expected error union type, found '?i32'", - ); + }); - cases.add( - "inline fn calls itself indirectly", + cases.add("inline fn calls itself indirectly", \\export fn foo() void { \\ bar(); \\} @@ -5322,94 +5340,85 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ quux(); \\} \\extern fn quux() void; - , + , &[_][]const u8{ "tmp.zig:4:1: error: unable to inline function", - ); + }); - cases.add( - "save reference to inline function", + cases.add("save reference to inline function", \\export fn foo() void { \\ quux(@ptrToInt(bar)); \\} \\inline fn bar() void { } \\extern fn quux(usize) void; - , + , &[_][]const u8{ "tmp.zig:4:1: error: unable to inline function", - ); + }); - cases.add( - "signed integer division", + cases.add("signed integer division", \\export fn foo(a: i32, b: i32) i32 { \\ return a / b; \\} - , + , &[_][]const u8{ "tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact", - ); + }); - cases.add( - "signed integer remainder division", + cases.add("signed integer remainder division", \\export fn foo(a: i32, b: i32) i32 { \\ return a % b; \\} - , + , &[_][]const u8{ "tmp.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod", - ); + }); - cases.add( - "compile-time division by zero", + cases.add("compile-time division by zero", \\comptime { \\ const a: i32 = 1; \\ const b: i32 = 0; \\ const c = a / b; \\} - , + , &[_][]const u8{ "tmp.zig:4:17: error: division by zero", - ); + }); - cases.add( - "compile-time remainder division by zero", + cases.add("compile-time remainder division by zero", \\comptime { \\ const a: i32 = 1; \\ const b: i32 = 0; \\ const c = a % b; \\} - , + , &[_][]const u8{ "tmp.zig:4:17: error: division by zero", - ); + }); - cases.add( - "@setRuntimeSafety twice for same scope", + cases.add("@setRuntimeSafety twice for same scope", \\export fn foo() void { \\ @setRuntimeSafety(false); \\ @setRuntimeSafety(false); \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: runtime safety set twice for same scope", "tmp.zig:2:5: note: first set here", - ); + }); - cases.add( - "@setFloatMode twice for same scope", + cases.add("@setFloatMode twice for same scope", \\export fn foo() void { \\ @setFloatMode(@import("builtin").FloatMode.Optimized); \\ @setFloatMode(@import("builtin").FloatMode.Optimized); \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: float mode set twice for same scope", "tmp.zig:2:5: note: first set here", - ); + }); - cases.add( - "array access of type", + cases.add("array access of type", \\export fn foo() void { \\ var b: u8[40] = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:14: error: array access of non-array type 'type'", - ); + }); - cases.add( - "cannot break out of defer expression", + cases.add("cannot break out of defer expression", \\export fn foo() void { \\ while (true) { \\ defer { @@ -5417,12 +5426,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:4:13: error: cannot break out of defer expression", - ); + }); - cases.add( - "cannot continue out of defer expression", + cases.add("cannot continue out of defer expression", \\export fn foo() void { \\ while (true) { \\ defer { @@ -5430,26 +5438,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:4:13: error: cannot continue out of defer expression", - ); + }); - cases.add( - "calling a var args function only known at runtime", - \\var foos = [_]fn(...) void { foo1, foo2 }; - \\ - \\fn foo1(args: ...) void {} - \\fn foo2(args: ...) void {} - \\ - \\pub fn main() !void { - \\ foos[0](); - \\} - , - "tmp.zig:7:9: error: calling a generic function requires compile-time known function value", - ); - - cases.add( - "calling a generic function only known at runtime", + cases.add("calling a generic function only known at runtime", \\var foos = [_]fn(var) void { foo1, foo2 }; \\ \\fn foo1(arg: var) void {} @@ -5458,12 +5451,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub fn main() !void { \\ foos[0](true); \\} - , + , &[_][]const u8{ "tmp.zig:7:9: error: calling a generic function requires compile-time known function value", - ); + }); - cases.add( - "@compileError shows traceback of references that caused it", + cases.add("@compileError shows traceback of references that caused it", \\const foo = @compileError("aoeu",); \\ \\const bar = baz + foo; @@ -5472,96 +5464,86 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() i32 { \\ return bar; \\} - , + , &[_][]const u8{ "tmp.zig:1:13: error: aoeu", "tmp.zig:3:19: note: referenced here", "tmp.zig:7:12: note: referenced here", - ); + }); - cases.add( - "float literal too large error", + cases.add("float literal too large error", \\comptime { \\ const a = 0x1.0p18495; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: float literal out of range of any type", - ); + }); - cases.add( - "float literal too small error (denormal)", + cases.add("float literal too small error (denormal)", \\comptime { \\ const a = 0x1.0p-19000; \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: float literal out of range of any type", - ); + }); - cases.add( - "explicit cast float literal to integer when there is a fraction component", + cases.add("explicit cast float literal to integer when there is a fraction component", \\export fn entry() i32 { - \\ return i32(12.34); + \\ return @as(i32, 12.34); \\} - , - "tmp.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", - ); + , &[_][]const u8{ + "tmp.zig:2:21: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", + }); - cases.add( - "non pointer given to @ptrToInt", + cases.add("non pointer given to @ptrToInt", \\export fn entry(x: i32) usize { \\ return @ptrToInt(x); \\} - , + , &[_][]const u8{ "tmp.zig:2:22: error: expected pointer, found 'i32'", - ); + }); - cases.add( - "@shlExact shifts out 1 bits", + cases.add("@shlExact shifts out 1 bits", \\comptime { - \\ const x = @shlExact(u8(0b01010101), 2); + \\ const x = @shlExact(@as(u8, 0b01010101), 2); \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: operation caused overflow", - ); + }); - cases.add( - "@shrExact shifts out 1 bits", + cases.add("@shrExact shifts out 1 bits", \\comptime { - \\ const x = @shrExact(u8(0b10101010), 2); + \\ const x = @shrExact(@as(u8, 0b10101010), 2); \\} - , + , &[_][]const u8{ "tmp.zig:2:15: error: exact shift shifted out 1 bits", - ); + }); - cases.add( - "shifting without int type or comptime known", + cases.add("shifting without int type or comptime known", \\export fn entry(x: u8) u8 { \\ return 0x11 << x; \\} - , + , &[_][]const u8{ "tmp.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known", - ); + }); - cases.add( - "shifting RHS is log2 of LHS int bit width", + cases.add("shifting RHS is log2 of LHS int bit width", \\export fn entry(x: u8, y: u8) u8 { \\ return x << y; \\} - , + , &[_][]const u8{ "tmp.zig:2:17: error: expected type 'u3', found 'u8'", - ); + }); - cases.add( - "globally shadowing a primitive type", + cases.add("globally shadowing a primitive type", \\const u16 = @intType(false, 8); \\export fn entry() void { \\ const a: u16 = 300; \\} - , + , &[_][]const u8{ "tmp.zig:1:1: error: declaration shadows primitive type 'u16'", - ); + }); - cases.add( - "implicitly increasing pointer alignment", + cases.add("implicitly increasing pointer alignment", \\const Foo = packed struct { \\ a: u8, \\ b: u32, @@ -5575,12 +5557,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bar(x: *u32) void { \\ x.* += 1; \\} - , + , &[_][]const u8{ "tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32'", - ); + }); - cases.add( - "implicitly increasing slice alignment", + cases.add("implicitly increasing slice alignment", \\const Foo = packed struct { \\ a: u8, \\ b: u32, @@ -5589,42 +5570,39 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var foo = Foo { .a = 1, .b = 10 }; \\ foo.b += 1; - \\ bar((*[1]u32)(&foo.b)[0..]); + \\ bar(@as(*[1]u32, &foo.b)[0..]); \\} \\ \\fn bar(x: []u32) void { \\ x[0] += 1; \\} - , - "tmp.zig:9:18: error: cast increases pointer alignment", - "tmp.zig:9:23: note: '*align(1) u32' has alignment 1", - "tmp.zig:9:18: note: '*[1]u32' has alignment 4", - ); + , &[_][]const u8{ + "tmp.zig:9:26: error: cast increases pointer alignment", + "tmp.zig:9:26: note: '*align(1) u32' has alignment 1", + "tmp.zig:9:26: note: '*[1]u32' has alignment 4", + }); - cases.add( - "increase pointer alignment in @ptrCast", + cases.add("increase pointer alignment in @ptrCast", \\export fn entry() u32 { \\ var bytes: [4]u8 = [_]u8{0x01, 0x02, 0x03, 0x04}; \\ const ptr = @ptrCast(*u32, &bytes[0]); \\ return ptr.*; \\} - , + , &[_][]const u8{ "tmp.zig:3:17: error: cast increases pointer alignment", "tmp.zig:3:38: note: '*u8' has alignment 1", "tmp.zig:3:26: note: '*u32' has alignment 4", - ); + }); - cases.add( - "@alignCast expects pointer or slice", + cases.add("@alignCast expects pointer or slice", \\export fn entry() void { - \\ @alignCast(4, u32(3)); + \\ @alignCast(4, @as(u32, 3)); \\} - , - "tmp.zig:2:22: error: expected pointer or slice, found 'u32'", - ); + , &[_][]const u8{ + "tmp.zig:2:19: error: expected pointer or slice, found 'u32'", + }); - cases.add( - "passing an under-aligned function pointer", + cases.add("passing an under-aligned function pointer", \\export fn entry() void { \\ testImplicitlyDecreaseFnAlign(alignedSmall, 1234); \\} @@ -5632,45 +5610,41 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (ptr() != answer) unreachable; \\} \\fn alignedSmall() align(4) i32 { return 1234; } - , + , &[_][]const u8{ "tmp.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", - ); + }); - cases.add( - "passing a not-aligned-enough pointer to cmpxchg", + cases.add("passing a not-aligned-enough pointer to cmpxchg", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn entry() bool { \\ var x: i32 align(1) = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} \\ return x == 5678; \\} - , + , &[_][]const u8{ "tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32'", - ); + }); - cases.add( - "wrong size to an array literal", + cases.add("wrong size to an array literal", \\comptime { \\ const array = [2]u8{1, 2, 3}; \\} - , + , &[_][]const u8{ "tmp.zig:2:31: error: index 2 outside array of size 2", - ); + }); - cases.add( - "wrong pointer implicitly casted to pointer to @OpaqueType()", + cases.add("wrong pointer coerced to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); \\extern fn bar(d: *Derp) void; \\export fn foo() void { - \\ var x = u8(1); + \\ var x = @as(u8, 1); \\ bar(@ptrCast(*c_void, &x)); \\} - , + , &[_][]const u8{ "tmp.zig:5:9: error: expected type '*Derp', found '*c_void'", - ); + }); - cases.add( - "non-const variables of things that require const variables", + cases.add("non-const variables of things that require const variables", \\export fn entry1() void { \\ var m2 = &2; \\} @@ -5702,7 +5676,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const Foo = struct { \\ fn bar(self: *const Foo) void {} \\}; - , + , &[_][]const u8{ "tmp.zig:2:4: error: variable of type '*comptime_int' must be const or comptime", "tmp.zig:5:4: error: variable of type '(undefined)' must be const or comptime", "tmp.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", @@ -5711,31 +5685,28 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:17:4: error: variable of type 'Opaque' not allowed", "tmp.zig:20:4: error: variable of type 'type' must be const or comptime", "tmp.zig:23:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", - "tmp.zig:26:4: error: unreachable code", - ); + "tmp.zig:26:22: error: unreachable code", + }); - cases.add( - "wrong types given to atomic order args in cmpxchg", + cases.add("wrong types given to atomic order args in cmpxchg", \\export fn entry() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, @as(u32, 1234), @as(u32, 1234))) {} \\} - , - "tmp.zig:3:50: error: expected type 'builtin.AtomicOrder', found 'u32'", - ); + , &[_][]const u8{ + "tmp.zig:3:47: error: expected type 'std.builtin.AtomicOrder', found 'u32'", + }); - cases.add( - "wrong types given to @export", - \\extern fn entry() void { } + cases.add("wrong types given to @export", + \\fn entry() callconv(.C) void { } \\comptime { - \\ @export("entry", entry, u32(1234)); + \\ @export(entry, .{.name = "entry", .linkage = @as(u32, 1234) }); \\} - , - "tmp.zig:3:32: error: expected type 'builtin.GlobalLinkage', found 'u32'", - ); + , &[_][]const u8{ + "tmp.zig:3:50: error: expected type 'std.builtin.GlobalLinkage', found 'u32'", + }); - cases.add( - "struct with invalid field", + cases.add("struct with invalid field", \\const std = @import("std",); \\const Allocator = std.mem.Allocator; \\const ArrayList = std.ArrayList; @@ -5759,62 +5730,56 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ .weight = HeaderWeight.H1, \\ }; \\} - , + , &[_][]const u8{ "tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue'", - ); + }); - cases.add( - "@setAlignStack outside function", + cases.add("@setAlignStack outside function", \\comptime { \\ @setAlignStack(16); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: @setAlignStack outside function", - ); + }); - cases.add( - "@setAlignStack in naked function", - \\export nakedcc fn entry() void { + cases.add("@setAlignStack in naked function", + \\export fn entry() callconv(.Naked) void { \\ @setAlignStack(16); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: @setAlignStack in naked function", - ); + }); - cases.add( - "@setAlignStack in inline function", + cases.add("@setAlignStack in inline function", \\export fn entry() void { \\ foo(); \\} \\inline fn foo() void { \\ @setAlignStack(16); \\} - , + , &[_][]const u8{ "tmp.zig:5:5: error: @setAlignStack in inline function", - ); + }); - cases.add( - "@setAlignStack set twice", + cases.add("@setAlignStack set twice", \\export fn entry() void { \\ @setAlignStack(16); \\ @setAlignStack(16); \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: alignstack set twice", "tmp.zig:2:5: note: first set here", - ); + }); - cases.add( - "@setAlignStack too big", + cases.add("@setAlignStack too big", \\export fn entry() void { \\ @setAlignStack(511 + 1); \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256", - ); + }); - cases.add( - "storing runtime value in compile time variable then using it", + cases.add("storing runtime value in compile time variable then using it", \\const Mode = @import("builtin").Mode; \\ \\fn Free(comptime filename: []const u8) TestCase { @@ -5856,12 +5821,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ } \\ } \\} - , + , &[_][]const u8{ "tmp.zig:37:29: error: cannot store runtime value in compile time variable", - ); + }); - cases.add( - "field access of opaque type", + cases.add("field access of opaque type", \\const MyType = @OpaqueType(); \\ \\export fn entry() bool { @@ -5872,163 +5836,143 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn bar(x: *MyType) bool { \\ return x.blah; \\} - , + , &[_][]const u8{ "tmp.zig:9:13: error: type '*MyType' does not support field access", - ); + }); - cases.add( - "carriage return special case", - "fn test() bool {\r\n" ++ - " true\r\n" ++ - "}\r\n", + cases.add("carriage return special case", "fn test() bool {\r\n" ++ + " true\r\n" ++ + "}\r\n", &[_][]const u8{ "tmp.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported", - ); + }); - cases.add( - "invalid legacy unicode escape", + cases.add("invalid legacy unicode escape", \\export fn entry() void { \\ const a = '\U1234'; \\} - , + , &[_][]const u8{ "tmp.zig:2:17: error: invalid character: 'U'", - ); + }); - cases.add( - "invalid empty unicode escape", + cases.add("invalid empty unicode escape", \\export fn entry() void { \\ const a = '\u{}'; \\} - , + , &[_][]const u8{ "tmp.zig:2:19: error: empty unicode escape sequence", - ); + }); - cases.add( - "non-printable invalid character", - "\xff\xfe" ++ - \\fn test() bool {\r - \\ true\r - \\} - , + cases.add("non-printable invalid character", "\xff\xfe" ++ + \\fn test() bool {\r + \\ true\r + \\} + , &[_][]const u8{ "tmp.zig:1:1: error: invalid character: '\\xff'", - ); + }); - cases.add( - "non-printable invalid character with escape alternative", - "fn test() bool {\n" ++ - "\ttrue\n" ++ - "}\n", + cases.add("non-printable invalid character with escape alternative", "fn test() bool {\n" ++ + "\ttrue\n" ++ + "}\n", &[_][]const u8{ "tmp.zig:2:1: error: invalid character: '\\t'", - ); + }); - cases.add( - "@ArgType given non function parameter", + cases.add("@ArgType given non function parameter", \\comptime { \\ _ = @ArgType(i32, 3); \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: expected function, found 'i32'", - ); + }); - cases.add( - "@ArgType arg index out of bounds", + cases.add("@ArgType arg index out of bounds", \\comptime { - \\ _ = @ArgType(@typeOf(add), 2); + \\ _ = @ArgType(@TypeOf(add), 2); \\} \\fn add(a: i32, b: i32) i32 { return a + b; } - , + , &[_][]const u8{ "tmp.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) i32' has 2 arguments", - ); + }); - cases.add( - "@memberType on unsupported type", + cases.add("@memberType on unsupported type", \\comptime { \\ _ = @memberType(i32, 0); \\} - , + , &[_][]const u8{ "tmp.zig:2:21: error: type 'i32' does not support @memberType", - ); + }); - cases.add( - "@memberType on enum", + cases.add("@memberType on enum", \\comptime { \\ _ = @memberType(Foo, 0); \\} \\const Foo = enum {A,}; - , + , &[_][]const u8{ "tmp.zig:2:21: error: type 'Foo' does not support @memberType", - ); + }); - cases.add( - "@memberType struct out of bounds", + cases.add("@memberType struct out of bounds", \\comptime { \\ _ = @memberType(Foo, 0); \\} \\const Foo = struct {}; - , + , &[_][]const u8{ "tmp.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", - ); + }); - cases.add( - "@memberType union out of bounds", + cases.add("@memberType union out of bounds", \\comptime { \\ _ = @memberType(Foo, 1); \\} \\const Foo = union {A: void,}; - , + , &[_][]const u8{ "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", - ); + }); - cases.add( - "@memberName on unsupported type", + cases.add("@memberName on unsupported type", \\comptime { \\ _ = @memberName(i32, 0); \\} - , + , &[_][]const u8{ "tmp.zig:2:21: error: type 'i32' does not support @memberName", - ); + }); - cases.add( - "@memberName struct out of bounds", + cases.add("@memberName struct out of bounds", \\comptime { \\ _ = @memberName(Foo, 0); \\} \\const Foo = struct {}; - , + , &[_][]const u8{ "tmp.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", - ); + }); - cases.add( - "@memberName enum out of bounds", + cases.add("@memberName enum out of bounds", \\comptime { \\ _ = @memberName(Foo, 1); \\} \\const Foo = enum {A,}; - , + , &[_][]const u8{ "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", - ); + }); - cases.add( - "@memberName union out of bounds", + cases.add("@memberName union out of bounds", \\comptime { \\ _ = @memberName(Foo, 1); \\} \\const Foo = union {A:i32,}; - , + , &[_][]const u8{ "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", - ); + }); - cases.add( - "calling var args extern function, passing array instead of pointer", + cases.add("calling var args extern function, passing array instead of pointer", \\export fn entry() void { - \\ foo("hello",); + \\ foo("hello".*,); \\} \\pub extern fn foo(format: *const u8, ...) void; - , - "tmp.zig:2:9: error: expected type '*const u8', found '[5]u8'", - ); + , &[_][]const u8{ + "tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8'", + }); - cases.add( - "constant inside comptime function has compile error", + cases.add("constant inside comptime function has compile error", \\const ContextAllocator = MemoryPool(usize); \\ \\pub fn MemoryPool(comptime T: type) type { @@ -6042,11 +5986,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var allocator: ContextAllocator = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:4:25: error: aoeu", "tmp.zig:1:36: note: referenced here", "tmp.zig:12:20: note: referenced here", - ); + }); cases.add("specify enum tag type that is too small", \\const Small = enum (u2) { @@ -6060,10 +6004,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x = Small.One; \\} - , "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'"); + , &[_][]const u8{ + "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'", + }); - cases.add( - "specify non-integer enum tag type", + cases.add("specify non-integer enum tag type", \\const Small = enum (f32) { \\ One, \\ Two, @@ -6073,12 +6018,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x = Small.One; \\} - , + , &[_][]const u8{ "tmp.zig:1:21: error: expected integer, found 'f32'", - ); + }); - cases.add( - "implicitly casting enum to tag type", + cases.add("implicitly casting enum to tag type", \\const Small = enum(u2) { \\ One, \\ Two, @@ -6089,12 +6033,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x: u2 = Small.Two; \\} - , + , &[_][]const u8{ "tmp.zig:9:22: error: expected type 'u2', found 'Small'", - ); + }); - cases.add( - "explicitly casting non tag type to enum", + cases.add("explicitly casting non tag type to enum", \\const Small = enum(u2) { \\ One, \\ Two, @@ -6103,48 +6046,44 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\}; \\ \\export fn entry() void { - \\ var y = u3(3); + \\ var y = @as(u3, 3); \\ var x = @intToEnum(Small, y); \\} - , + , &[_][]const u8{ "tmp.zig:10:31: error: expected type 'u2', found 'u3'", - ); + }); - cases.add( - "union fields with value assignments", + cases.add("union fields with value assignments", \\const MultipleChoice = union { \\ A: i32 = 20, \\}; \\export fn entry() void { \\ var x: MultipleChoice = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:14: error: untagged union field assignment", "tmp.zig:1:24: note: consider 'union(enum)' here", - ); + }); - cases.add( - "enum with 0 fields", + cases.add("enum with 0 fields", \\const Foo = enum {}; \\export fn entry() usize { \\ return @sizeOf(Foo); \\} - , + , &[_][]const u8{ "tmp.zig:1:13: error: enums must have 1 or more fields", - ); + }); - cases.add( - "union with 0 fields", + cases.add("union with 0 fields", \\const Foo = union {}; \\export fn entry() usize { \\ return @sizeOf(Foo); \\} - , + , &[_][]const u8{ "tmp.zig:1:13: error: unions must have 1 or more fields", - ); + }); - cases.add( - "enum value already taken", + cases.add("enum value already taken", \\const MultipleChoice = enum(u32) { \\ A = 20, \\ B = 40, @@ -6155,13 +6094,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x = MultipleChoice.C; \\} - , + , &[_][]const u8{ "tmp.zig:6:5: error: enum tag value 60 already taken", "tmp.zig:4:5: note: other occurrence here", - ); + }); - cases.add( - "union with specified enum omits field", + cases.add("union with specified enum omits field", \\const Letter = enum { \\ A, \\ B, @@ -6174,50 +6112,46 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() usize { \\ return @sizeOf(Payload); \\} - , + , &[_][]const u8{ "tmp.zig:6:17: error: enum field missing: 'C'", "tmp.zig:4:5: note: declared here", - ); + }); - cases.add( - "@TagType when union has no attached enum", + cases.add("@TagType when union has no attached enum", \\const Foo = union { \\ A: i32, \\}; \\export fn entry() void { \\ const x = @TagType(Foo); \\} - , + , &[_][]const u8{ "tmp.zig:5:24: error: union 'Foo' has no tag", "tmp.zig:1:13: note: consider 'union(enum)' here", - ); + }); - cases.add( - "non-integer tag type to automatic union enum", + cases.add("non-integer tag type to automatic union enum", \\const Foo = union(enum(f32)) { \\ A: i32, \\}; \\export fn entry() void { \\ const x = @TagType(Foo); \\} - , + , &[_][]const u8{ "tmp.zig:1:24: error: expected integer tag type, found 'f32'", - ); + }); - cases.add( - "non-enum tag type passed to union", + cases.add("non-enum tag type passed to union", \\const Foo = union(u32) { \\ A: i32, \\}; \\export fn entry() void { \\ const x = @TagType(Foo); \\} - , + , &[_][]const u8{ "tmp.zig:1:19: error: expected enum tag type, found 'u32'", - ); + }); - cases.add( - "union auto-enum value already taken", + cases.add("union auto-enum value already taken", \\const MultipleChoice = union(enum(u32)) { \\ A = 20, \\ B = 40, @@ -6228,13 +6162,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x = MultipleChoice { .C = {} }; \\} - , + , &[_][]const u8{ "tmp.zig:6:9: error: enum tag value 60 already taken", "tmp.zig:4:9: note: other occurrence here", - ); + }); - cases.add( - "union enum field does not match enum", + cases.add("union enum field does not match enum", \\const Letter = enum { \\ A, \\ B, @@ -6249,13 +6182,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var a = Payload {.A = 1234}; \\} - , + , &[_][]const u8{ "tmp.zig:10:5: error: enum field not found: 'D'", "tmp.zig:1:16: note: enum declared here", - ); + }); - cases.add( - "field type supplied in an enum", + cases.add("field type supplied in an enum", \\const Letter = enum { \\ A: void, \\ B, @@ -6264,37 +6196,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var b = Letter.B; \\} - , + , &[_][]const u8{ "tmp.zig:2:8: error: structs and unions, not enums, support field types", "tmp.zig:1:16: note: consider 'union(enum)' here", - ); + }); - cases.add( - "struct field missing type", + cases.add("struct field missing type", \\const Letter = struct { \\ A, \\}; \\export fn entry() void { \\ var a = Letter { .A = {} }; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: struct field missing type", - ); + }); - cases.add( - "extern union field missing type", + cases.add("extern union field missing type", \\const Letter = extern union { \\ A, \\}; \\export fn entry() void { \\ var a = Letter { .A = {} }; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: union field missing type", - ); + }); - cases.add( - "extern union given enum tag type", + cases.add("extern union given enum tag type", \\const Letter = enum { \\ A, \\ B, @@ -6308,12 +6237,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var a = Payload { .A = 1234 }; \\} - , + , &[_][]const u8{ "tmp.zig:6:30: error: extern union does not support enum tag type", - ); + }); - cases.add( - "packed union given enum tag type", + cases.add("packed union given enum tag type", \\const Letter = enum { \\ A, \\ B, @@ -6327,12 +6255,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var a = Payload { .A = 1234 }; \\} - , + , &[_][]const u8{ "tmp.zig:6:30: error: packed union does not support enum tag type", - ); + }); - cases.add( - "packed union with automatic layout field", + cases.add("packed union with automatic layout field", \\const Foo = struct { \\ a: u32, \\ b: f32, @@ -6344,12 +6271,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var a = Payload { .B = true }; \\} - , + , &[_][]const u8{ "tmp.zig:6:5: error: non-packed, non-extern struct 'Foo' not allowed in packed union; no guaranteed in-memory representation", - ); + }); - cases.add( - "switch on union with no attached enum", + cases.add("switch on union with no attached enum", \\const Payload = union { \\ A: i32, \\ B: f64, @@ -6365,13 +6291,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ else => unreachable, \\ } \\} - , + , &[_][]const u8{ "tmp.zig:11:14: error: switch on union which has no attached enum", "tmp.zig:1:17: note: consider 'union(enum)' here", - ); + }); - cases.add( - "enum in field count range but not matching tag", + cases.add("enum in field count range but not matching tag", \\const Foo = enum(u32) { \\ A = 10, \\ B = 11, @@ -6379,13 +6304,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x = @intToEnum(Foo, 0); \\} - , + , &[_][]const u8{ "tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", "tmp.zig:1:13: note: 'Foo' declared here", - ); + }); - cases.add( - "comptime cast enum to union but field has payload", + cases.add("comptime cast enum to union but field has payload", \\const Letter = enum { A, B, C }; \\const Value = union(Letter) { \\ A: i32, @@ -6395,13 +6319,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var x: Value = Letter.A; \\} - , + , &[_][]const u8{ "tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", "tmp.zig:3:5: note: field 'A' declared here", - ); + }); - cases.add( - "runtime cast to union which has non-void fields", + cases.add("runtime cast to union which has non-void fields", \\const Letter = enum { A, B, C }; \\const Value = union(Letter) { \\ A: i32, @@ -6414,37 +6337,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo(l: Letter) void { \\ var x: Value = l; \\} - , + , &[_][]const u8{ "tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", "tmp.zig:3:5: note: field 'A' has type 'i32'", - ); + }); - cases.add( - "taking byte offset of void field in struct", + cases.add("taking byte offset of void field in struct", \\const Empty = struct { \\ val: void, \\}; \\export fn foo() void { \\ const fieldOffset = @byteOffsetOf(Empty, "val",); \\} - , + , &[_][]const u8{ "tmp.zig:5:46: error: zero-bit field 'val' in struct 'Empty' has no offset", - ); + }); - cases.add( - "taking bit offset of void field in struct", + cases.add("taking bit offset of void field in struct", \\const Empty = struct { \\ val: void, \\}; \\export fn foo() void { \\ const fieldOffset = @bitOffsetOf(Empty, "val",); \\} - , + , &[_][]const u8{ "tmp.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", - ); + }); - cases.add( - "invalid union field access in comptime", + cases.add("invalid union field access in comptime", \\const Foo = union { \\ Bar: u8, \\ Baz: void, @@ -6453,60 +6373,54 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var foo = Foo {.Baz = {}}; \\ const bar_val = foo.Bar; \\} - , + , &[_][]const u8{ "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set", - ); + }); - cases.add( - "getting return type of generic function", + cases.add("getting return type of generic function", \\fn generic(a: var) void {} \\comptime { - \\ _ = @typeOf(generic).ReturnType; + \\ _ = @TypeOf(generic).ReturnType; \\} - , - "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic", - ); + , &[_][]const u8{ + "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var) var' is generic", + }); - cases.add( - "getting @ArgType of generic function", + cases.add("getting @ArgType of generic function", \\fn generic(a: var) void {} \\comptime { - \\ _ = @ArgType(@typeOf(generic), 0); + \\ _ = @ArgType(@TypeOf(generic), 0); \\} - , - "tmp.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", - ); + , &[_][]const u8{ + "tmp.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var) var' is generic", + }); - cases.add( - "unsupported modifier at start of asm output constraint", + cases.add("unsupported modifier at start of asm output constraint", \\export fn foo() void { \\ var bar: u32 = 3; \\ asm volatile ("" : [baz]"+r"(bar) : : ""); \\} - , + , &[_][]const u8{ "tmp.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215", - ); + }); - cases.add( - "comptime_int in asm input", + cases.add("comptime_int in asm input", \\export fn foo() void { \\ asm volatile ("" : : [bar]"r"(3) : ""); \\} - , + , &[_][]const u8{ "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_int", - ); + }); - cases.add( - "comptime_float in asm input", + cases.add("comptime_float in asm input", \\export fn foo() void { \\ asm volatile ("" : : [bar]"r"(3.17) : ""); \\} - , + , &[_][]const u8{ "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_float", - ); + }); - cases.add( - "runtime assignment to comptime struct type", + cases.add("runtime assignment to comptime struct type", \\const Foo = struct { \\ Bar: u8, \\ Baz: type, @@ -6515,12 +6429,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u8 = 0; \\ const foo = Foo { .Bar = x, .Baz = u8 }; \\} - , + , &[_][]const u8{ "tmp.zig:7:23: error: unable to evaluate constant expression", - ); + }); - cases.add( - "runtime assignment to comptime union type", + cases.add("runtime assignment to comptime union type", \\const Foo = union { \\ Bar: u8, \\ Baz: type, @@ -6529,42 +6442,39 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u8 = 0; \\ const foo = Foo { .Bar = x }; \\} - , + , &[_][]const u8{ "tmp.zig:7:23: error: unable to evaluate constant expression", - ); + }); - cases.addTest( - "@shuffle with selected index past first vector length", + cases.addTest("@shuffle with selected index past first vector length", \\export fn entry() void { \\ const v: @Vector(4, u32) = [4]u32{ 10, 11, 12, 13 }; \\ const x: @Vector(4, u32) = [4]u32{ 14, 15, 16, 17 }; \\ var z = @shuffle(u32, v, x, [8]i32{ 0, 1, 2, 3, 7, 6, 5, 4 }); \\} - , + , &[_][]const u8{ "tmp.zig:4:39: error: mask index '4' has out-of-bounds selection", "tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32)", "tmp.zig:4:30: note: selections from the second vector are specified with negative numbers", - ); + }); - cases.addTest( - "nested vectors", + cases.addTest("nested vectors", \\export fn entry() void { \\ const V = @Vector(4, @Vector(4, u8)); \\ var v: V = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:26: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid", - ); + }); - cases.addTest( - "bad @splat type", + cases.addTest("bad @splat type", \\export fn entry() void { \\ const c = 4; \\ var v = @splat(4, c); \\} - , + , &[_][]const u8{ "tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; 'comptime_int' is invalid", - ); + }); cases.add("compileLog of tagged enum doesn't crash the compiler", \\const Bar = union(enum(u32)) { @@ -6578,33 +6488,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub fn main () void { \\ comptime testCompileLog(Bar{.X = 123}); \\} - , "tmp.zig:6:5: error: found compile log statement"); + , &[_][]const u8{ + "tmp.zig:6:5: error: found compile log statement", + }); - cases.add( - "attempted implicit cast from *const T to *[1]T", + cases.add("attempted implicit cast from *const T to *[1]T", \\export fn entry(byte: u8) void { \\ const w: i32 = 1234; \\ var x: *const i32 = &w; \\ var y: *[1]i32 = x; \\ y[0] += 1; \\} - , + , &[_][]const u8{ "tmp.zig:4:22: error: expected type '*[1]i32', found '*const i32'", - "tmp.zig:4:22: note: pointer type child 'i32' cannot cast into pointer type child '[1]i32'", - ); + "tmp.zig:4:22: note: cast discards const qualifier", + }); - cases.add( - "attempted implicit cast from *const T to []T", + cases.add("attempted implicit cast from *const T to []T", \\export fn entry() void { \\ const u: u32 = 42; \\ const x: []u32 = &u; \\} - , + , &[_][]const u8{ "tmp.zig:3:23: error: expected type '[]u32', found '*const u32'", - ); + }); - cases.add( - "for loop body expression ignored", + cases.add("for loop body expression ignored", \\fn returns() usize { \\ return 2; \\} @@ -6615,28 +6524,35 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: anyerror!i32 = error.Bad; \\ for ("hello") |_| returns() else unreachable; \\} - , + , &[_][]const u8{ "tmp.zig:5:30: error: expression value is ignored", "tmp.zig:9:30: error: expression value is ignored", - ); + }); - cases.add( - "aligned variable of zero-bit type", + cases.add("aligned variable of zero-bit type", \\export fn f() void { \\ var s: struct {} align(4) = undefined; \\} - , + , &[_][]const u8{ "tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned", - ); + }); - cases.add( - "function returning opaque type", + cases.add("function returning opaque type", \\const FooType = @OpaqueType(); \\export fn bar() !FooType { \\ return error.InvalidValue; \\} - , + , &[_][]const u8{ "tmp.zig:2:18: error: opaque return type 'FooType' not allowed", "tmp.zig:1:1: note: declared here", - ); + }); + + cases.add( // fixed bug #2032 + "compile diagnostic string for top level decl type", + \\export fn entry() void { + \\ var foo: u32 = @This(){}; + \\} + , &[_][]const u8{ + "tmp.zig:2:27: error: type 'u32' does not support array initialization", + }); } diff --git a/test/gen_h.zig b/test/gen_h.zig index 5979afd66..f519d8feb 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -4,16 +4,15 @@ pub fn addCases(cases: *tests.GenHContext) void { cases.add("declare enum", \\const Foo = extern enum { A, B, C }; \\export fn entry(foo: Foo) void { } - , + , &[_][]const u8{ \\enum Foo { \\ A = 0, \\ B = 1, \\ C = 2 \\}; - \\ - \\TEST_EXTERN_C void entry(enum Foo foo); - \\ - ); + , + \\void entry(enum Foo foo); + }); cases.add("declare struct", \\const Foo = extern struct { @@ -25,7 +24,7 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ F: u64, \\}; \\export fn entry(foo: Foo) void { } - , + , &[_][]const u8{ \\struct Foo { \\ int32_t A; \\ float B; @@ -34,10 +33,10 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ uint64_t E; \\ uint64_t F; \\}; + , + \\void entry(struct Foo foo); \\ - \\TEST_EXTERN_C void entry(struct Foo foo); - \\ - ); + }); cases.add("declare union", \\const Big = extern struct { @@ -54,7 +53,7 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ D: Big, \\}; \\export fn entry(foo: Foo) void {} - , + , &[_][]const u8{ \\struct Big { \\ uint64_t A; \\ uint64_t B; @@ -69,20 +68,20 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ bool C; \\ struct Big D; \\}; + , + \\void entry(union Foo foo); \\ - \\TEST_EXTERN_C void entry(union Foo foo); - \\ - ); + }); cases.add("declare opaque type", - \\export const Foo = @OpaqueType(); + \\const Foo = @OpaqueType(); \\ \\export fn entry(foo: ?*Foo) void { } - , + , &[_][]const u8{ \\struct Foo; - \\ - \\TEST_EXTERN_C void entry(struct Foo * foo); - ); + , + \\void entry(struct Foo * foo); + }); cases.add("array field-type", \\const Foo = extern struct { @@ -90,15 +89,15 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ B: [4]*u32, \\}; \\export fn entry(foo: Foo, bar: [3]u8) void { } - , + , &[_][]const u8{ \\struct Foo { \\ int32_t A[2]; \\ uint32_t * B[4]; \\}; + , + \\void entry(struct Foo foo, uint8_t bar[]); \\ - \\TEST_EXTERN_C void entry(struct Foo foo, uint8_t bar[]); - \\ - ); + }); cases.add("ptr to zig struct", \\const S = struct { @@ -108,11 +107,12 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *S) u8 { \\ return s.a; \\} - , + , &[_][]const u8{ \\struct S; - \\TEST_EXTERN_C uint8_t a(struct S * s); + , + \\uint8_t a(struct S * s); \\ - ); + }); cases.add("ptr to zig union", \\const U = union(enum) { @@ -123,11 +123,12 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *U) u8 { \\ return s.A; \\} - , + , &[_][]const u8{ \\union U; - \\TEST_EXTERN_C uint8_t a(union U * s); + , + \\uint8_t a(union U * s); \\ - ); + }); cases.add("ptr to zig enum", \\const E = enum(u8) { @@ -138,9 +139,10 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *E) u8 { \\ return @enumToInt(s.*); \\} - , + , &[_][]const u8{ \\enum E; - \\TEST_EXTERN_C uint8_t a(enum E * s); + , + \\uint8_t a(enum E * s); \\ - ); + }); } diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig new file mode 100644 index 000000000..d48c3dc5c --- /dev/null +++ b/test/run_translated_c.zig @@ -0,0 +1,182 @@ +const std = @import("std"); +const tests = @import("tests.zig"); +const nl = std.cstr.line_sep; + +pub fn addCases(cases: *tests.RunTranslatedCContext) void { + cases.add("forward declarations", + \\#include + \\int foo(int); + \\int foo(int x) { return x + 1; } + \\int main(int argc, char **argv) { + \\ if (foo(2) != 3) abort(); + \\ return 0; + \\} + , ""); + + cases.add("typedef and function pointer", + \\#include + \\typedef struct _Foo Foo; + \\typedef int Ret; + \\typedef int Param; + \\struct _Foo { Ret (*func)(Param p); }; + \\static Ret add1(Param p) { + \\ return p + 1; + \\} + \\int main(int argc, char **argv) { + \\ Foo strct = { .func = add1 }; + \\ if (strct.func(16) != 17) abort(); + \\ return 0; + \\} + , ""); + + cases.add("ternary operator", + \\#include + \\static int cnt = 0; + \\int foo() { cnt++; return 42; } + \\int main(int argc, char **argv) { + \\ short q = 3; + \\ signed char z0 = q?:1; + \\ if (z0 != 3) abort(); + \\ int z1 = 3?:1; + \\ if (z1 != 3) abort(); + \\ int z2 = foo()?:-1; + \\ if (z2 != 42) abort(); + \\ if (cnt != 1) abort(); + \\ return 0; + \\} + , ""); + + cases.add("switch case", + \\#include + \\int lottery(unsigned int x) { + \\ switch (x) { + \\ case 3: return 0; + \\ case -1: return 3; + \\ case 8 ... 10: return x; + \\ default: return -1; + \\ } + \\} + \\int main(int argc, char **argv) { + \\ if (lottery(2) != -1) abort(); + \\ if (lottery(3) != 0) abort(); + \\ if (lottery(-1) != 3) abort(); + \\ if (lottery(9) != 9) abort(); + \\ return 0; + \\} + , ""); + + cases.add("boolean values and expressions", + \\#include + \\static const _Bool false_val = 0; + \\static const _Bool true_val = 1; + \\void foo(int x, int y) { + \\ _Bool r = x < y; + \\ if (!r) abort(); + \\ _Bool self = foo; + \\ if (self == false_val) abort(); + \\ if (((r) ? 'a' : 'b') != 'a') abort(); + \\} + \\int main(int argc, char **argv) { + \\ foo(2, 5); + \\ if (false_val == true_val) abort(); + \\ return 0; + \\} + , ""); + + cases.add("hello world", + \\#define _NO_CRT_STDIO_INLINE 1 + \\#include + \\int main(int argc, char **argv) { + \\ printf("hello, world!\n"); + \\ return 0; + \\} + , "hello, world!" ++ nl); + + cases.add("anon struct init", + \\#include + \\struct {int a; int b;} x = {1, 2}; + \\int main(int argc, char **argv) { + \\ x.a += 2; + \\ x.b += 1; + \\ if (x.a != 3) abort(); + \\ if (x.b != 3) abort(); + \\ return 0; + \\} + , ""); + + cases.add("casting away const and volatile", + \\void foo(int *a) {} + \\void bar(const int *a) { + \\ foo((int *)a); + \\} + \\void baz(volatile int *a) { + \\ foo((int *)a); + \\} + \\int main(int argc, char **argv) { + \\ int a = 0; + \\ bar((const int *)&a); + \\ baz((volatile int *)&a); + \\ return 0; + \\} + , ""); + + cases.add("anonymous struct & unions", + \\#include + \\#include + \\static struct { struct { uint16_t x, y; }; } x = { 1 }; + \\static struct { union { uint32_t x; uint8_t y; }; } y = { 0x55AA55AA }; + \\int main(int argc, char **argv) { + \\ if (x.x != 1) abort(); + \\ if (x.y != 0) abort(); + \\ if (y.x != 0x55AA55AA) abort(); + \\ if (y.y != 0xAA) abort(); + \\ return 0; + \\} + , ""); + + cases.add("array to pointer decay", + \\#include + \\int main(int argc, char **argv) { + \\ char data[3] = {'a','b','c'}; + \\ if (2[data] != data[2]) abort(); + \\ if ("abc"[1] != data[1]) abort(); + \\ char *as_ptr = data; + \\ if (2[as_ptr] != as_ptr[2]) abort(); + \\ if ("abc"[1] != as_ptr[1]) abort(); + \\ return 0; + \\} + , ""); + + cases.add("struct initializer - packed", + \\#define _NO_CRT_STDIO_INLINE 1 + \\#include + \\#include + \\struct s {uint8_t x,y; + \\ uint32_t z;} __attribute__((packed)) s0 = {1, 2}; + \\int main() { + \\ /* sizeof nor offsetof currently supported */ + \\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast signed array index to unsigned", + \\#include + \\int main(int argc, char **argv) { + \\ int a[10], i = 0; + \\ a[i] = 0; + \\ if (a[i] != 0) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast long long array index to unsigned", + \\#include + \\int main(int argc, char **argv) { + \\ long long a[10], i = 0; + \\ a[i] = 0; + \\ if (a[i] != 0) abort(); + \\ return 0; + \\} + , ""); +} diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 17f0f3230..2217a7f2d 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,140 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("slice sentinel mismatch - optional pointers", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var buf: [4]?*i32 = undefined; + \\ const slice = buf[0..3 :null]; + \\} + ); + + cases.addRuntimeSafety("slice sentinel mismatch - floats", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var buf: [4]f32 = undefined; + \\ const slice = buf[0..3 :1.2]; + \\} + ); + + cases.addRuntimeSafety("pointer slice sentinel mismatch", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var buf: [4]u8 = undefined; + \\ const ptr = buf[0..].ptr; + \\ const slice = ptr[0..3 :0]; + \\} + ); + + cases.addRuntimeSafety("slice slice sentinel mismatch", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var buf: [4]u8 = undefined; + \\ const slice = buf[0..]; + \\ const slice2 = slice[0..3 :0]; + \\} + ); + + cases.addRuntimeSafety("array slice sentinel mismatch", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var buf: [4]u8 = undefined; + \\ const slice = buf[0..3 :0]; + \\} + ); + + cases.addRuntimeSafety("intToPtr with misaligned address", + \\const std = @import("std"); + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "incorrect alignment")) { + \\ std.os.exit(126); // good + \\ } + \\ std.os.exit(0); // test failed + \\} + \\pub fn main() void { + \\ var x: usize = 5; + \\ var y = @intToPtr([*]align(4) u8, x); + \\} + ); + + cases.addRuntimeSafety("resuming a non-suspended function which never been suspended", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\fn foo() void { + \\ var f = async bar(@frame()); + \\ @import("std").os.exit(0); + \\} + \\ + \\fn bar(frame: anyframe) void { + \\ suspend { + \\ resume frame; + \\ } + \\ @import("std").os.exit(0); + \\} + \\ + \\pub fn main() void { + \\ _ = async foo(); + \\} + ); + + cases.addRuntimeSafety("resuming a non-suspended function which has been suspended and resumed", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\fn foo() void { + \\ suspend { + \\ global_frame = @frame(); + \\ } + \\ var f = async bar(@frame()); + \\ @import("std").os.exit(0); + \\} + \\ + \\fn bar(frame: anyframe) void { + \\ suspend { + \\ resume frame; + \\ } + \\ @import("std").os.exit(0); + \\} + \\ + \\var global_frame: anyframe = undefined; + \\pub fn main() void { + \\ _ = async foo(); + \\ resume global_frame; + \\ @import("std").os.exit(0); + \\} + ); + cases.addRuntimeSafety("noasync function call, callee suspends", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); @@ -213,7 +347,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\pub fn main() void { \\ const a = [_]i32{1, 2, 3, 4}; - \\ baz(bar(a)); + \\ baz(bar(&a)); \\} \\fn bar(a: []const i32) i32 { \\ return a[4]; @@ -423,7 +557,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { - \\ const x = widenSlice([_]u8{1, 2, 3, 4, 5}); + \\ const x = widenSlice(&[_]u8{1, 2, 3, 4, 5}); \\ if (x.len == 0) return error.Whatever; \\} \\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig new file mode 100644 index 000000000..ae994a069 --- /dev/null +++ b/test/src/compare_output.zig @@ -0,0 +1,165 @@ +// This is the implementation of the test harness. +// For the actual test cases, see test/compare_output.zig. +const std = @import("std"); +const builtin = std.builtin; +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; +const Mode = builtin.Mode; + +pub const CompareOutputContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + modes: []const Mode, + + const Special = enum { + None, + Asm, + RuntimeSafety, + }; + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_output: []const u8, + link_libc: bool, + special: Special, + cli_args: []const []const u8, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + + pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { + self.cli_args = args; + } + }; + + pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { + var tc = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_output = expected_output, + .link_libc = false, + .special = special, + .cli_args = &[_][]const u8{}, + }; + const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; + tc.addSourceFile(root_src_name, source); + return tc; + } + + pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { + return createExtra(self, name, source, expected_output, Special.None); + } + + pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + var tc = self.create(name, source, expected_output); + tc.link_libc = true; + self.addCase(tc); + } + + pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.create(name, source, expected_output); + self.addCase(tc); + } + + pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + const tc = self.createExtra(name, source, expected_output, Special.Asm); + self.addCase(tc); + } + + pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { + const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); + self.addCase(tc); + } + + pub fn addCase(self: *CompareOutputContext, case: TestCase) void { + const b = self.b; + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + + switch (case.special) { + Special.Asm => { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + case.name, + }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const exe = b.addExecutable("test", null); + exe.addAssemblyFileFromWriteFileStep(write_src, case.sources.toSliceConst()[0].filename); + + const run = exe.run(); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + }, + Special.None => { + for (self.modes) |mode| { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + "compare-output", + case.name, + @tagName(mode), + }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; + } + + const basename = case.sources.toSliceConst()[0].filename; + const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); + exe.setBuildMode(mode); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.addArgs(case.cli_args); + run.expectStdErrEqual(""); + run.expectStdOutEqual(case.expected_output); + + self.step.dependOn(&run.step); + } + }, + Special.RuntimeSafety => { + const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const basename = case.sources.toSliceConst()[0].filename; + const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); + if (case.link_libc) { + exe.linkSystemLibrary("c"); + } + + const run = exe.run(); + run.addArgs(case.cli_args); + run.stderr_action = .ignore; + run.stdout_action = .ignore; + run.expected_exit_code = 126; + + self.step.dependOn(&run.step); + }, + } + } +}; diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig new file mode 100644 index 000000000..14b0ce593 --- /dev/null +++ b/test/src/run_translated_c.zig @@ -0,0 +1,107 @@ +// This is the implementation of the test harness for running translated +// C code. For the actual test cases, see test/run_translated_c.zig. +const std = @import("std"); +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; + +pub const RunTranslatedCContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_stdout: []const u8, + allow_warnings: bool, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + }; + + pub fn create( + self: *RunTranslatedCContext, + allow_warnings: bool, + filename: []const u8, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) *TestCase { + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_stdout = expected_stdout, + .allow_warnings = allow_warnings, + }; + + tc.addSourceFile(filename, source); + return tc; + } + + pub fn add( + self: *RunTranslatedCContext, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) void { + const tc = self.create(false, "source.c", name, source, expected_stdout); + self.addCase(tc); + } + + pub fn addAllowWarnings( + self: *RunTranslatedCContext, + name: []const u8, + source: []const u8, + expected_stdout: []const u8, + ) void { + const tc = self.create(true, "source.c", name, source, expected_stdout); + self.addCase(tc); + } + + pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { + const b = self.b; + + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + const translate_c = b.addTranslateC(.{ + .write_file = .{ + .step = write_src, + .basename = case.sources.toSliceConst()[0].filename, + }, + }); + translate_c.step.name = b.fmt("{} translate-c", .{annotated_case_name}); + const exe = translate_c.addExecutable(); + exe.step.name = b.fmt("{} build-exe", .{annotated_case_name}); + exe.linkLibC(); + const run = exe.run(); + run.step.name = b.fmt("{} run", .{annotated_case_name}); + if (!case.allow_warnings) { + run.expectStdErrEqual(""); + } + run.expectStdOutEqual(case.expected_stdout); + + self.step.dependOn(&run.step); + } +}; diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig new file mode 100644 index 000000000..21aa92537 --- /dev/null +++ b/test/src/translate_c.zig @@ -0,0 +1,124 @@ +// This is the implementation of the test harness. +// For the actual test cases, see test/translate_c.zig. +const std = @import("std"); +const build = std.build; +const ArrayList = std.ArrayList; +const fmt = std.fmt; +const mem = std.mem; +const fs = std.fs; +const warn = std.debug.warn; + +pub const TranslateCContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + + const TestCase = struct { + name: []const u8, + sources: ArrayList(SourceFile), + expected_lines: ArrayList([]const u8), + allow_warnings: bool, + target: std.Target = .Native, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { + self.sources.append(SourceFile{ + .filename = filename, + .source = source, + }) catch unreachable; + } + + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { + self.expected_lines.append(text) catch unreachable; + } + }; + + pub fn create( + self: *TranslateCContext, + allow_warnings: bool, + filename: []const u8, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) *TestCase { + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ + .name = name, + .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), + .expected_lines = ArrayList([]const u8).init(self.b.allocator), + .allow_warnings = allow_warnings, + }; + + tc.addSourceFile(filename, source); + var arg_i: usize = 0; + while (arg_i < expected_lines.len) : (arg_i += 1) { + tc.addExpectedLine(expected_lines[arg_i]); + } + return tc; + } + + pub fn add( + self: *TranslateCContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { + const tc = self.create(false, "source.h", name, source, expected_lines); + self.addCase(tc); + } + + pub fn addWithTarget( + self: *TranslateCContext, + name: []const u8, + target: std.Target, + source: []const u8, + expected_lines: []const []const u8, + ) void { + const tc = self.create(false, "source.h", name, source, expected_lines); + tc.target = target; + self.addCase(tc); + } + + pub fn addAllowWarnings( + self: *TranslateCContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { + const tc = self.create(true, "source.h", name, source, expected_lines); + self.addCase(tc); + } + + pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { + const b = self.b; + + const translate_c_cmd = "translate-c"; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) return; + } + + const write_src = b.addWriteFiles(); + for (case.sources.toSliceConst()) |src_file| { + write_src.add(src_file.filename, src_file.source); + } + + const translate_c = b.addTranslateC(.{ + .write_file = .{ + .step = write_src, + .basename = case.sources.toSliceConst()[0].filename, + }, + }); + translate_c.step.name = annotated_case_name; + translate_c.setTarget(case.target); + + const check_file = translate_c.addCheckFile(case.expected_lines.toSliceConst()); + + self.step.dependOn(&check_file.step); + } +}; diff --git a/test/stack_traces.zig b/test/stack_traces.zig index ab00f7491..ad4a34fe4 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -55,7 +55,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in std.special.main (test) + \\source.zig:4:5: [address] in std.start.main (test) \\ , // release-fast @@ -79,8 +79,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in std.special.main (test) - \\source.zig:8:5: [address] in std.special.main (test) + \\source.zig:4:5: [address] in std.start.main (test) + \\source.zig:8:5: [address] in std.start.main (test) \\ , // release-fast @@ -106,10 +106,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:12:5: [address] in std.special.main (test) - \\source.zig:8:5: [address] in std.special.main (test) - \\source.zig:4:5: [address] in std.special.main (test) - \\source.zig:16:5: [address] in std.special.main (test) + \\source.zig:12:5: [address] in std.start.main (test) + \\source.zig:8:5: [address] in std.start.main (test) + \\source.zig:4:5: [address] in std.start.main (test) + \\source.zig:16:5: [address] in std.start.main (test) \\ , // release-fast @@ -134,7 +134,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) \\ , // release-fast @@ -158,8 +158,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) - \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) + \\source.zig:8:5: [address] in std.start.posixCallMainAndExit (test) \\ , // release-fast @@ -185,10 +185,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , // release-safe \\error: TheSkyIsFalling - \\source.zig:12:5: [address] in std.special.posixCallMainAndExit (test) - \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test) - \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) - \\source.zig:16:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:12:5: [address] in std.start.posixCallMainAndExit (test) + \\source.zig:8:5: [address] in std.start.posixCallMainAndExit (test) + \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) + \\source.zig:16:5: [address] in std.start.posixCallMainAndExit (test) \\ , // release-fast diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 8696ddfaa..ea8720d98 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -32,8 +32,13 @@ comptime { _ = @import("behavior/bugs/2346.zig"); _ = @import("behavior/bugs/2578.zig"); _ = @import("behavior/bugs/2692.zig"); + _ = @import("behavior/bugs/2889.zig"); + _ = @import("behavior/bugs/3007.zig"); _ = @import("behavior/bugs/3046.zig"); _ = @import("behavior/bugs/3112.zig"); + _ = @import("behavior/bugs/3367.zig"); + _ = @import("behavior/bugs/3384.zig"); + _ = @import("behavior/bugs/3742.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); @@ -48,6 +53,7 @@ comptime { _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/call.zig"); _ = @import("behavior/cast.zig"); _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/defer.zig"); @@ -97,6 +103,7 @@ comptime { _ = @import("behavior/this.zig"); _ = @import("behavior/truncate.zig"); _ = @import("behavior/try.zig"); + _ = @import("behavior/tuple.zig"); _ = @import("behavior/type.zig"); _ = @import("behavior/type_info.zig"); _ = @import("behavior/typename.zig"); diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig index 266e5d351..c83d2379b 100644 --- a/test/stage1/behavior/align.zig +++ b/test/stage1/behavior/align.zig @@ -5,10 +5,10 @@ const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - expect(@typeOf(&foo).alignment == 4); - expect(@typeOf(&foo) == *align(4) u8); - const slice = (*[1]u8)(&foo)[0..]; - expect(@typeOf(slice) == []align(4) u8); + expect(@TypeOf(&foo).alignment == 4); + expect(@TypeOf(&foo) == *align(4) u8); + const slice = @as(*[1]u8, &foo)[0..]; + expect(@TypeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -19,8 +19,8 @@ fn noop4() align(4) void {} test "function alignment" { expect(derp() == 1234); - expect(@typeOf(noop1) == fn () align(1) void); - expect(@typeOf(noop4) == fn () align(4) void); + expect(@TypeOf(noop1) == fn () align(1) void); + expect(@TypeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -31,7 +31,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - expect(@typeOf(&baz.b) == *align(1) u32); + expect(@TypeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -41,7 +41,7 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - expect(@typeOf(&blah.b) == *align(1:3:1) const u3); + expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); } test "default alignment allows unspecified in type syntax" { @@ -61,7 +61,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - expect(addUnalignedSlice((*const [1]u32)(&a)[0..], (*const [1]u32)(&b)[0..]) == 7); + expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; @@ -165,28 +165,28 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; const ptr = @ptrCast(*u8, &x); - expect(@typeOf(ptr) == *align(16) u8); + expect(@TypeOf(ptr) == *align(16) u8); } test "runtime known array index has best alignment possible" { // take full advantage of over-alignment var array align(4) = [_]u8{ 1, 2, 3, 4 }; - expect(@typeOf(&array[0]) == *align(4) u8); - expect(@typeOf(&array[1]) == *u8); - expect(@typeOf(&array[2]) == *align(2) u8); - expect(@typeOf(&array[3]) == *u8); + expect(@TypeOf(&array[0]) == *align(4) u8); + expect(@TypeOf(&array[1]) == *u8); + expect(@TypeOf(&array[2]) == *align(2) u8); + expect(@TypeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = [_]u64{ 1, 2, 3, 4 }; - expect(@typeOf(&bigger[0]) == *align(2) u64); - expect(@typeOf(&bigger[1]) == *align(2) u64); - expect(@typeOf(&bigger[2]) == *align(2) u64); - expect(@typeOf(&bigger[3]) == *align(2) u64); + expect(@TypeOf(&bigger[0]) == *align(2) u64); + expect(@TypeOf(&bigger[1]) == *align(2) u64); + expect(@TypeOf(&bigger[2]) == *align(2) u64); + expect(@TypeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = [_]u32{ 1, 2, 3, 4 }; - comptime expect(@typeOf(smaller[0..]) == []align(2) u32); - comptime expect(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + comptime expect(@TypeOf(smaller[0..]) == []align(2) u32); + comptime expect(@TypeOf(smaller[0..].ptr) == [*]align(2) u32); testIndex(smaller[0..].ptr, 0, *align(2) u32); testIndex(smaller[0..].ptr, 1, *align(2) u32); testIndex(smaller[0..].ptr, 2, *align(2) u32); @@ -199,10 +199,10 @@ test "runtime known array index has best alignment possible" { testIndex2(array[0..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { - comptime expect(@typeOf(&smaller[index]) == T); + comptime expect(@TypeOf(&smaller[index]) == T); } fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime expect(@typeOf(&ptr[index]) == T); + comptime expect(@TypeOf(&ptr[index]) == T); } test "alignstack" { @@ -221,14 +221,14 @@ test "alignment of structs" { }) == @alignOf(usize)); } -test "alignment of extern() void" { +test "alignment of function with c calling convention" { var runtime_nothing = nothing; const casted1 = @ptrCast(*const u8, runtime_nothing); - const casted2 = @ptrCast(extern fn () void, casted1); + const casted2 = @ptrCast(fn () callconv(.C) void, casted1); casted2(); } -extern fn nothing() void {} +fn nothing() callconv(.C) void {} test "return error union with 128-bit integer" { expect(3 == try give()); @@ -303,7 +303,7 @@ test "struct field explicit alignment" { var node: S.Node = undefined; node.massive_byte = 100; expect(node.massive_byte == 100); - comptime expect(@typeOf(&node.massive_byte) == *align(64) u8); + comptime expect(@TypeOf(&node.massive_byte) == *align(64) u8); expect(@ptrToInt(&node.massive_byte) % 64 == 0); } @@ -328,3 +328,10 @@ test "align(@alignOf(T)) T does not force resolution of T" { _ = async S.doTheTest(); expect(S.ok); } + +test "align(N) on functions" { + expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0); +} +fn overaligned_fn() align(0x1000) i32 { + return 42; +} diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index 462977066..7ac1e8197 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -12,7 +12,7 @@ test "arrays" { } i = 0; - var accumulator = u32(0); + var accumulator = @as(u32, 0); while (i < 5) { accumulator += array[i]; @@ -20,7 +20,7 @@ test "arrays" { } expect(accumulator == 15); - expect(getArrayLen(array) == 5); + expect(getArrayLen(&array) == 5); } fn getArrayLen(a: []const u32) usize { return a.len; @@ -30,7 +30,7 @@ test "void arrays" { var array: [4]void = undefined; array[0] = void{}; array[1] = array[2]; - expect(@sizeOf(@typeOf(array)) == 0); + expect(@sizeOf(@TypeOf(array)) == 0); expect(array.len == 4); } @@ -109,12 +109,12 @@ test "array literal with specified size" { test "array child property" { var x: [5]i32 = undefined; - expect(@typeOf(x).Child == i32); + expect(@TypeOf(x).Child == i32); } test "array len property" { var x: [5]i32 = undefined; - expect(@typeOf(x).len == 5); + expect(@TypeOf(x).len == 5); } test "array len field" { @@ -132,9 +132,16 @@ test "single-item pointer to array indexing and slicing" { } fn testSingleItemPtrArrayIndexSlice() void { - var array = "aaaa"; - doSomeMangling(&array); - expect(mem.eql(u8, "azya", array)); + { + var array: [4]u8 = "aaaa".*; + doSomeMangling(&array); + expect(mem.eql(u8, "azya", &array)); + } + { + var array = "aaaa".*; + doSomeMangling(&array); + expect(mem.eql(u8, "azya", &array)); + } } fn doSomeMangling(array: *[4]u8) void { @@ -149,7 +156,7 @@ test "implicit cast single-item pointer" { fn testImplicitCastSingleItemPtr() void { var byte: u8 = 100; - const slice = (*[1]u8)(&byte)[0..]; + const slice = @as(*[1]u8, &byte)[0..]; slice[0] += 1; expect(byte == 101); } @@ -175,29 +182,29 @@ fn plusOne(x: u32) u32 { test "runtime initialize array elem and then implicit cast to slice" { var two: i32 = 2; - const x: []const i32 = [_]i32{two}; + const x: []const i32 = &[_]i32{two}; expect(x[0] == 2); } test "array literal as argument to function" { const S = struct { fn entry(two: i32) void { - foo([_]i32{ + foo(&[_]i32{ 1, 2, 3, }); - foo([_]i32{ + foo(&[_]i32{ 1, two, 3, }); - foo2(true, [_]i32{ + foo2(true, &[_]i32{ 1, 2, 3, }); - foo2(true, [_]i32{ + foo2(true, &[_]i32{ 1, two, 3, @@ -223,17 +230,17 @@ test "double nested array to const slice cast in array literal" { const S = struct { fn entry(two: i32) void { const cases = [_][]const []const i32{ - [_][]const i32{[_]i32{1}}, - [_][]const i32{[_]i32{ 2, 3 }}, - [_][]const i32{ - [_]i32{4}, - [_]i32{ 5, 6, 7 }, + &[_][]const i32{&[_]i32{1}}, + &[_][]const i32{&[_]i32{ 2, 3 }}, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, }, }; - check(cases); + check(&cases); const cases2 = [_][]const i32{ - [_]i32{1}, + &[_]i32{1}, &[_]i32{ two, 3 }, }; expect(cases2.len == 2); @@ -244,14 +251,14 @@ test "double nested array to const slice cast in array literal" { expect(cases2[1][1] == 3); const cases3 = [_][]const []const i32{ - [_][]const i32{[_]i32{1}}, + &[_][]const i32{&[_]i32{1}}, &[_][]const i32{&[_]i32{ two, 3 }}, - [_][]const i32{ - [_]i32{4}, - [_]i32{ 5, 6, 7 }, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, }, }; - check(cases3); + check(&cases3); } fn check(cases: []const []const []const i32) void { @@ -294,7 +301,62 @@ test "read/write through global variable array of struct fields initialized via } test "implicit cast zero sized array ptr to slice" { - var b = ""; - const c: []const u8 = &b; - expect(c.len == 0); + { + var b = "".*; + const c: []const u8 = &b; + expect(c.len == 0); + } + { + var b: [0]u8 = "".*; + const c: []const u8 = &b; + expect(c.len == 0); + } +} + +test "anonymous list literal syntax" { + const S = struct { + fn doTheTest() void { + var array: [4]u8 = .{ 1, 2, 3, 4 }; + expect(array[0] == 1); + expect(array[1] == 2); + expect(array[2] == 3); + expect(array[3] == 4); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "anonymous literal in array" { + const S = struct { + const Foo = struct { + a: usize = 2, + b: usize = 4, + }; + fn doTheTest() void { + var array: [2]Foo = .{ + .{ .a = 3 }, + .{ .b = 3 }, + }; + expect(array[0].a == 3); + expect(array[0].b == 4); + expect(array[1].a == 2); + expect(array[1].b == 3); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "access the null element of a null terminated array" { + const S = struct { + fn doTheTest() void { + var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; + comptime expect(array[4] == 0); + var len: usize = 4; + expect(array[len] == 0); + } + }; + S.doTheTest(); + comptime S.doTheTest(); } diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig index 3fe0654a6..d1404c6f4 100644 --- a/test/stage1/behavior/asm.zig +++ b/test/stage1/behavior/asm.zig @@ -1,5 +1,6 @@ +const std = @import("std"); const config = @import("builtin"); -const expect = @import("std").testing.expect; +const expect = std.testing.expect; comptime { if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { @@ -45,42 +46,42 @@ test "alternative constraints" { test "sized integer/float in asm input" { asm volatile ("" : - : [_] "m" (usize(3)) + : [_] "m" (@as(usize, 3)) : "" ); asm volatile ("" : - : [_] "m" (i15(-3)) + : [_] "m" (@as(i15, -3)) : "" ); asm volatile ("" : - : [_] "m" (u3(3)) + : [_] "m" (@as(u3, 3)) : "" ); asm volatile ("" : - : [_] "m" (i3(3)) + : [_] "m" (@as(i3, 3)) : "" ); asm volatile ("" : - : [_] "m" (u121(3)) + : [_] "m" (@as(u121, 3)) : "" ); asm volatile ("" : - : [_] "m" (i121(3)) + : [_] "m" (@as(i121, 3)) : "" ); asm volatile ("" : - : [_] "m" (f32(3.17)) + : [_] "m" (@as(f32, 3.17)) : "" ); asm volatile ("" : - : [_] "m" (f64(3.17)) + : [_] "m" (@as(f64, 3.17)) : "" ); } diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 8445bcb5b..1b21ba7ef 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -143,7 +143,7 @@ test "coroutine suspend, resume" { resume frame; seq('h'); - expect(std.mem.eql(u8, points, "abcdefgh")); + expect(std.mem.eql(u8, &points, "abcdefgh")); } fn amain() void { @@ -185,13 +185,13 @@ var a_promise: anyframe = undefined; var global_result = false; async fn testSuspendBlock() void { suspend { - comptime expect(@typeOf(@frame()) == *@Frame(testSuspendBlock)); + comptime expect(@TypeOf(@frame()) == *@Frame(testSuspendBlock)); a_promise = @frame(); } // Test to make sure that @frame() works as advertised (issue #1296) // var our_handle: anyframe = @frame(); - expect(a_promise == anyframe(@frame())); + expect(a_promise == @as(anyframe, @frame())); global_result = true; } @@ -206,7 +206,7 @@ test "coroutine await" { resume await_a_promise; await_seq('i'); expect(await_final_result == 1234); - expect(std.mem.eql(u8, await_points, "abcdefghi")); + expect(std.mem.eql(u8, &await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -240,7 +240,7 @@ test "coroutine await early return" { var p = async early_amain(); early_seq('f'); expect(early_final_result == 1234); - expect(std.mem.eql(u8, early_points, "abcdef")); + expect(std.mem.eql(u8, &early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -282,7 +282,7 @@ test "async fn pointer in a struct field" { var foo = Foo{ .bar = simpleAsyncFn2 }; var bytes: [64]u8 align(16) = undefined; const f = @asyncCall(&bytes, {}, foo.bar, &data); - comptime expect(@typeOf(f) == anyframe->void); + comptime expect(@TypeOf(f) == anyframe->void); expect(data == 2); resume f; expect(data == 4); @@ -332,7 +332,7 @@ test "async fn with inferred error set" { fn doTheTest() void { var frame: [1]@Frame(middle) = undefined; var fn_ptr = middle; - var result: @typeOf(fn_ptr).ReturnType.ErrorSet!void = undefined; + var result: @TypeOf(fn_ptr).ReturnType.ErrorSet!void = undefined; _ = @asyncCall(@sliceToBytes(frame[0..]), &result, fn_ptr); resume global_frame; std.testing.expectError(error.Fail, result); @@ -410,8 +410,8 @@ test "heap allocated async function frame" { var x: i32 = 42; fn doTheTest() !void { - const frame = try std.heap.direct_allocator.create(@Frame(someFunc)); - defer std.heap.direct_allocator.destroy(frame); + const frame = try std.heap.page_allocator.create(@Frame(someFunc)); + defer std.heap.page_allocator.destroy(frame); expect(x == 42); frame.* = async someFunc(); @@ -543,7 +543,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); suspend; - expectEqual(([]const u8)("hello"), msg); + expectEqual(@as([]const u8, "hello"), msg); ok = true; } }; @@ -671,7 +671,7 @@ fn testAsyncAwaitTypicalUsage( } fn amain() !void { - const allocator = std.heap.direct_allocator; // TODO once we have the debug allocator, use that, so that this can detect leaks + const allocator = std.heap.page_allocator; // TODO once we have the debug allocator, use that, so that this can detect leaks var download_frame = async fetchUrl(allocator, "https://example.com/"); var download_awaited = false; errdefer if (!download_awaited) { @@ -935,12 +935,12 @@ fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type { _ = async amain(&result); return result; } else { - return fib(std.heap.direct_allocator, 10) catch unreachable; + return fib(std.heap.page_allocator, 10) catch unreachable; } } fn amain(result: *u32) void { - var x = async fib(std.heap.direct_allocator, 10); + var x = async fib(std.heap.page_allocator, 10); result.* = (await x) catch unreachable; } }; @@ -952,7 +952,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { fn doTheTest() void { var frame: [1]@Frame(middle) = undefined; - var result: @typeOf(middle).ReturnType.ErrorSet!void = undefined; + var result: @TypeOf(middle).ReturnType.ErrorSet!void = undefined; _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle); resume global_frame; std.testing.expectError(error.Fail, result); @@ -1009,7 +1009,7 @@ test "@asyncCall using the result location inside the frame" { var foo = Foo{ .bar = S.simple2 }; var bytes: [64]u8 align(16) = undefined; const f = @asyncCall(&bytes, {}, foo.bar, &data); - comptime expect(@typeOf(f) == anyframe->i32); + comptime expect(@TypeOf(f) == anyframe->i32); expect(data == 2); resume f; expect(data == 4); @@ -1017,18 +1017,18 @@ test "@asyncCall using the result location inside the frame" { expect(data == 1234); } -test "@typeOf an async function call of generic fn with error union type" { +test "@TypeOf an async function call of generic fn with error union type" { const S = struct { fn func(comptime x: var) anyerror!i32 { - const T = @typeOf(async func(x)); - comptime expect(T == @typeOf(@frame()).Child); + const T = @TypeOf(async func(x)); + comptime expect(T == @TypeOf(@frame()).Child); return undefined; } }; _ = async S.func(i32); } -test "using @typeOf on a generic function call" { +test "using @TypeOf on a generic function call" { const S = struct { var global_frame: anyframe = undefined; var global_ok = false; @@ -1043,12 +1043,12 @@ test "using @typeOf on a generic function call" { suspend { global_frame = @frame(); } - const F = @typeOf(async amain(x - 1)); + const F = @TypeOf(async amain(x - 1)); const frame = @intToPtr(*F, @ptrToInt(&buf)); return await @asyncCall(frame, {}, amain, x - 1); } }; - _ = async S.amain(u32(1)); + _ = async S.amain(@as(u32, 1)); resume S.global_frame; expect(S.global_ok); } @@ -1068,7 +1068,7 @@ test "recursive call of await @asyncCall with struct return type" { suspend { global_frame = @frame(); } - const F = @typeOf(async amain(x - 1)); + const F = @TypeOf(async amain(x - 1)); const frame = @intToPtr(*F, @ptrToInt(&buf)); return await @asyncCall(frame, {}, amain, x - 1); } @@ -1080,8 +1080,8 @@ test "recursive call of await @asyncCall with struct return type" { }; }; var res: S.Foo = undefined; - var frame: @typeOf(async S.amain(u32(1))) = undefined; - _ = @asyncCall(&frame, &res, S.amain, u32(1)); + var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined; + _ = @asyncCall(&frame, &res, S.amain, @as(u32, 1)); resume S.global_frame; expect(S.global_ok); expect(res.x == 1); @@ -1132,7 +1132,9 @@ test "await used in expression after a fn call" { async fn add(a: i32, b: i32) i32 { return a + b; } - fn foo() i32 { return 1; } + fn foo() i32 { + return 1; + } }; _ = async S.atest(); } @@ -1147,7 +1149,9 @@ test "async fn call used in expression after a fn call" { async fn add(a: i32, b: i32) i32 { return a + b; } - fn foo() i32 { return 1; } + fn foo() i32 { + return 1; + } }; _ = async S.atest(); } @@ -1162,7 +1166,7 @@ test "suspend in for loop" { } fn atest() void { - expect(func([_]u8{ 1, 2, 3 }) == 6); + expect(func(&[_]u8{ 1, 2, 3 }) == 6); } fn func(stuff: []const u8) u32 { global_frame = @frame(); @@ -1201,20 +1205,19 @@ test "correctly spill when returning the error union result of another async fn" resume S.global_frame; } - test "spill target expr in a for loop" { const S = struct { var global_frame: anyframe = undefined; fn doTheTest() void { var foo = Foo{ - .slice = [_]i32{1, 2}, + .slice = &[_]i32{ 1, 2 }, }; expect(atest(&foo) == 3); } const Foo = struct { - slice: []i32, + slice: []const i32, }; fn atest(foo: *Foo) i32 { @@ -1239,13 +1242,13 @@ test "spill target expr in a for loop, with a var decl in the loop body" { fn doTheTest() void { var foo = Foo{ - .slice = [_]i32{1, 2}, + .slice = &[_]i32{ 1, 2 }, }; expect(atest(&foo) == 3); } const Foo = struct { - slice: []i32, + slice: []const i32, }; fn atest(foo: *Foo) i32 { @@ -1268,3 +1271,25 @@ test "spill target expr in a for loop, with a var decl in the loop body" { resume S.global_frame; resume S.global_frame; } + +test "async call with @call" { + const S = struct { + var global_frame: anyframe = undefined; + fn doTheTest() void { + _ = @call(.{ .modifier = .async_kw }, atest, .{}); + resume global_frame; + } + fn atest() void { + var frame = @call(.{ .modifier = .async_kw }, afoo, .{}); + const res = await frame; + expect(res == 42); + } + fn afoo() i32 { + suspend { + global_frame = @frame(); + } + return 42; + } + }; + S.doTheTest(); +} diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig index 1a941cf21..7155e8009 100644 --- a/test/stage1/behavior/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -98,5 +98,66 @@ test "cmpxchg with ignored result" { _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); - expectEqual(i32(5678), x); + expectEqual(@as(i32, 5678), x); +} + +var a_global_variable = @as(u32, 1234); + +test "cmpxchg on a global variable" { + _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); + expectEqual(@as(u32, 42), a_global_variable); +} + +test "atomic load and rmw with enum" { + const Value = enum(u8) { + a, + b, + c, + }; + var x = Value.a; + + expect(@atomicLoad(Value, &x, .SeqCst) != .b); + + _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); + expect(@atomicLoad(Value, &x, .SeqCst) == .c); + expect(@atomicLoad(Value, &x, .SeqCst) != .a); + expect(@atomicLoad(Value, &x, .SeqCst) != .b); +} + +test "atomic store" { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .SeqCst); + expect(@atomicLoad(u32, &x, .SeqCst) == 1); + @atomicStore(u32, &x, 12345678, .SeqCst); + expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); +} + +test "atomic store comptime" { + comptime testAtomicStore(); + testAtomicStore(); +} + +fn testAtomicStore() void { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .SeqCst); + expect(@atomicLoad(u32, &x, .SeqCst) == 1); + @atomicStore(u32, &x, 12345678, .SeqCst); + expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); +} + +test "atomicrmw with floats" { + if (builtin.arch == .aarch64 or builtin.arch == .arm) + return; + testAtomicRmwFloat(); +} + +fn testAtomicRmwFloat() void { + var x: f32 = 0; + expect(x == 0); + _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); + expect(x == 1); + _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); + expect(x == 6); + _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); + expect(x == 4); } diff --git a/test/stage1/behavior/await_struct.zig b/test/stage1/behavior/await_struct.zig index 6e4d330ea..2d4faadc2 100644 --- a/test/stage1/behavior/await_struct.zig +++ b/test/stage1/behavior/await_struct.zig @@ -16,7 +16,7 @@ test "coroutine await struct" { resume await_a_promise; await_seq('i'); expect(await_final_result.x == 1234); - expect(std.mem.eql(u8, await_points, "abcdefghi")); + expect(std.mem.eql(u8, &await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/stage1/behavior/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig index 3c5d4aa64..84d5f8f60 100644 --- a/test/stage1/behavior/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -96,3 +96,7 @@ test "comptime shr of BigInt" { std.debug.assert(n1 >> 64 == 0); } } + +test "comptime shift safety check" { + const x = @as(usize, 42) << @sizeOf(usize); +} diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index ceb538c09..fe3dd6902 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -146,3 +146,24 @@ test "comptime bitcast used in expression has the correct type" { }; expect(@bitCast(u8, Foo{ .value = 0xF }) == 0xf); } + +test "bitcast result to _" { + _ = @bitCast(u8, @as(i8, 1)); +} + +test "nested bitcast" { + const S = struct { + fn moo(x: isize) void { + @import("std").testing.expectEqual(@intCast(isize, 42), x); + } + + fn foo(x: isize) void { + @This().moo( + @bitCast(isize, if (x != 0) @bitCast(usize, x) else @bitCast(usize, x)), + ); + } + }; + + S.foo(42); + comptime S.foo(42); +} diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig index 2b0eb71fb..8de2d5d2c 100644 --- a/test/stage1/behavior/bitreverse.zig +++ b/test/stage1/behavior/bitreverse.zig @@ -46,24 +46,24 @@ fn testBitReverse() void { expect(@bitReverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); // using comptime_ints, signed, positive - expect(@bitReverse(u8, u8(0)) == 0); - expect(@bitReverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8(0x49))); - expect(@bitReverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x2c48))); - expect(@bitReverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x6a2c48))); - expect(@bitReverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x1e6a2c48))); - expect(@bitReverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x591e6a2c48))); - expect(@bitReverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0x3d591e6a2c48))); - expect(@bitReverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0x7b3d591e6a2c48))); - expect(@bitReverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0x8f7b3d591e6a2c48))); - expect(@bitReverse(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == @bitCast(i128, u128(0x818e868a828c84888f7b3d591e6a2c48))); + expect(@bitReverse(u8, @as(u8, 0)) == 0); + expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49))); + expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48))); + expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x123456))) == @bitCast(i24, @as(u24, 0x6a2c48))); + expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x12345678))) == @bitCast(i32, @as(u32, 0x1e6a2c48))); + expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48))); + expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48))); + expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); + expect(@bitReverse(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1))) == @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); + expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); // using signed, negative. Compare to runtime ints returned from llvm. var neg8: i8 = -18; - expect(@bitReverse(i8, i8(-18)) == @bitReverse(i8, neg8)); + expect(@bitReverse(i8, @as(i8, -18)) == @bitReverse(i8, neg8)); var neg16: i16 = -32694; - expect(@bitReverse(i16, i16(-32694)) == @bitReverse(i16, neg16)); + expect(@bitReverse(i16, @as(i16, -32694)) == @bitReverse(i16, neg16)); var neg24: i24 = -6773785; - expect(@bitReverse(i24, i24(-6773785)) == @bitReverse(i24, neg24)); + expect(@bitReverse(i24, @as(i24, -6773785)) == @bitReverse(i24, neg24)); var neg32: i32 = -16773785; - expect(@bitReverse(i32, i32(-16773785)) == @bitReverse(i32, neg32)); + expect(@bitReverse(i32, @as(i32, -16773785)) == @bitReverse(i32, neg32)); } diff --git a/test/stage1/behavior/bool.zig b/test/stage1/behavior/bool.zig index dfc227900..ef9383244 100644 --- a/test/stage1/behavior/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -8,14 +8,14 @@ test "bool literals" { test "cast bool to int" { const t = true; const f = false; - expect(@boolToInt(t) == u32(1)); - expect(@boolToInt(f) == u32(0)); + expect(@boolToInt(t) == @as(u32, 1)); + expect(@boolToInt(f) == @as(u32, 0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - expect(@boolToInt(t) == u32(1)); - expect(@boolToInt(f) == u32(0)); + expect(@boolToInt(t) == @as(u32, 1)); + expect(@boolToInt(f) == @as(u32, 0)); } test "bool cmp" { diff --git a/test/stage1/behavior/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig index 550ee9703..fa3caf0df 100644 --- a/test/stage1/behavior/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -8,8 +8,16 @@ test "comptime code should not modify constant data" { } fn testCastPtrOfArrayToSliceAndPtr() void { - var array = "aoeu"; - const x: [*]u8 = &array; - x[0] += 1; - expect(mem.eql(u8, array[0..], "boeu")); + { + var array = "aoeu".*; + const x: [*]u8 = &array; + x[0] += 1; + expect(mem.eql(u8, array[0..], "boeu")); + } + { + var array: [4]u8 = "aoeu".*; + const x: [*]u8 = &array; + x[0] += 1; + expect(mem.eql(u8, array[0..], "boeu")); + } } diff --git a/test/stage1/behavior/bugs/1310.zig b/test/stage1/behavior/bugs/1310.zig index f0f696e4b..788cba575 100644 --- a/test/stage1/behavior/bugs/1310.zig +++ b/test/stage1/behavior/bugs/1310.zig @@ -3,7 +3,7 @@ const expect = std.testing.expect; pub const VM = ?[*]const struct_InvocationTable_; pub const struct_InvocationTable_ = extern struct { - GetVM: ?extern fn (?[*]VM) c_int, + GetVM: ?fn (?[*]VM) callconv(.C) c_int, }; pub const struct_VM_ = extern struct { @@ -15,7 +15,7 @@ pub const struct_VM_ = extern struct { pub const InvocationTable_ = struct_InvocationTable_; pub const VM_ = struct_VM_; -extern fn agent_callback(_vm: [*]VM, options: [*]u8) i32 { +fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 { return 11; } diff --git a/test/stage1/behavior/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig index f1d61baa3..3231a985e 100644 --- a/test/stage1/behavior/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -13,7 +13,7 @@ const C = struct {}; test "tagged union with all void fields but a meaningful tag" { var a: A = A{ .b = B{ .c = C{} } }; - std.testing.expect(@TagType(B)(a.b) == @TagType(B).c); + std.testing.expect(@as(@TagType(B), a.b) == @TagType(B).c); a = A{ .b = B.None }; - std.testing.expect(@TagType(B)(a.b) == @TagType(B).None); + std.testing.expect(@as(@TagType(B), a.b) == @TagType(B).None); } diff --git a/test/stage1/behavior/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig index 48cf1ae2a..da0ba4168 100644 --- a/test/stage1/behavior/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -10,5 +10,5 @@ const S = struct { test "functions with return type required to be comptime are generic" { const ti = S.method(); - expect(builtin.TypeId(ti) == builtin.TypeId.Struct); + expect(@as(builtin.TypeId, ti) == builtin.TypeId.Struct); } diff --git a/test/stage1/behavior/bugs/1607.zig b/test/stage1/behavior/bugs/1607.zig index 3a1de80a8..ffc1aa85d 100644 --- a/test/stage1/behavior/bugs/1607.zig +++ b/test/stage1/behavior/bugs/1607.zig @@ -10,6 +10,6 @@ fn checkAddress(s: []const u8) void { } test "slices pointing at the same address as global array." { - checkAddress(a); - comptime checkAddress(a); + checkAddress(&a); + comptime checkAddress(&a); } diff --git a/test/stage1/behavior/bugs/1851.zig b/test/stage1/behavior/bugs/1851.zig index ff9ab419f..679f4bf83 100644 --- a/test/stage1/behavior/bugs/1851.zig +++ b/test/stage1/behavior/bugs/1851.zig @@ -15,7 +15,7 @@ test "allocation and looping over 3-byte integer" { x[1] = 0xFFFFFF; const bytes = @sliceToBytes(x); - expect(@typeOf(bytes) == []align(4) u8); + expect(@TypeOf(bytes) == []align(4) u8); expect(bytes.len == 8); for (bytes) |*b| { diff --git a/test/stage1/behavior/bugs/1914.zig b/test/stage1/behavior/bugs/1914.zig index 22269590d..2c9e836e6 100644 --- a/test/stage1/behavior/bugs/1914.zig +++ b/test/stage1/behavior/bugs/1914.zig @@ -7,7 +7,7 @@ const B = struct { a_pointer: *const A, }; -const b_list: []B = [_]B{}; +const b_list: []B = &[_]B{}; const a = A{ .b_list_pointer = &b_list }; test "segfault bug" { @@ -24,7 +24,7 @@ pub const B2 = struct { pointer_array: []*A2, }; -var b_value = B2{ .pointer_array = [_]*A2{} }; +var b_value = B2{ .pointer_array = &[_]*A2{} }; test "basic stuff" { std.debug.assert(&b_value == &b_value); diff --git a/test/stage1/behavior/bugs/2114.zig b/test/stage1/behavior/bugs/2114.zig index e26627956..ab32a22cf 100644 --- a/test/stage1/behavior/bugs/2114.zig +++ b/test/stage1/behavior/bugs/2114.zig @@ -3,7 +3,7 @@ const expect = std.testing.expect; const math = std.math; fn ctz(x: var) usize { - return @ctz(@typeOf(x), x); + return @ctz(@TypeOf(x), x); } test "fixed" { @@ -12,8 +12,8 @@ test "fixed" { } fn testClz() void { - expect(ctz(u128(0x40000000000000000000000000000000)) == 126); - expect(math.rotl(u128, u128(0x40000000000000000000000000000000), u8(1)) == u128(0x80000000000000000000000000000000)); - expect(ctz(u128(0x80000000000000000000000000000000)) == 127); - expect(ctz(math.rotl(u128, u128(0x40000000000000000000000000000000), u8(1))) == 127); + expect(ctz(@as(u128, 0x40000000000000000000000000000000)) == 126); + expect(math.rotl(u128, @as(u128, 0x40000000000000000000000000000000), @as(u8, 1)) == @as(u128, 0x80000000000000000000000000000000)); + expect(ctz(@as(u128, 0x80000000000000000000000000000000)) == 127); + expect(ctz(math.rotl(u128, @as(u128, 0x40000000000000000000000000000000), @as(u8, 1))) == 127); } diff --git a/test/stage1/behavior/bugs/2889.zig b/test/stage1/behavior/bugs/2889.zig new file mode 100644 index 000000000..4991b5651 --- /dev/null +++ b/test/stage1/behavior/bugs/2889.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +const source = "A-"; + +fn parseNote() ?i32 { + const letter = source[0]; + const modifier = source[1]; + + const semitone = blk: { + if (letter == 'C' and modifier == '-') break :blk @as(i32, 0); + if (letter == 'C' and modifier == '#') break :blk @as(i32, 1); + if (letter == 'D' and modifier == '-') break :blk @as(i32, 2); + if (letter == 'D' and modifier == '#') break :blk @as(i32, 3); + if (letter == 'E' and modifier == '-') break :blk @as(i32, 4); + if (letter == 'F' and modifier == '-') break :blk @as(i32, 5); + if (letter == 'F' and modifier == '#') break :blk @as(i32, 6); + if (letter == 'G' and modifier == '-') break :blk @as(i32, 7); + if (letter == 'G' and modifier == '#') break :blk @as(i32, 8); + if (letter == 'A' and modifier == '-') break :blk @as(i32, 9); + if (letter == 'A' and modifier == '#') break :blk @as(i32, 10); + if (letter == 'B' and modifier == '-') break :blk @as(i32, 11); + return null; + }; + + return semitone; +} + +test "fixed" { + const result = parseNote(); + std.testing.expect(result.? == 9); +} diff --git a/test/stage1/behavior/bugs/3007.zig b/test/stage1/behavior/bugs/3007.zig new file mode 100644 index 000000000..b723ebc09 --- /dev/null +++ b/test/stage1/behavior/bugs/3007.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +const Foo = struct { + free: bool, + + pub const FooError = error{NotFree}; +}; + +var foo = Foo{ .free = true }; +var default_foo: ?*Foo = null; + +fn get_foo() Foo.FooError!*Foo { + if (foo.free) { + foo.free = false; + return &foo; + } + return error.NotFree; +} + +test "fixed" { + default_foo = get_foo() catch null; // This Line + std.testing.expect(!default_foo.?.free); +} diff --git a/test/stage1/behavior/bugs/3046.zig b/test/stage1/behavior/bugs/3046.zig index 709198fd5..b62474f9b 100644 --- a/test/stage1/behavior/bugs/3046.zig +++ b/test/stage1/behavior/bugs/3046.zig @@ -13,7 +13,7 @@ var some_struct: SomeStruct = undefined; test "fixed" { some_struct = SomeStruct{ - .field = couldFail() catch |_| i32(0), + .field = couldFail() catch |_| @as(i32, 0), }; expect(some_struct.field == 1); } diff --git a/test/stage1/behavior/bugs/3367.zig b/test/stage1/behavior/bugs/3367.zig new file mode 100644 index 000000000..3df3adbff --- /dev/null +++ b/test/stage1/behavior/bugs/3367.zig @@ -0,0 +1,12 @@ +const Foo = struct { + usingnamespace Mixin; +}; + +const Mixin = struct { + pub fn two(self: Foo) void {} +}; + +test "container member access usingnamespace decls" { + var foo = Foo{}; + foo.two(); +} diff --git a/test/stage1/behavior/bugs/3384.zig b/test/stage1/behavior/bugs/3384.zig new file mode 100644 index 000000000..789b0be51 --- /dev/null +++ b/test/stage1/behavior/bugs/3384.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "resolve array slice using builtin" { + expect(@hasDecl(@This(), "std") == true); + expect(@hasDecl(@This(), "std"[0..0]) == false); + expect(@hasDecl(@This(), "std"[0..1]) == false); + expect(@hasDecl(@This(), "std"[0..2]) == false); + expect(@hasDecl(@This(), "std"[0..3]) == true); + expect(@hasDecl(@This(), "std"[0..]) == true); +} diff --git a/test/stage1/behavior/bugs/3468.zig b/test/stage1/behavior/bugs/3468.zig new file mode 100644 index 000000000..adf3db330 --- /dev/null +++ b/test/stage1/behavior/bugs/3468.zig @@ -0,0 +1,6 @@ +// zig fmt: off +test "pointer deref next to assignment" { + var a:i32=2; + var b=&a; + b.*=3; +} diff --git a/test/stage1/behavior/bugs/3742.zig b/test/stage1/behavior/bugs/3742.zig new file mode 100644 index 000000000..01bcb49f3 --- /dev/null +++ b/test/stage1/behavior/bugs/3742.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + +pub const GET = struct { + key: []const u8, + + pub fn init(key: []const u8) GET { + return .{ .key = key }; + } + + pub const Redis = struct { + pub const Command = struct { + pub fn serialize(self: GET, comptime rootSerializer: type) void { + return rootSerializer.serializeCommand(.{ "GET", self.key }); + } + }; + }; +}; + +pub fn isCommand(comptime T: type) bool { + const tid = @typeId(T); + return (tid == .Struct or tid == .Enum or tid == .Union) and + @hasDecl(T, "Redis") and @hasDecl(T.Redis, "Command"); +} + +pub const ArgSerializer = struct { + pub fn serializeCommand(command: var) void { + const CmdT = @TypeOf(command); + + if (comptime isCommand(CmdT)) { + // COMMENTING THE NEXT LINE REMOVES THE ERROR + return CmdT.Redis.Command.serialize(command, ArgSerializer); + } + } +}; + +test "fixed" { + ArgSerializer.serializeCommand(GET.init("banana")); +} diff --git a/test/stage1/behavior/bugs/655.zig b/test/stage1/behavior/bugs/655.zig index d4491bfc2..3d1bccb18 100644 --- a/test/stage1/behavior/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -3,7 +3,7 @@ const other_file = @import("655_other_file.zig"); test "function with *const parameter with type dereferenced by namespace" { const x: other_file.Integer = 1234; - comptime std.testing.expect(@typeOf(&x) == *const other_file.Integer); + comptime std.testing.expect(@TypeOf(&x) == *const other_file.Integer); foo(&x); } diff --git a/test/stage1/behavior/bugs/718.zig b/test/stage1/behavior/bugs/718.zig index 8dfb511bb..b5a57b894 100644 --- a/test/stage1/behavior/bugs/718.zig +++ b/test/stage1/behavior/bugs/718.zig @@ -9,7 +9,7 @@ const Keys = struct { }; var keys: Keys = undefined; test "zero keys with @memset" { - @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@typeOf(keys))); + @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@TypeOf(keys))); expect(!keys.up); expect(!keys.down); expect(!keys.left); diff --git a/test/stage1/behavior/byteswap.zig b/test/stage1/behavior/byteswap.zig index 449fad67a..c799ba484 100644 --- a/test/stage1/behavior/byteswap.zig +++ b/test/stage1/behavior/byteswap.zig @@ -1,5 +1,6 @@ const std = @import("std"); const expect = std.testing.expect; +const builtin = @import("builtin"); test "@byteSwap integers" { const ByteSwapIntTest = struct { @@ -10,24 +11,24 @@ test "@byteSwap integers" { t(u24, 0x123456, 0x563412); t(u32, 0x12345678, 0x78563412); t(u40, 0x123456789a, 0x9a78563412); - t(i48, 0x123456789abc, @bitCast(i48, u48(0xbc9a78563412))); + t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); t(u56, 0x123456789abcde, 0xdebc9a78563412); t(u64, 0x123456789abcdef1, 0xf1debc9a78563412); t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); - t(u0, u0(0), 0); - t(i8, i8(-50), -50); - t(i16, @bitCast(i16, u16(0x1234)), @bitCast(i16, u16(0x3412))); - t(i24, @bitCast(i24, u24(0x123456)), @bitCast(i24, u24(0x563412))); - t(i32, @bitCast(i32, u32(0x12345678)), @bitCast(i32, u32(0x78563412))); - t(u40, @bitCast(i40, u40(0x123456789a)), u40(0x9a78563412)); - t(i48, @bitCast(i48, u48(0x123456789abc)), @bitCast(i48, u48(0xbc9a78563412))); - t(i56, @bitCast(i56, u56(0x123456789abcde)), @bitCast(i56, u56(0xdebc9a78563412))); - t(i64, @bitCast(i64, u64(0x123456789abcdef1)), @bitCast(i64, u64(0xf1debc9a78563412))); + t(u0, @as(u0, 0), 0); + t(i8, @as(i8, -50), -50); + t(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x3412))); + t(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x563412))); + t(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x78563412))); + t(u40, @bitCast(i40, @as(u40, 0x123456789a)), @as(u40, 0x9a78563412)); + t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); + t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); + t(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); t( i128, - @bitCast(i128, u128(0x123456789abcdef11121314151617181)), - @bitCast(i128, u128(0x8171615141312111f1debc9a78563412)), + @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), + @bitCast(i128, @as(u128, 0x8171615141312111f1debc9a78563412)), ); } fn t(comptime I: type, input: I, expected_output: I) void { @@ -39,8 +40,11 @@ test "@byteSwap integers" { } test "@byteSwap vectors" { - // Disabled because of #3317 - if (@import("builtin").arch == .mipsel) return error.SkipZigTest; + // https://github.com/ziglang/zig/issues/3563 + if (builtin.os == .dragonfly) return error.SkipZigTest; + + // https://github.com/ziglang/zig/issues/3317 + if (builtin.arch == .mipsel) return error.SkipZigTest; const ByteSwapVectorTest = struct { fn run() void { diff --git a/test/stage1/behavior/call.zig b/test/stage1/behavior/call.zig new file mode 100644 index 000000000..40b5be4cd --- /dev/null +++ b/test/stage1/behavior/call.zig @@ -0,0 +1,74 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; + +test "basic invocations" { + const foo = struct { + fn foo() i32 { + return 1234; + } + }.foo; + expect(@call(.{}, foo, .{}) == 1234); + comptime { + // modifiers that allow comptime calls + expect(@call(.{}, foo, .{}) == 1234); + expect(@call(.{ .modifier = .no_async }, foo, .{}) == 1234); + expect(@call(.{ .modifier = .always_tail }, foo, .{}) == 1234); + expect(@call(.{ .modifier = .always_inline }, foo, .{}) == 1234); + } + { + // comptime call without comptime keyword + const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234; + comptime expect(result); + } + { + // call of non comptime-known function + var alias_foo = foo; + expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234); + expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234); + expect(@call(.{ .modifier = .never_inline }, alias_foo, .{}) == 1234); + } +} + +test "tuple parameters" { + const add = struct { + fn add(a: i32, b: i32) i32 { + return a + b; + } + }.add; + var a: i32 = 12; + var b: i32 = 34; + expect(@call(.{}, add, .{ a, 34 }) == 46); + expect(@call(.{}, add, .{ 12, b }) == 46); + expect(@call(.{}, add, .{ a, b }) == 46); + expect(@call(.{}, add, .{ 12, 34 }) == 46); + comptime expect(@call(.{}, add, .{ 12, 34 }) == 46); + { + const separate_args0 = .{ a, b }; + const separate_args1 = .{ a, 34 }; + const separate_args2 = .{ 12, 34 }; + const separate_args3 = .{ 12, b }; + expect(@call(.{ .modifier = .always_inline }, add, separate_args0) == 46); + expect(@call(.{ .modifier = .always_inline }, add, separate_args1) == 46); + expect(@call(.{ .modifier = .always_inline }, add, separate_args2) == 46); + expect(@call(.{ .modifier = .always_inline }, add, separate_args3) == 46); + } +} + +test "comptime call with bound function as parameter" { + const S = struct { + fn ReturnType(func: var) type { + return switch (@typeInfo(@TypeOf(func))) { + .BoundFn => |info| info, + else => unreachable, + }.return_type orelse void; + } + + fn call_me_maybe() ?i32 { + return 123; + } + }; + + var inst: S = undefined; + expectEqual(?i32, S.ReturnType(inst.call_me_maybe)); +} diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 8176f5cf8..ae2530af6 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -4,7 +4,7 @@ const mem = std.mem; const maxInt = std.math.maxInt; test "int to ptr cast" { - const x = usize(13); + const x = @as(usize, 13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); expect(z == 13); @@ -75,8 +75,8 @@ test "peer resolve array and const slice" { comptime testPeerResolveArrayConstSlice(true); } fn testPeerResolveArrayConstSlice(b: bool) void { - const value1 = if (b) "aoeu" else ([]const u8)("zz"); - const value2 = if (b) ([]const u8)("zz") else "aoeu"; + const value1 = if (b) "aoeu" else @as([]const u8, "zz"); + const value2 = if (b) @as([]const u8, "zz") else "aoeu"; expect(mem.eql(u8, value1, "aoeu")); expect(mem.eql(u8, value2, "zz")); } @@ -90,7 +90,7 @@ const A = struct { a: i32, }; fn castToOptionalTypeError(z: i32) void { - const x = i32(1); + const x = @as(i32, 1); const y: anyerror!?i32 = x; expect((try y).? == 1); @@ -134,10 +134,10 @@ test "peer type resolution: ?T and T" { } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { if (c) { - return if (b) null else usize(0); + return if (b) null else @as(usize, 0); } - return usize(3); + return @as(usize, 3); } test "peer type resolution: [0]u8 and []const u8" { @@ -150,7 +150,7 @@ test "peer type resolution: [0]u8 and []const u8" { } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { if (a) { - return [_]u8{}; + return &[_]u8{}; } return slice[0..1]; @@ -175,26 +175,32 @@ fn testCastZeroArrayToErrSliceMut() void { } fn gimmeErrOrSlice() anyerror![]u8 { - return [_]u8{}; + return &[_]u8{}; } test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { - { - var data = "hi"; - const slice = data[0..]; - expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } - comptime { - var data = "hi"; - const slice = data[0..]; - expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } + const S = struct { + fn doTheTest() anyerror!void { + { + var data = "hi".*; + const slice = data[0..]; + expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } + { + var data: [2]u8 = "hi".*; + const slice = data[0..]; + expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { if (a) { - return [_]u8{}; + return &[_]u8{}; } return slice[0..1]; @@ -217,27 +223,35 @@ test "implicit cast from &const [N]T to []const T" { } fn testCastConstArrayRefToConstSlice() void { - const blah = "aoeu"; - const const_array_ref = &blah; - expect(@typeOf(const_array_ref) == *const [4]u8); - const slice: []const u8 = const_array_ref; - expect(mem.eql(u8, slice, "aoeu")); + { + const blah = "aoeu".*; + const const_array_ref = &blah; + expect(@TypeOf(const_array_ref) == *const [4:0]u8); + const slice: []const u8 = const_array_ref; + expect(mem.eql(u8, slice, "aoeu")); + } + { + const blah: [4]u8 = "aoeu".*; + const const_array_ref = &blah; + expect(@TypeOf(const_array_ref) == *const [4]u8); + const slice: []const u8 = const_array_ref; + expect(mem.eql(u8, slice, "aoeu")); + } } test "peer type resolution: error and [N]T" { - // TODO: implicit error!T to error!U where T can implicitly cast to U - //expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + comptime expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); expect(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); comptime expect(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } -//fn testPeerErrorAndArray(x: u8) error![]const u8 { -// return switch (x) { -// 0x00 => "OK", -// else => error.BadValue, -// }; -//} +fn testPeerErrorAndArray(x: u8) anyerror![]const u8 { + return switch (x) { + 0x00 => "OK", + else => error.BadValue, + }; +} fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { return switch (x) { 0x00 => "OK", @@ -256,9 +270,9 @@ test "@floatToInt" { } fn testFloatToInts() void { - const x = i32(1e4); + const x = @as(i32, 1e4); expect(x == 10000); - const y = @floatToInt(i32, f32(1e4)); + const y = @floatToInt(i32, @as(f32, 1e4)); expect(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); @@ -310,46 +324,57 @@ test "single-item pointer of array to slice and to unknown length pointer" { } fn testCastPtrOfArrayToSliceAndPtr() void { - var array = "aoeu"; - const x: [*]u8 = &array; - x[0] += 1; - expect(mem.eql(u8, array[0..], "boeu")); - const y: []u8 = &array; - y[0] += 1; - expect(mem.eql(u8, array[0..], "coeu")); + { + var array = "aoeu".*; + const x: [*]u8 = &array; + x[0] += 1; + expect(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + expect(mem.eql(u8, array[0..], "coeu")); + } + { + var array: [4]u8 = "aoeu".*; + const x: [*]u8 = &array; + x[0] += 1; + expect(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + expect(mem.eql(u8, array[0..], "coeu")); + } } test "cast *[1][*]const u8 to [*]const ?[*]const u8" { - const window_name = [1][*]const u8{c"window name"}; + const window_name = [1][*]const u8{"window name"}; const x: [*]const ?[*]const u8 = &window_name; - expect(mem.eql(u8, std.mem.toSliceConst(u8, x[0].?), "window name")); + expect(mem.eql(u8, std.mem.toSliceConst(u8, @ptrCast([*:0]const u8, x[0].?)), "window name")); } test "@intCast comptime_int" { const result = @intCast(i32, 1234); - expect(@typeOf(result) == i32); + expect(@TypeOf(result) == i32); expect(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - expect(@typeOf(result) == f16); + expect(@TypeOf(result) == f16); expect(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - expect(@typeOf(result) == f16); + expect(@TypeOf(result) == f16); expect(result == 1234.0); } { const result = @floatCast(f32, 1234); - expect(@typeOf(result) == f32); + expect(@TypeOf(result) == f32); expect(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - expect(@typeOf(result) == f32); + expect(@TypeOf(result) == f32); expect(result == 1234.0); } } @@ -357,12 +382,12 @@ test "@floatCast comptime_int and comptime_float" { test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - expect(@typeOf(result) == f16); + expect(@TypeOf(result) == f16); expect(result == 1234.0); } { const result = @intToFloat(f32, 1234); - expect(@typeOf(result) == f32); + expect(@TypeOf(result) == f32); expect(result == 1234.0); } } @@ -370,7 +395,7 @@ test "comptime_int @intToFloat" { test "@bytesToSlice keeps pointer alignment" { var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); - comptime expect(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + comptime expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); } test "@intCast i32 to u7" { @@ -392,7 +417,7 @@ fn MakeType(comptime T: type) type { } fn getNonNull() ?T { - return T(undefined); + return @as(T, undefined); } }; } @@ -431,7 +456,7 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = [_]u8{ 3, 2, 1 }; incrementVoidPtrArray(a[0..].ptr, 3); - expect(std.mem.eql(u8, a, [_]u8{ 4, 3, 2 })); + expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 })); } fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { @@ -442,7 +467,7 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { } test "*usize to *void" { - var i = usize(0); + var i = @as(usize, 0); var v = @ptrCast(*void, &i); v.* = {}; } @@ -452,7 +477,7 @@ test "compile time int to ptr of function" { } pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); -pub const PFN_void = extern fn (*c_void) void; +pub const PFN_void = fn (*c_void) callconv(.C) void; fn foobar(func: PFN_void) void { std.testing.expect(@ptrToInt(func) == maxInt(usize)); @@ -535,6 +560,212 @@ test "peer type resolution: unreachable, error set, unreachable" { } test "implicit cast comptime_int to comptime_float" { - comptime expect(comptime_float(10) == f32(10)); + comptime expect(@as(comptime_float, 10) == @as(f32, 10)); expect(2 == 2.0); } + +test "implicit cast *[0]T to E![]const u8" { + var x = @as(anyerror![]const u8, &[0]u8{}); + expect((x catch unreachable).len == 0); +} + +test "peer cast *[0]T to E![]const T" { + var buffer: [5]u8 = "abcde".*; + var buf: anyerror![]const u8 = buffer[0..]; + var b = false; + var y = if (b) &[0]u8{} else buf; + expect(mem.eql(u8, "abcde", y catch unreachable)); +} + +test "peer cast *[0]T to []const T" { + var buffer: [5]u8 = "abcde".*; + var buf: []const u8 = buffer[0..]; + var b = false; + var y = if (b) &[0]u8{} else buf; + expect(mem.eql(u8, "abcde", y)); +} + +var global_array: [4]u8 = undefined; +test "cast from array reference to fn" { + const f = @ptrCast(fn () callconv(.C) void, &global_array); + expect(@ptrToInt(f) == @ptrToInt(&global_array)); +} + +test "*const [N]null u8 to ?[]const u8" { + const S = struct { + fn doTheTest() void { + var a = "Hello"; + var b: ?[]const u8 = a; + expect(mem.eql(u8, b.?, "Hello")); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "peer resolution of string literals" { + const S = struct { + const E = extern enum { + a, + b, + c, + d, + }; + + fn doTheTest(e: E) void { + const cmd = switch (e) { + .a => "one", + .b => "two", + .c => "three", + .d => "four", + else => unreachable, + }; + expect(mem.eql(u8, cmd, "two")); + } + }; + S.doTheTest(.b); + comptime S.doTheTest(.b); +} + +test "type coercion related to sentinel-termination" { + const S = struct { + fn doTheTest() void { + // [:x]T to []T + { + var array = [4:0]i32{ 1, 2, 3, 4 }; + var slice: [:0]i32 = &array; + var dest: []i32 = slice; + expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 })); + } + + // [*:x]T to [*]T + { + var array = [4:99]i32{ 1, 2, 3, 4 }; + var dest: [*]i32 = &array; + expect(dest[0] == 1); + expect(dest[1] == 2); + expect(dest[2] == 3); + expect(dest[3] == 4); + expect(dest[4] == 99); + } + + // [N:x]T to [N]T + { + var array = [4:0]i32{ 1, 2, 3, 4 }; + var dest: [4]i32 = array; + expect(mem.eql(i32, &dest, &[_]i32{ 1, 2, 3, 4 })); + } + + // *[N:x]T to *[N]T + { + var array = [4:0]i32{ 1, 2, 3, 4 }; + var dest: *[4]i32 = &array; + expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 })); + } + + // [:x]T to [*:x]T + { + var array = [4:0]i32{ 1, 2, 3, 4 }; + var slice: [:0]i32 = &array; + var dest: [*:0]i32 = slice; + expect(dest[0] == 1); + expect(dest[1] == 2); + expect(dest[2] == 3); + expect(dest[3] == 4); + expect(dest[4] == 0); + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "cast i8 fn call peers to i32 result" { + const S = struct { + fn doTheTest() void { + var cond = true; + const value: i32 = if (cond) smallBoi() else bigBoi(); + expect(value == 123); + } + fn smallBoi() i8 { + return 123; + } + fn bigBoi() i16 { + return 1234; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "return u8 coercing into ?u32 return type" { + const S = struct { + fn doTheTest() void { + expect(foo(123).? == 123); + } + fn foo(arg: u8) ?u32 { + return arg; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "peer result null and comptime_int" { + const S = struct { + fn blah(n: i32) ?i32 { + if (n == 0) { + return null; + } else if (n < 0) { + return -1; + } else { + return 1; + } + } + }; + + expect(S.blah(0) == null); + comptime expect(S.blah(0) == null); + expect(S.blah(10).? == 1); + comptime expect(S.blah(10).? == 1); + expect(S.blah(-10).? == -1); + comptime expect(S.blah(-10).? == -1); +} + +test "peer type resolution implicit cast to return type" { + const S = struct { + fn doTheTest() void { + for ("hello") |c| _ = f(c); + } + fn f(c: u8) []const u8 { + return switch (c) { + 'h', 'e' => &[_]u8{c}, // should cast to slice + 'l', ' ' => &[_]u8{ c, '.' }, // should cast to slice + else => ([_]u8{c})[0..], // is a slice + }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "peer type resolution implicit cast to variable type" { + const S = struct { + fn doTheTest() void { + var x: []const u8 = undefined; + for ("hello") |c| x = switch (c) { + 'h', 'e' => &[_]u8{c}, // should cast to slice + 'l', ' ' => &[_]u8{ c, '.' }, // should cast to slice + else => ([_]u8{c})[0..], // is a slice + }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "variable initialization uses result locations properly with regards to the type" { + var b = true; + const x: i32 = if (b) 1 else 2; + expect(x == 1); +} diff --git a/test/stage1/behavior/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig index 13713e1ee..672066718 100644 --- a/test/stage1/behavior/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -6,12 +6,11 @@ var argv: [*]const [*]const u8 = undefined; test "const slice child" { const strs = [_][*]const u8{ - c"one", - c"two", - c"three", + "one", + "two", + "three", }; - // TODO this should implicitly cast - argv = @ptrCast([*]const [*]const u8, &strs); + argv = &strs; bar(strs.len); } diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig index 6c0e2a432..5a643609f 100644 --- a/test/stage1/behavior/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -52,7 +52,7 @@ fn testBreakContInDefer(x: usize) void { } test "defer and labeled break" { - var i = usize(0); + var i = @as(usize, 0); blk: { defer i += 1; diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 6084dad3c..d5bb2ddbb 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,6 +1,33 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; +test "extern enum" { + const S = struct { + const i = extern enum { + n = 0, + o = 2, + p = 4, + q = 4, + }; + fn doTheTest(y: c_int) void { + var x = i.o; + expect(@enumToInt(x) == 2); + x = @intToEnum(i, 12); + expect(@enumToInt(x) == 12); + x = @intToEnum(i, y); + expect(@enumToInt(x) == 52); + switch (x) { + .n, + .o, + .p => unreachable, + else => {}, + } + } + }; + S.doTheTest(52); + comptime S.doTheTest(52); +} + test "enum type" { const foo1 = Foo{ .One = 13 }; const foo2 = Foo{ @@ -788,7 +815,7 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { expect(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, - MultipleChoice.C => u32(1234), + MultipleChoice.C => @as(u32, 1234), MultipleChoice.D => 4, }); } @@ -816,7 +843,7 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, - MultipleChoice2.D => u32(1234), + MultipleChoice2.D => @as(u32, 1234), MultipleChoice2.Unspecified1 => 5, MultipleChoice2.Unspecified2 => 6, MultipleChoice2.Unspecified3 => 7, diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig index 983f790cb..d4f64b7cf 100644 --- a/test/stage1/behavior/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,6 +1,7 @@ const std = @import("std"); const expect = std.testing.expect; const expectError = std.testing.expectError; +const expectEqual = std.testing.expectEqual; const mem = std.mem; const builtin = @import("builtin"); @@ -51,7 +52,7 @@ test "error binary operator" { expect(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { - return if (x) error.ItBroke else isize(10); + return if (x) error.ItBroke else @as(isize, 10); } test "unwrap simple value from error" { @@ -83,9 +84,9 @@ test "error union type " { fn testErrorUnionType() void { const x: anyerror!i32 = 1234; if (x) |value| expect(value == 1234) else |_| unreachable; - expect(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - expect(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - expect(@typeOf(x).ErrorSet == anyerror); + expect(@typeId(@TypeOf(x)) == builtin.TypeId.ErrorUnion); + expect(@typeId(@TypeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + expect(@TypeOf(x).ErrorSet == anyerror); } test "error set type" { @@ -160,6 +161,10 @@ fn testErrToIntWithOnePossibleValue( } } +test "empty error union" { + const x = error{} || error{}; +} + test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); } @@ -295,7 +300,7 @@ test "nested error union function call in optional unwrap" { test "widen cast integer payload of error union function call" { const S = struct { fn errorable() !u64 { - var x = u64(try number()); + var x = @as(u64, try number()); return x; } @@ -396,3 +401,44 @@ test "function pointer with return type that is error union with payload which i }; S.doTheTest(); } + +test "return result loc as peer result loc in inferred error set function" { + const S = struct { + fn doTheTest() void { + if (foo(2)) |x| { + expect(x.Two); + } else |e| switch (e) { + error.Whatever => @panic("fail"), + } + expectError(error.Whatever, foo(99)); + } + const FormValue = union(enum) { + One: void, + Two: bool, + }; + + fn foo(id: u64) !FormValue { + return switch (id) { + 2 => FormValue{ .Two = true }, + 1 => FormValue{ .One = {} }, + else => return error.Whatever, + }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "error payload type is correctly resolved" { + const MyIntWrapper = struct { + const Self = @This(); + + x: i32, + + pub fn create() anyerror!Self { + return Self{ .x = 42 }; + } + }; + + expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create()); +} diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 58d662d76..e33def59c 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -105,7 +105,7 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - expect(@sizeOf(@typeOf(array)) == 20); + expect(@sizeOf(@TypeOf(array)) == 20); } const array_size: u8 = 20; @@ -405,19 +405,19 @@ test "float literal at compile time not lossy" { } test "f32 at compile time is lossy" { - expect(f32(1 << 24) + 1 == 1 << 24); + expect(@as(f32, 1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - expect(f64(1 << 53) + 1 == 1 << 53); + expect(@as(f64, 1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - expect(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } comptime { - expect(f128(1 << 113) == 10384593717069655257060992658440192); + expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); } pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { @@ -434,9 +434,9 @@ test "string literal used as comptime slice is memoized" { } test "comptime slice of undefined pointer of length 0" { - const slice1 = ([*]i32)(undefined)[0..0]; + const slice1 = @as([*]i32, undefined)[0..0]; expect(slice1.len == 0); - const slice2 = ([*]i32)(undefined)[100..100]; + const slice2 = @as([*]i32, undefined)[100..100]; expect(slice2.len == 0); } @@ -444,10 +444,10 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { comptime var i: usize = 0; inline while (i < 4) : (i += 1) { s[i] = 0; - s[i] |= u32(b[i * 4 + 0]) << 24; - s[i] |= u32(b[i * 4 + 1]) << 16; - s[i] |= u32(b[i * 4 + 2]) << 8; - s[i] |= u32(b[i * 4 + 3]) << 0; + s[i] |= @as(u32, b[i * 4 + 0]) << 24; + s[i] |= @as(u32, b[i * 4 + 1]) << 16; + s[i] |= @as(u32, b[i * 4 + 2]) << 8; + s[i] |= @as(u32, b[i * 4 + 3]) << 0; } } @@ -557,14 +557,14 @@ test "array concat of slices gives slice" { test "comptime shlWithOverflow" { const ct_shifted: u64 = comptime amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + var amt = @as(u64, 0); + _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt); break :amt amt; }; const rt_shifted: u64 = amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + var amt = @as(u64, 0); + _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt); break :amt amt; }; @@ -598,7 +598,7 @@ test "pointer to type" { var T: type = i32; expect(T == i32); var ptr = &T; - expect(@typeOf(ptr) == *type); + expect(@TypeOf(ptr) == *type); ptr.* = f32; expect(T == f32); expect(*T == *f32); @@ -670,10 +670,10 @@ fn loopNTimes(comptime n: usize) void { } test "variable inside inline loop that has different types on different iterations" { - testVarInsideInlineLoop(true, u32(42)); + testVarInsideInlineLoop(.{true, @as(u32, 42)}); } -fn testVarInsideInlineLoop(args: ...) void { +fn testVarInsideInlineLoop(args: var) void { comptime var i = 0; inline while (i < args.len) : (i += 1) { const x = args[i]; @@ -717,7 +717,7 @@ test "@bytesToslice on a packed struct" { }; var b = [1]u8{9}; - var f = @bytesToSlice(F, b); + var f = @bytesToSlice(F, &b); expect(f[0].a == 9); } @@ -736,7 +736,7 @@ test "comptime pointer cast array and then slice" { test "slice bounds in comptime concatenation" { const bs = comptime blk: { - const b = c"........1........"; + const b = "........1........"; break :blk b[8..9]; }; const str = "" ++ bs; @@ -757,11 +757,11 @@ test "comptime bitwise operators" { expect(-3 | -1 == -1); expect(3 ^ -1 == -4); expect(-3 ^ -1 == 2); - expect(~i8(-1) == 0); - expect(~i128(-1) == 0); + expect(~@as(i8, -1) == 0); + expect(~@as(i128, -1) == 0); expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - expect(~u128(0) == 0xffffffffffffffffffffffffffffffff); + expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); } } @@ -774,12 +774,12 @@ test "*align(1) u16 is the same as *align(1:0:2) u16" { test "array concatenation forces comptime" { var a = oneItem(3) ++ oneItem(4); - expect(std.mem.eql(i32, a, [_]i32{ 3, 4 })); + expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); } test "array multiplication forces comptime" { var a = oneItem(3) ** scalar(2); - expect(std.mem.eql(i32, a, [_]i32{ 3, 3 })); + expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 })); } fn oneItem(x: i32) [1]i32 { diff --git a/test/stage1/behavior/floatop.zig b/test/stage1/behavior/floatop.zig index de2f6815a..5fe116250 100644 --- a/test/stage1/behavior/floatop.zig +++ b/test/stage1/behavior/floatop.zig @@ -1,6 +1,10 @@ -const expect = @import("std").testing.expect; -const pi = @import("std").math.pi; -const e = @import("std").math.e; +const std = @import("std"); +const expect = std.testing.expect; +const math = std.math; +const pi = std.math.pi; +const e = std.math.e; + +const epsilon = 0.000001; test "@sqrt" { comptime testSqrt(); @@ -10,25 +14,55 @@ test "@sqrt" { fn testSqrt() void { { var a: f16 = 4; - expect(@sqrt(f16, a) == 2); + expect(@sqrt(a) == 2); } { var a: f32 = 9; - expect(@sqrt(f32, a) == 3); + expect(@sqrt(a) == 3); + var b: f32 = 1.1; + expect(math.approxEq(f32, @sqrt(b), 1.0488088481701516, epsilon)); } { var a: f64 = 25; - expect(@sqrt(f64, a) == 5); + expect(@sqrt(a) == 5); } { const a: comptime_float = 25.0; - expect(@sqrt(comptime_float, a) == 5.0); + expect(@sqrt(a) == 5.0); } - // Waiting on a c.zig implementation + // TODO https://github.com/ziglang/zig/issues/4026 //{ // var a: f128 = 49; - // expect(@sqrt(f128, a) == 7); + // expect(@sqrt(a) == 7); //} + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4}; + var result = @sqrt(v); + expect(math.approxEq(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon)); + expect(math.approxEq(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon)); + } +} + +test "more @sqrt f16 tests" { + // TODO these are not all passing at comptime + expect(@sqrt(@as(f16, 0.0)) == 0.0); + expect(math.approxEq(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); + expect(math.approxEq(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); + expect(@sqrt(@as(f16, 4.0)) == 2.0); + expect(math.approxEq(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); + expect(math.approxEq(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); + expect(@sqrt(@as(f16, 64.0)) == 8.0); + expect(math.approxEq(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); + expect(math.approxEq(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); + + // special cases + expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); + expect(@sqrt(@as(f16, 0.0)) == 0.0); + expect(@sqrt(@as(f16, -0.0)) == -0.0); + expect(math.isNan(@sqrt(@as(f16, -1.0)))); + expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); } test "@sin" { @@ -37,26 +71,28 @@ test "@sin" { } fn testSin() void { - // TODO - this is actually useful and should be implemented - // (all the trig functions for f16) - // but will probably wait till self-hosted - //{ - // var a: f16 = pi; - // expect(@sin(f16, a/2) == 1); - //} + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 0; + expect(@sin(a) == 0); + } { var a: f32 = 0; - expect(@sin(f32, a) == 0); + expect(@sin(a) == 0); } { var a: f64 = 0; - expect(@sin(f64, a) == 0); + expect(@sin(a) == 0); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4}; + var result = @sin(v); + expect(math.approxEq(f32, @sin(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @sin(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @sin(@as(f32, 3.3)), result[2], epsilon)); + expect(math.approxEq(f32, @sin(@as(f32, 4.4)), result[3], epsilon)); } - // TODO - //{ - // var a: f16 = pi; - // expect(@sqrt(f128, a/2) == 1); - //} } test "@cos" { @@ -65,13 +101,27 @@ test "@cos" { } fn testCos() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 0; + expect(@cos(a) == 1); + } { var a: f32 = 0; - expect(@cos(f32, a) == 1); + expect(@cos(a) == 1); } { var a: f64 = 0; - expect(@cos(f64, a) == 1); + expect(@cos(a) == 1); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4}; + var result = @cos(v); + expect(math.approxEq(f32, @cos(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @cos(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @cos(@as(f32, 3.3)), result[2], epsilon)); + expect(math.approxEq(f32, @cos(@as(f32, 4.4)), result[3], epsilon)); } } @@ -81,13 +131,27 @@ test "@exp" { } fn testExp() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 0; + expect(@exp(a) == 1); + } { var a: f32 = 0; - expect(@exp(f32, a) == 1); + expect(@exp(a) == 1); } { var a: f64 = 0; - expect(@exp(f64, a) == 1); + expect(@exp(a) == 1); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4}; + var result = @exp(v); + expect(math.approxEq(f32, @exp(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @exp(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @exp(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @exp(@as(f32, 0.4)), result[3], epsilon)); } } @@ -97,31 +161,59 @@ test "@exp2" { } fn testExp2() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 2; + expect(@exp2(a) == 4); + } { var a: f32 = 2; - expect(@exp2(f32, a) == 4); + expect(@exp2(a) == 4); } { var a: f64 = 2; - expect(@exp2(f64, a) == 4); + expect(@exp2(a) == 4); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4}; + var result = @exp2(v); + expect(math.approxEq(f32, @exp2(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @exp2(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @exp2(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @exp2(@as(f32, 0.4)), result[3], epsilon)); } } -test "@ln" { +test "@log" { // Old musl (and glibc?), and our current math.ln implementation do not return 1 // so also accept those values. - comptime testLn(); - testLn(); + comptime testLog(); + testLog(); } -fn testLn() void { +fn testLog() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = e; + expect(math.approxEq(f16, @log(a), 1, epsilon)); + } { var a: f32 = e; - expect(@ln(f32, a) == 1 or @ln(f32, a) == @bitCast(f32, u32(0x3f7fffff))); + expect(@log(a) == 1 or @log(a) == @bitCast(f32, @as(u32, 0x3f7fffff))); } { var a: f64 = e; - expect(@ln(f64, a) == 1 or @ln(f64, a) == @bitCast(f64, u64(0x3ff0000000000000))); + expect(@log(a) == 1 or @log(a) == @bitCast(f64, @as(u64, 0x3ff0000000000000))); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4}; + var result = @log(v); + expect(math.approxEq(f32, @log(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @log(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @log(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @log(@as(f32, 0.4)), result[3], epsilon)); } } @@ -131,13 +223,27 @@ test "@log2" { } fn testLog2() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 4; + expect(@log2(a) == 2); + } { var a: f32 = 4; - expect(@log2(f32, a) == 2); + expect(@log2(a) == 2); } { var a: f64 = 4; - expect(@log2(f64, a) == 2); + expect(@log2(a) == 2); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4}; + var result = @log2(v); + expect(math.approxEq(f32, @log2(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @log2(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @log2(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @log2(@as(f32, 0.4)), result[3], epsilon)); } } @@ -147,13 +253,27 @@ test "@log10" { } fn testLog10() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 100; + expect(@log10(a) == 2); + } { var a: f32 = 100; - expect(@log10(f32, a) == 2); + expect(@log10(a) == 2); } { var a: f64 = 1000; - expect(@log10(f64, a) == 3); + expect(@log10(a) == 3); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4}; + var result = @log10(v); + expect(math.approxEq(f32, @log10(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @log10(@as(f32, 2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @log10(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @log10(@as(f32, 0.4)), result[3], epsilon)); } } @@ -163,17 +283,33 @@ test "@fabs" { } fn testFabs() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = -2.5; + var b: f16 = 2.5; + expect(@fabs(a) == 2.5); + expect(@fabs(b) == 2.5); + } { var a: f32 = -2.5; var b: f32 = 2.5; - expect(@fabs(f32, a) == 2.5); - expect(@fabs(f32, b) == 2.5); + expect(@fabs(a) == 2.5); + expect(@fabs(b) == 2.5); } { var a: f64 = -2.5; var b: f64 = 2.5; - expect(@fabs(f64, a) == 2.5); - expect(@fabs(f64, b) == 2.5); + expect(@fabs(a) == 2.5); + expect(@fabs(b) == 2.5); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4}; + var result = @fabs(v); + expect(math.approxEq(f32, @fabs(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @fabs(@as(f32, -2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @fabs(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @fabs(@as(f32, -0.4)), result[3], epsilon)); } } @@ -183,13 +319,27 @@ test "@floor" { } fn testFloor() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 2.1; + expect(@floor(a) == 2); + } { var a: f32 = 2.1; - expect(@floor(f32, a) == 2); + expect(@floor(a) == 2); } { var a: f64 = 3.5; - expect(@floor(f64, a) == 3); + expect(@floor(a) == 3); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4}; + var result = @floor(v); + expect(math.approxEq(f32, @floor(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @floor(@as(f32, -2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @floor(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @floor(@as(f32, -0.4)), result[3], epsilon)); } } @@ -199,13 +349,27 @@ test "@ceil" { } fn testCeil() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 2.1; + expect(@ceil(a) == 3); + } { var a: f32 = 2.1; - expect(@ceil(f32, a) == 3); + expect(@ceil(a) == 3); } { var a: f64 = 3.5; - expect(@ceil(f64, a) == 4); + expect(@ceil(a) == 4); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4}; + var result = @ceil(v); + expect(math.approxEq(f32, @ceil(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @ceil(@as(f32, -2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @ceil(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @ceil(@as(f32, -0.4)), result[3], epsilon)); } } @@ -215,29 +379,45 @@ test "@trunc" { } fn testTrunc() void { + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + { + var a: f16 = 2.1; + expect(@trunc(a) == 2); + } { var a: f32 = 2.1; - expect(@trunc(f32, a) == 2); + expect(@trunc(a) == 2); } { var a: f64 = -3.5; - expect(@trunc(f64, a) == -3); + expect(@trunc(a) == -3); + } + { + var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4}; + var result = @trunc(v); + expect(math.approxEq(f32, @trunc(@as(f32, 1.1)), result[0], epsilon)); + expect(math.approxEq(f32, @trunc(@as(f32, -2.2)), result[1], epsilon)); + expect(math.approxEq(f32, @trunc(@as(f32, 0.3)), result[2], epsilon)); + expect(math.approxEq(f32, @trunc(@as(f32, -0.4)), result[3], epsilon)); } } -// This is waiting on library support for the Windows build (not sure why the other's don't need it) -//test "@nearbyInt" { +// TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) +//test "@nearbyint" { // comptime testNearbyInt(); // testNearbyInt(); //} //fn testNearbyInt() void { +// // TODO test f16, f128, and c_longdouble +// // https://github.com/ziglang/zig/issues/4026 // { // var a: f32 = 2.1; -// expect(@nearbyInt(f32, a) == 2); +// expect(@nearbyint(a) == 2); // } // { // var a: f64 = -3.75; -// expect(@nearbyInt(f64, a) == -4); +// expect(@nearbyint(a) == -4); // } //} diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig index 6b9c8b8fe..13859b92c 100644 --- a/test/stage1/behavior/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -29,7 +29,7 @@ test "mutable local variables" { var zero: i32 = 0; expect(zero == 0); - var i = i32(0); + var i = @as(i32, 0); while (i != 3) { i += 1; } @@ -43,7 +43,7 @@ test "separate block scopes" { } const c = x: { - const no_conflict = i32(10); + const no_conflict = @as(i32, 10); break :x no_conflict; }; expect(c == 10); @@ -73,7 +73,7 @@ fn fnWithUnreachable() noreturn { } test "function pointers" { - const fns = [_]@typeOf(fn1){ + const fns = [_]@TypeOf(fn1){ fn1, fn2, fn3, @@ -96,14 +96,6 @@ fn fn4() u32 { return 8; } -test "inline function call" { - expect(@inlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - test "number literal as an argument" { numberLiteralArg(3); comptime numberLiteralArg(3); @@ -138,7 +130,7 @@ test "pass by non-copying value through var arg" { } fn addPointCoordsVar(pt: var) i32 { - comptime expect(@typeOf(pt) == Point); + comptime expect(@TypeOf(pt) == Point); return pt.x + pt.y; } @@ -178,7 +170,7 @@ test "pass by non-copying value as method, at comptime" { } fn outer(y: u32) fn (u32) u32 { - const Y = @typeOf(y); + const Y = @TypeOf(y); const st = struct { fn get(z: u32) u32 { return z + @sizeOf(Y); @@ -194,9 +186,9 @@ test "return inner function which references comptime variable of outer function test "extern struct with stdcallcc fn pointer" { const S = extern struct { - ptr: stdcallcc fn () i32, + ptr: fn () callconv(.Stdcall) i32, - stdcallcc fn foo() i32 { + fn foo() callconv(.Stdcall) i32 { return 1234; } }; @@ -247,3 +239,36 @@ test "discard the result of a function that returns a struct" { S.entry(); comptime S.entry(); } + +test "function call with anon list literal" { + const S = struct { + fn doTheTest() void { + consumeVec(.{ 9, 8, 7 }); + } + + fn consumeVec(vec: [3]f32) void { + expect(vec[0] == 9); + expect(vec[1] == 8); + expect(vec[2] == 7); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "ability to give comptime types and non comptime types to same parameter" { + const S = struct { + fn doTheTest() void { + var x: i32 = 1; + expect(foo(x) == 10); + expect(foo(i32) == 20); + } + + fn foo(arg: var) i32 { + if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20; + return 9 + arg; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/for.zig b/test/stage1/behavior/for.zig index cfa68bd21..29b6f934f 100644 --- a/test/stage1/behavior/for.zig +++ b/test/stage1/behavior/for.zig @@ -26,12 +26,12 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - expect(mem.eql(u8, target, "bcdefgh")); + expect(mem.eql(u8, &target, "bcdefgh")); for (source) |*c, i| - expect(@typeOf(c) == *const u8); + expect(@TypeOf(c) == *const u8); for (target) |*c, i| - expect(@typeOf(c) == *u8); + expect(@TypeOf(c) == *u8); } fn mangleString(s: []u8) void { @@ -64,7 +64,7 @@ test "basic for loop" { buffer[buf_index] = @intCast(u8, index); buf_index += 1; } - const unknown_size: []const u8 = array; + const unknown_size: []const u8 = &array; for (unknown_size) |item| { buffer[buf_index] = item; buf_index += 1; @@ -74,7 +74,7 @@ test "basic for loop" { buf_index += 1; } - expect(mem.eql(u8, buffer[0..buf_index], expected_result)); + expect(mem.eql(u8, buffer[0..buf_index], &expected_result)); } test "break from outer for loop" { @@ -139,6 +139,6 @@ test "for with null and T peer types and inferred result location type" { } } }; - S.doTheTest([_]u8{ 1, 2 }); - comptime S.doTheTest([_]u8{ 1, 2 }); + S.doTheTest(&[_]u8{ 1, 2 }); + comptime S.doTheTest(&[_]u8{ 1, 2 }); } diff --git a/test/stage1/behavior/generics.zig b/test/stage1/behavior/generics.zig index 664b982c2..a5d2f9dab 100644 --- a/test/stage1/behavior/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -47,7 +47,7 @@ comptime { expect(max_f64(1.2, 3.4) == 3.4); } -fn max_var(a: var, b: var) @typeOf(a + b) { +fn max_var(a: var, b: var) @TypeOf(a + b) { return if (a > b) a else b; } @@ -120,8 +120,8 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) T { } test "generic fn with implicit cast" { - expect(getFirstByte(u8, [_]u8{13}) == 13); - expect(getFirstByte(u16, [_]u16{ + expect(getFirstByte(u8, &[_]u8{13}) == 13); + expect(getFirstByte(u16, &[_]u16{ 0, 13, }) == 0); diff --git a/test/stage1/behavior/if.zig b/test/stage1/behavior/if.zig index d299817f4..63c31fb03 100644 --- a/test/stage1/behavior/if.zig +++ b/test/stage1/behavior/if.zig @@ -32,7 +32,7 @@ fn elseIfExpressionF(c: u8) u8 { } else if (c == 1) { return 1; } else { - return u8(2); + return @as(u8, 2); } } @@ -58,7 +58,7 @@ test "labeled break inside comptime if inside runtime if" { var c = true; if (c) { answer = if (true) blk: { - break :blk i32(42); + break :blk @as(i32, 42); }; } expect(answer == 42); @@ -81,6 +81,10 @@ test "if prongs cast to expected type instead of peer type resolution" { var x: i32 = 0; x = if (f) 1 else 2; expect(x == 2); + + var b = true; + const y: i32 = if (b) 1 else 2; + expect(y == 1); } }; S.doTheTest(false); diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig index aa6e9d82b..2aa2ec4e6 100644 --- a/test/stage1/behavior/import.zig +++ b/test/stage1/behavior/import.zig @@ -3,7 +3,7 @@ const expectEqual = @import("std").testing.expectEqual; const a_namespace = @import("import/a_namespace.zig"); test "call fn via namespace lookup" { - expectEqual(i32(1234), a_namespace.foo()); + expectEqual(@as(i32, 1234), a_namespace.foo()); } test "importing the same thing gives the same import" { @@ -14,5 +14,5 @@ test "import in non-toplevel scope" { const S = struct { usingnamespace @import("import/a_namespace.zig"); }; - expectEqual(i32(1234), S.foo()); + expectEqual(@as(i32, 1234), S.foo()); } diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 1ff63e3e2..e00b1a83f 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -4,6 +4,7 @@ const expectEqual = std.testing.expectEqual; const expectEqualSlices = std.testing.expectEqualSlices; const maxInt = std.math.maxInt; const minInt = std.math.minInt; +const mem = std.mem; test "division" { if (@import("builtin").arch == .riscv64) { @@ -186,9 +187,9 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(90 >> 1 >> 2 != 90 >> 3); assertFalse(100 - 1 + 1000 != 1099); assertFalse(5 * 4 / 2 % 3 != 1); - assertFalse(i32(i32(5)) != 5); + assertFalse(@as(i32, @as(i32, 5)) != 5); assertFalse(!!false); - assertFalse(i32(7) != --(i32(7))); + assertFalse(@as(i32, 7) != --(@as(i32, 7))); } fn assertFalse(b: bool) void { expect(!b); @@ -256,10 +257,10 @@ const DivResult = struct { test "binary not" { expect(comptime x: { - break :x ~u16(0b1010101010101010) == 0b0101010101010101; + break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; }); expect(comptime x: { - break :x ~u64(2147483647) == 18446744071562067968; + break :x ~@as(u64, 2147483647) == 18446744071562067968; }); testBinaryNot(0b1010101010101010); } @@ -281,8 +282,8 @@ test "small int addition" { x += 1; expect(x == 3); - var result: @typeOf(x) = 3; - expect(@addWithOverflow(@typeOf(x), x, 1, &result)); + var result: @TypeOf(x) = 3; + expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); expect(result == 0); } @@ -472,7 +473,7 @@ test "comptime_int multiplication" { test "comptime_int shifting" { comptime { - expect((u128(1) << 127) == 0x80000000000000000000000000000000); + expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000); } } @@ -480,13 +481,13 @@ test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - expect(u32(a & 0xffffffff) == 0xaaaaaaab); + expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab); a >>= 32; - expect(u32(a & 0xffffffff) == 0xeeeeeeef); + expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef); a >>= 32; - expect(u32(a & 0xffffffff) == 0xa0000001); + expect(@as(u32, a & 0xffffffff) == 0xa0000001); a >>= 32; - expect(u32(a & 0xffffffff) == 0xefffffff); + expect(@as(u32, a & 0xffffffff) == 0xefffffff); a >>= 32; expect(a == 0); @@ -552,7 +553,7 @@ fn should_not_be_zero(x: f128) void { test "comptime float rem int" { comptime { - var x = f32(1) % 2; + var x = @as(f32, 1) % 2; expect(x == 1.0); } } @@ -568,8 +569,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - expect(T(1) == T(1) % T(2)); - expect(T(1) == T(7) % T(3)); + expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); + expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); } test "@sqrt" { @@ -586,12 +587,12 @@ test "@sqrt" { const x = 14.0; const y = x * x; - const z = @sqrt(@typeOf(y), y); + const z = @sqrt(y); comptime expect(z == x); } fn testSqrt(comptime T: type, x: T) void { - expect(@sqrt(T, x * x) == x); + expect(@sqrt(x * x) == x); } test "comptime_int param and return" { @@ -653,3 +654,27 @@ test "128-bit multiplication" { var c = a * b; expect(c == 6); } + +test "vector comparison" { + const S = struct { + fn doTheTest() void { + var a: @Vector(6, i32) = [_]i32{1, 3, -1, 5, 7, 9}; + var b: @Vector(6, i32) = [_]i32{-1, 3, 0, 6, 10, -10}; + expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{false, false, true, true, true, false})); + expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{false, true, true, true, true, false})); + expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{false, true, false, false, false, false})); + expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{true, false, true, true, true, true})); + expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{true, false, false, false, false, true})); + expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{true, true, false, false, false, true})); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "compare undefined literal with comptime_int" { + var x = undefined == 1; + // x is now undefined with type bool + x = true; + expect(x); +} diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index ab16b08be..bd11de5db 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -16,10 +16,10 @@ test "empty function with comments" { } comptime { - @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); + @export(disabledExternFn, .{ .name = "disabledExternFn", .linkage = .Internal }); } -extern fn disabledExternFn() void {} +fn disabledExternFn() callconv(.C) void {} test "call disabled extern fn" { disabledExternFn(); @@ -204,11 +204,11 @@ test "multiline string" { test "multiline C string" { const s1 = - c\\one - c\\two) - c\\three + \\one + \\two) + \\three ; - const s2 = c"one\ntwo)\nthree"; + const s2 = "one\ntwo)\nthree"; expect(std.cstr.cmp(s1, s2) == 0); } @@ -241,14 +241,14 @@ fn memFree(comptime T: type, memory: []T) void {} test "cast undefined" { const array: [100]u8 = undefined; - const slice = ([]const u8)(array); + const slice = @as([]const u8, &array); testCastUndefined(slice); } fn testCastUndefined(x: []const u8) void {} test "cast small unsigned to larger signed" { - expect(castSmallUnsignedToLargerSigned1(200) == i16(200)); - expect(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); + expect(castSmallUnsignedToLargerSigned1(200) == @as(i16, 200)); + expect(castSmallUnsignedToLargerSigned2(9999) == @as(i64, 9999)); } fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; @@ -268,7 +268,7 @@ fn outer() i64 { } test "pointer dereferencing" { - var x = i32(3); + var x = @as(i32, 3); const y = &x; y.* += 1; @@ -350,7 +350,7 @@ fn testTakeAddressOfParameter(f: f32) void { } test "pointer comparison" { - const a = ([]const u8)("a"); + const a = @as([]const u8, "a"); const b = &a; expect(ptrEql(b, b)); } @@ -358,9 +358,12 @@ fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; } -test "C string concatenation" { - const a = c"OK" ++ c" IT " ++ c"WORKED"; - const b = c"OK IT WORKED"; +test "string concatenation" { + const a = "OK" ++ " IT " ++ "WORKED"; + const b = "OK IT WORKED"; + + comptime expect(@TypeOf(a) == *const [12:0]u8); + comptime expect(@TypeOf(b) == *const [12:0]u8); const len = mem.len(u8, b); const len_with_null = len + 1; @@ -457,19 +460,19 @@ test "@typeId" { expect(@typeId(*f32) == Tid.Pointer); expect(@typeId([2]u8) == Tid.Array); expect(@typeId(AStruct) == Tid.Struct); - expect(@typeId(@typeOf(1)) == Tid.ComptimeInt); - expect(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - expect(@typeId(@typeOf(undefined)) == Tid.Undefined); - expect(@typeId(@typeOf(null)) == Tid.Null); + expect(@typeId(@TypeOf(1)) == Tid.ComptimeInt); + expect(@typeId(@TypeOf(1.0)) == Tid.ComptimeFloat); + expect(@typeId(@TypeOf(undefined)) == Tid.Undefined); + expect(@typeId(@TypeOf(null)) == Tid.Null); expect(@typeId(?i32) == Tid.Optional); expect(@typeId(anyerror!i32) == Tid.ErrorUnion); expect(@typeId(anyerror) == Tid.ErrorSet); expect(@typeId(AnEnum) == Tid.Enum); - expect(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + expect(@typeId(@TypeOf(AUnionEnum.One)) == Tid.Enum); expect(@typeId(AUnionEnum) == Tid.Union); expect(@typeId(AUnion) == Tid.Union); expect(@typeId(fn () void) == Tid.Fn); - expect(@typeId(@typeOf(builtin)) == Tid.Type); + expect(@typeId(@TypeOf(builtin)) == Tid.Type); // TODO bound fn // TODO arg tuple // TODO opaque @@ -500,7 +503,7 @@ fn TypeFromFn(comptime T: type) type { } test "double implicit cast in same expression" { - var x = i32(u16(nine())); + var x = @as(i32, @as(u16, nine())); expect(x == 9); } fn nine() u8 { @@ -611,7 +614,7 @@ test "slicing zero length array" { expect(s1.len == 0); expect(s2.len == 0); expect(mem.eql(u8, s1, "")); - expect(mem.eql(u32, s2, [_]u32{})); + expect(mem.eql(u32, s2, &[_]u32{})); } const addr1 = @ptrCast(*const u8, emptyFn); @@ -642,16 +645,16 @@ test "self reference through fn ptr field" { test "volatile load and store" { var number: i32 = 1234; - const ptr = (*volatile i32)(&number); + const ptr = @as(*volatile i32, &number); ptr.* += 1; expect(ptr.* == 1235); } test "slice string literal has type []const u8" { comptime { - expect(@typeOf("aoeu"[0..]) == []const u8); + expect(@TypeOf("aoeu"[0..]) == []const u8); const array = [_]i32{ 1, 2, 3, 4 }; - expect(@typeOf(array[0..]) == []const i32); + expect(@TypeOf(array[0..]) == []const i32); } } @@ -699,11 +702,15 @@ test "unicode escape in character literal" { expect(a == 128169); } +test "unicode character in character literal" { + expect('๐Ÿ’ฉ' == 128169); +} + test "result location zero sized array inside struct field implicit cast to slice" { const E = struct { entries: []u32, }; - var foo = E{ .entries = [_]u32{} }; + var foo = E{ .entries = &[_]u32{} }; expect(foo.entries.len == 0); } @@ -757,7 +764,7 @@ test "nested optional field in struct" { fn maybe(x: bool) anyerror!?u32 { return switch (x) { - true => u32(42), + true => @as(u32, 42), else => null, }; } @@ -766,3 +773,11 @@ test "result location is optional inside error union" { const x = maybe(true) catch unreachable; expect(x.? == 42); } + +threadlocal var buffer: [11]u8 = undefined; + +test "pointer to thread local array" { + const s = "Hello world"; + std.mem.copy(u8, buffer[0..], s); + std.testing.expectEqualSlices(u8, buffer[0..], s); +} diff --git a/test/stage1/behavior/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig index a2f3773af..69763a52c 100644 --- a/test/stage1/behavior/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -12,11 +12,14 @@ test "calling a function with a new stack" { // TODO: https://github.com/ziglang/zig/issues/3338 return error.SkipZigTest; } + if (comptime !std.Target.current.supportsNewStackCall()) { + return error.SkipZigTest; + } const arg = 1234; - const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); - const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + const a = @call(.{ .stack = new_stack_bytes[0..512] }, targetFunction, .{arg}); + const b = @call(.{ .stack = new_stack_bytes[512..] }, targetFunction, .{arg}); _ = targetFunction(arg); expect(arg == 1234); diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index 8cc90d10a..4d9d8e1e8 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -119,3 +119,37 @@ test "self-referential struct through a slice of optional" { var n = S.Node.new(); expect(n.data == null); } + +test "assigning to an unwrapped optional field in an inline loop" { + comptime var maybe_pos_arg: ?comptime_int = null; + inline for ("ab") |x| { + maybe_pos_arg = 0; + if (maybe_pos_arg.? != 0) { + @compileError("bad"); + } + maybe_pos_arg.? = 10; + } +} + +test "coerce an anon struct literal to optional struct" { + const S = struct { + const Struct = struct { + field: u32, + }; + export fn doTheTest() void { + var maybe_dims: ?Struct = null; + maybe_dims = .{ .field = 1 }; + expect(maybe_dims.?.field == 1); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "optional with void type" { + const Foo = struct { + x: ?void, + }; + var x = Foo{ .x = null }; + expect(x.x == null); +} diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index bf292bad0..fdaa5867d 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -14,8 +14,24 @@ fn testDerefPtr() void { expect(x == 1235); } +const Foo1 = struct { + x: void, +}; + +test "dereference pointer again" { + testDerefPtrOneVal(); + comptime testDerefPtrOneVal(); +} + +fn testDerefPtrOneVal() void { + // Foo1 satisfies the OnePossibleValueYes criteria + const x = &Foo1{ .x = {} }; + const y = x.*; + expect(@TypeOf(y.x) == void); +} + test "pointer arithmetic" { - var ptr = c"abcd"; + var ptr: [*]const u8 = "abcd"; expect(ptr[0] == 'a'); ptr += 1; @@ -67,10 +83,10 @@ test "C pointer comparison and arithmetic" { expect(ptr1 == 0); expect(ptr1 >= 0); expect(ptr1 <= 0); - expect(ptr1 < 1); - expect(ptr1 < one); - expect(1 > ptr1); - expect(one > ptr1); + // expect(ptr1 < 1); + // expect(ptr1 < one); + // expect(1 > ptr1); + // expect(one > ptr1); expect(ptr1 < ptr2); expect(ptr2 > ptr1); expect(ptr2 >= 40); @@ -93,10 +109,10 @@ test "peer type resolution with C pointers" { var x2 = if (t) ptr_many else ptr_c; var x3 = if (t) ptr_c else ptr_one; var x4 = if (t) ptr_c else ptr_many; - expect(@typeOf(x1) == [*c]u8); - expect(@typeOf(x2) == [*c]u8); - expect(@typeOf(x3) == [*c]u8); - expect(@typeOf(x4) == [*c]u8); + expect(@TypeOf(x1) == [*c]u8); + expect(@TypeOf(x2) == [*c]u8); + expect(@TypeOf(x3) == [*c]u8); + expect(@TypeOf(x4) == [*c]u8); } test "implicit casting between C pointer and optional non-C pointer" { @@ -144,11 +160,11 @@ test "allowzero pointer and slice" { expect(opt_ptr != null); expect(@ptrToInt(ptr) == 0); var slice = ptr[0..10]; - expect(@typeOf(slice) == []allowzero i32); + expect(@TypeOf(slice) == []allowzero i32); expect(@ptrToInt(&slice[5]) == 20); - expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero); - expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero); + expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); + expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); } test "assign null directly to C pointer and test null equality" { @@ -200,3 +216,105 @@ test "assign null directly to C pointer and test null equality" { } comptime expect((y1 orelse &othery) == y1); } + +test "null terminated pointer" { + const S = struct { + fn doTheTest() void { + var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' }; + var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); + var no_zero_ptr: [*]const u8 = zero_ptr; + var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr); + expect(std.mem.eql(u8, std.mem.toSliceConst(u8, zero_ptr_again), "hello")); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "allow any sentinel" { + const S = struct { + fn doTheTest() void { + var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 }; + var ptr: [*:std.math.minInt(i32)]i32 = &array; + expect(ptr[4] == std.math.minInt(i32)); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "pointer sentinel with enums" { + const S = struct { + const Number = enum { + one, + two, + sentinel, + }; + + fn doTheTest() void { + var ptr: [*:.sentinel]Number = &[_:.sentinel]Number{ .one, .two, .two, .one }; + expect(ptr[4] == .sentinel); // TODO this should be comptime expect, see #3731 + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "pointer sentinel with optional element" { + const S = struct { + fn doTheTest() void { + var ptr: [*:null]?i32 = &[_:null]?i32{ 1, 2, 3, 4 }; + expect(ptr[4] == null); // TODO this should be comptime expect, see #3731 + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "pointer sentinel with +inf" { + const S = struct { + fn doTheTest() void { + const inf = std.math.inf_f32; + var ptr: [*:inf]f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 }; + expect(ptr[4] == inf); // TODO this should be comptime expect, see #3731 + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "pointer to array at fixed address" { + const array = @intToPtr(*volatile [1]u32, 0x10); + // Silly check just to reference `array` + expect(@ptrToInt(&array[0]) == 0x10); +} + +test "pointer arithmetic affects the alignment" { + { + var ptr: [*]align(8) u32 = undefined; + var x: usize = 1; + + expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); + const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 + expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); + const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 + expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); + const ptr3 = ptr + 0; // no-op + expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + x; // runtime-known addend + expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); + } + { + var ptr: [*]align(8) [3]u8 = undefined; + var x: usize = 1; + + const ptr1 = ptr + 17; // 3 * 17 = 51 + expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); + const ptr2 = ptr + x; // runtime-known addend + expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); + const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 + expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 + expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); + } +} diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig index 044864ced..884a7bdb6 100644 --- a/test/stage1/behavior/popcount.zig +++ b/test/stage1/behavior/popcount.zig @@ -35,7 +35,7 @@ fn testPopCount() void { expect(@popCount(i8, x) == 2); } comptime { - expect(@popCount(u8, @bitCast(u8, i8(-120))) == 2); + expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2); } comptime { expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24); diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig index bf9288821..1c1cf251a 100644 --- a/test/stage1/behavior/ptrcast.zig +++ b/test/stage1/behavior/ptrcast.zig @@ -37,7 +37,7 @@ fn testReinterpretBytesAsExternStruct() void { test "reinterpret struct field at comptime" { const numLittle = comptime Bytes.init(0x12345678); - expect(std.mem.eql(u8, [_]u8{ 0x78, 0x56, 0x34, 0x12 }, numLittle.bytes)); + expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numLittle.bytes)); } const Bytes = struct { @@ -55,12 +55,12 @@ test "comptime ptrcast keeps larger alignment" { comptime { const a: u32 = 1234; const p = @ptrCast([*]const u8, &a); - std.debug.assert(@typeOf(p) == [*]align(@alignOf(u32)) const u8); + std.debug.assert(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); } } test "implicit optional pointer to optional c_void pointer" { - var buf: [4]u8 = "aoeu"; + var buf: [4]u8 = "aoeu".*; var x: ?[*]u8 = &buf; var y: ?*c_void = x; var z = @ptrCast(*[4]u8, y); diff --git a/test/stage1/behavior/pub_enum.zig b/test/stage1/behavior/pub_enum.zig index 6275be7a0..0613df94d 100644 --- a/test/stage1/behavior/pub_enum.zig +++ b/test/stage1/behavior/pub_enum.zig @@ -9,5 +9,5 @@ fn pubEnumTest(foo: other.APubEnum) void { } test "cast with imported symbol" { - expect(other.size_t(42) == 42); + expect(@as(other.size_t, 42) == 42); } diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig index 739e0b318..37bf2a582 100644 --- a/test/stage1/behavior/reflection.zig +++ b/test/stage1/behavior/reflection.zig @@ -13,20 +13,18 @@ test "reflection: array, pointer, optional, error union type child" { test "reflection: function return type, var args, and param types" { comptime { - expect(@typeOf(dummy).ReturnType == i32); - expect(!@typeOf(dummy).is_var_args); - expect(@typeOf(dummy_varargs).is_var_args); - expect(@typeOf(dummy).arg_count == 3); - expect(@ArgType(@typeOf(dummy), 0) == bool); - expect(@ArgType(@typeOf(dummy), 1) == i32); - expect(@ArgType(@typeOf(dummy), 2) == f32); + expect(@TypeOf(dummy).ReturnType == i32); + expect(!@TypeOf(dummy).is_var_args); + expect(@TypeOf(dummy).arg_count == 3); + expect(@ArgType(@TypeOf(dummy), 0) == bool); + expect(@ArgType(@TypeOf(dummy), 1) == i32); + expect(@ArgType(@TypeOf(dummy), 2) == f32); } } fn dummy(a: bool, b: i32, c: f32) i32 { return 1234; } -fn dummy_varargs(args: ...) void {} test "reflection: struct member types and names" { comptime { diff --git a/test/stage1/behavior/shuffle.zig b/test/stage1/behavior/shuffle.zig index a8daf6557..2189b3e0e 100644 --- a/test/stage1/behavior/shuffle.zig +++ b/test/stage1/behavior/shuffle.zig @@ -7,39 +7,39 @@ test "@shuffle" { fn doTheTest() void { var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; - const mask: @Vector(4, i32) = [4]i32{ 0, ~i32(2), 3, ~i32(3) }; + const mask: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }; var res = @shuffle(i32, v, x, mask); - expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 40, 4 })); + expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); // Implicit cast from array (of mask) - res = @shuffle(i32, v, x, [4]i32{ 0, ~i32(2), 3, ~i32(3) }); - expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 40, 4 })); + res = @shuffle(i32, v, x, [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }); + expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); // Undefined const mask2: @Vector(4, i32) = [4]i32{ 3, 1, 2, 0 }; res = @shuffle(i32, v, undefined, mask2); - expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 40, -2, 30, 2147483647 })); + expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 })); // Upcasting of b var v2: @Vector(2, i32) = [2]i32{ 2147483647, undefined }; - const mask3: @Vector(4, i32) = [4]i32{ ~i32(0), 2, ~i32(0), 3 }; + const mask3: @Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 }; res = @shuffle(i32, x, v2, mask3); - expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 2147483647, 4 })); + expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 })); // Upcasting of a var v3: @Vector(2, i32) = [2]i32{ 2147483647, -2 }; - const mask4: @Vector(4, i32) = [4]i32{ 0, ~i32(2), 1, ~i32(3) }; + const mask4: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) }; res = @shuffle(i32, v3, x, mask4); - expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, -2, 4 })); + expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 })); // bool // Disabled because of #3317 if (@import("builtin").arch != .mipsel) { var x2: @Vector(4, bool) = [4]bool{ false, true, false, true }; var v4: @Vector(2, bool) = [2]bool{ true, false }; - const mask5: @Vector(4, i32) = [4]i32{ 0, ~i32(1), 1, 2 }; + const mask5: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; var res2 = @shuffle(bool, x2, v4, mask5); - expect(mem.eql(bool, ([4]bool)(res2), [4]bool{ false, false, true, false })); + expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); } // TODO re-enable when LLVM codegen is fixed @@ -47,9 +47,9 @@ test "@shuffle" { if (false) { var x2: @Vector(3, bool) = [3]bool{ false, true, false }; var v4: @Vector(2, bool) = [2]bool{ true, false }; - const mask5: @Vector(4, i32) = [4]i32{ 0, ~i32(1), 1, 2 }; + const mask5: @Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; var res2 = @shuffle(bool, x2, v4, mask5); - expect(mem.eql(bool, ([4]bool)(res2), [4]bool{ false, false, true, false })); + expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); } } }; diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index 0369be998..d46cdccd0 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -1,12 +1,12 @@ const builtin = @import("builtin"); const expect = @import("std").testing.expect; -test "@sizeOf and @typeOf" { - const y: @typeOf(x) = 120; - expect(@sizeOf(@typeOf(y)) == 2); +test "@sizeOf and @TypeOf" { + const y: @TypeOf(x) = 120; + expect(@sizeOf(@TypeOf(y)) == 2); } const x: u16 = 13; -const z: @typeOf(x) = 19; +const z: @TypeOf(x) = 19; const A = struct { a: u8, @@ -71,8 +71,8 @@ test "@bitOffsetOf" { test "@sizeOf on compile-time types" { expect(@sizeOf(comptime_int) == 0); expect(@sizeOf(comptime_float) == 0); - expect(@sizeOf(@typeOf(.hi)) == 0); - expect(@sizeOf(@typeOf(type)) == 0); + expect(@sizeOf(@TypeOf(.hi)) == 0); + expect(@sizeOf(@TypeOf(type)) == 0); } test "@sizeOf(T) == 0 doesn't force resolving struct size" { @@ -90,7 +90,7 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { expect(@sizeOf(S.Bar) == 8); } -test "@typeOf() has no runtime side effects" { +test "@TypeOf() has no runtime side effects" { const S = struct { fn foo(comptime T: type, ptr: *T) T { ptr.* += 1; @@ -98,12 +98,12 @@ test "@typeOf() has no runtime side effects" { } }; var data: i32 = 0; - const T = @typeOf(S.foo(i32, &data)); + const T = @TypeOf(S.foo(i32, &data)); comptime expect(T == i32); expect(data == 0); } -test "branching logic inside @typeOf" { +test "branching logic inside @TypeOf" { const S = struct { var data: i32 = 0; fn foo() anyerror!i32 { @@ -111,7 +111,7 @@ test "branching logic inside @typeOf" { return undefined; } }; - const T = @typeOf(S.foo() catch undefined); + const T = @TypeOf(S.foo() catch undefined); comptime expect(T == i32); expect(S.data == 0); } diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index d4a8353ca..b985ec1f8 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -28,7 +28,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { test "implicitly cast array of size 0 to slice" { var msg = [_]u8{}; - assertLenIsZero(msg); + assertLenIsZero(&msg); } fn assertLenIsZero(msg: []const u8) void { @@ -36,7 +36,7 @@ fn assertLenIsZero(msg: []const u8) void { } test "C pointer" { - var buf: [*c]const u8 = c"kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; + var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; var len: u32 = 10; var slice = buf[0..len]; expectEqualSlices(u8, "kjdhfkjdhf", slice); @@ -51,8 +51,8 @@ fn sliceSum(comptime q: []const u8) i32 { } test "comptime slices are disambiguated" { - expect(sliceSum([_]u8{ 1, 2 }) == 3); - expect(sliceSum([_]u8{ 3, 4 }) == 7); + expect(sliceSum(&[_]u8{ 1, 2 }) == 3); + expect(sliceSum(&[_]u8{ 3, 4 }) == 7); } test "slice type with custom alignment" { @@ -65,3 +65,35 @@ test "slice type with custom alignment" { slice[1].anything = 42; expect(array[1].anything == 42); } + +test "access len index of sentinel-terminated slice" { + const S = struct { + fn doTheTest() void { + var slice: [:0]const u8 = "hello"; + + expect(slice.len == 5); + expect(slice[5] == 0); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "obtaining a null terminated slice" { + // here we have a normal array + var buf: [50]u8 = undefined; + + buf[0] = 'a'; + buf[1] = 'b'; + buf[2] = 'c'; + buf[3] = 0; + + // now we obtain a null terminated slice: + const ptr = buf[0..3 :0]; + + var runtime_len: usize = 3; + const ptr2 = buf[0..runtime_len :0]; + // ptr2 is a null-terminated slice + comptime expect(@TypeOf(ptr2) == [:0]u8); + comptime expect(@TypeOf(ptr2[0..2]) == []u8); +} diff --git a/test/stage1/behavior/slicetobytes.zig b/test/stage1/behavior/slicetobytes.zig index b86b38bea..c0eb4a8f9 100644 --- a/test/stage1/behavior/slicetobytes.zig +++ b/test/stage1/behavior/slicetobytes.zig @@ -10,7 +10,7 @@ test "@sliceToBytes packed struct at runtime and comptime" { const S = struct { fn doTheTest() void { var foo: Foo = undefined; - var slice = @sliceToBytes(((*[1]Foo)(&foo))[0..1]); + var slice = @sliceToBytes(@as(*[1]Foo, &foo)[0..1]); slice[0] = 0x13; switch (builtin.endian) { builtin.Endian.Big => { diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index c0dc580c3..8e5ab29db 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -184,7 +184,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - expect(testPassSliceOfEmptyStructToFn([_]EmptyStruct2{EmptyStruct2{}}) == 1); + expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -388,8 +388,8 @@ test "runtime struct initialization of bitfield" { expect(s2.y == @intCast(u4, x2)); } -var x1 = u4(1); -var x2 = u8(2); +var x1 = @as(u4, 1); +var x2 = @as(u8, 2); const Nibbles = packed struct { x: u4, @@ -432,7 +432,7 @@ const Expr = union(enum) { }; fn alloc(comptime T: type) []T { - return [_]T{}; + return &[_]T{}; } test "call method with mutable reference to struct with no fields" { @@ -493,9 +493,10 @@ test "non-byte-aligned array inside packed struct" { fn doTheTest() void { var foo = Foo{ .a = true, - .b = "abcdefghijklmnopqurstu", + .b = "abcdefghijklmnopqurstu".*, }; - bar(foo.b); + const value = foo.b; + bar(&value); } }; S.doTheTest(); @@ -545,9 +546,9 @@ test "packed struct with fp fields" { s.data[1] = 2.0; s.data[2] = 3.0; s.frob(); - expectEqual(f32(6.0), s.data[0]); - expectEqual(f32(11.0), s.data[1]); - expectEqual(f32(20.0), s.data[2]); + expectEqual(@as(f32, 6.0), s.data[0]); + expectEqual(@as(f32, 11.0), s.data[1]); + expectEqual(@as(f32, 20.0), s.data[2]); } test "use within struct scope" { @@ -558,7 +559,7 @@ test "use within struct scope" { } }; }; - expectEqual(i32(42), S.inner()); + expectEqual(@as(i32, 42), S.inner()); } test "default struct initialization fields" { @@ -579,18 +580,18 @@ test "default struct initialization fields" { expectEqual(1239, x.a + x.b); } -test "extern fn returns struct by value" { +test "fn with C calling convention returns struct by value" { const S = struct { fn entry() void { var x = makeBar(10); - expectEqual(i32(10), x.handle); + expectEqual(@as(i32, 10), x.handle); } const ExternBar = extern struct { handle: i32, }; - extern fn makeBar(t: i32) ExternBar { + fn makeBar(t: i32) callconv(.C) ExternBar { return ExternBar{ .handle = t, }; @@ -614,7 +615,7 @@ test "for loop over pointers to struct, getting field from struct pointer" { const ArrayList = struct { fn toSlice(self: *ArrayList) []*Foo { - return ([*]*Foo)(undefined)[0..0]; + return @as([*]*Foo, undefined)[0..0]; } }; @@ -689,3 +690,135 @@ test "non-packed struct with u128 entry in union" { s.f2 = v2; std.testing.expect(s.f2.Num == 123); } + +test "packed struct field passed to generic function" { + const S = struct { + const P = packed struct { + b: u5, + g: u5, + r: u5, + a: u1, + }; + + fn genericReadPackedField(ptr: var) u5 { + return ptr.*; + } + }; + + var p: S.P = undefined; + p.b = 29; + var loaded = S.genericReadPackedField(&p.b); + expect(loaded == 29); +} + +test "anonymous struct literal syntax" { + const S = struct { + const Point = struct { + x: i32, + y: i32, + }; + + fn doTheTest() void { + var p: Point = .{ + .x = 1, + .y = 2, + }; + expect(p.x == 1); + expect(p.y == 2); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "fully anonymous struct" { + const S = struct { + fn doTheTest() void { + dump(.{ + .int = @as(u32, 1234), + .float = @as(f64, 12.34), + .b = true, + .s = "hi", + }); + } + fn dump(args: var) void { + expect(args.int == 1234); + expect(args.float == 12.34); + expect(args.b); + expect(args.s[0] == 'h'); + expect(args.s[1] == 'i'); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "fully anonymous list literal" { + const S = struct { + fn doTheTest() void { + dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); + } + fn dump(args: var) void { + expect(args.@"0" == 1234); + expect(args.@"1" == 12.34); + expect(args.@"2"); + expect(args.@"3"[0] == 'h'); + expect(args.@"3"[1] == 'i'); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "anonymous struct literal assigned to variable" { + var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; + expect(vec.@"0" == 22); + expect(vec.@"1" == 55); + expect(vec.@"2" == 99); +} + +test "struct with var field" { + const Point = struct { + x: var, + y: var, + }; + const pt = Point{ + .x = 1, + .y = 2, + }; + expect(pt.x == 1); + expect(pt.y == 2); +} + +test "comptime struct field" { + const T = struct { + a: i32, + comptime b: i32 = 1234, + }; + + var foo: T = undefined; + comptime expect(foo.b == 1234); +} + +test "anon struct literal field value initialized with fn call" { + const S = struct { + fn doTheTest() void { + var x = .{foo()}; + expectEqualSlices(u8, x[0], "hi"); + } + fn foo() []const u8 { + return "hi"; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "self-referencing struct via array member" { + const T = struct { + children: [1]*@This(), + }; + var x: T = undefined; + x = T{ .children = .{&x} }; + expect(x.children[0] == &x); +} diff --git a/test/stage1/behavior/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig index 2f3b5f41d..14bf0320a 100644 --- a/test/stage1/behavior/struct_contains_slice_of_itself.zig +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -14,21 +14,21 @@ test "struct contains slice of itself" { var other_nodes = [_]Node{ Node{ .payload = 31, - .children = [_]Node{}, + .children = &[_]Node{}, }, Node{ .payload = 32, - .children = [_]Node{}, + .children = &[_]Node{}, }, }; var nodes = [_]Node{ Node{ .payload = 1, - .children = [_]Node{}, + .children = &[_]Node{}, }, Node{ .payload = 2, - .children = [_]Node{}, + .children = &[_]Node{}, }, Node{ .payload = 3, @@ -51,21 +51,21 @@ test "struct contains aligned slice of itself" { var other_nodes = [_]NodeAligned{ NodeAligned{ .payload = 31, - .children = [_]NodeAligned{}, + .children = &[_]NodeAligned{}, }, NodeAligned{ .payload = 32, - .children = [_]NodeAligned{}, + .children = &[_]NodeAligned{}, }, }; var nodes = [_]NodeAligned{ NodeAligned{ .payload = 1, - .children = [_]NodeAligned{}, + .children = &[_]NodeAligned{}, }, NodeAligned{ .payload = 2, - .children = [_]NodeAligned{}, + .children = &[_]NodeAligned{}, }, NodeAligned{ .payload = 3, diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index bc1b4a7a0..3cfab63f8 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -68,7 +68,7 @@ test "switch statement" { } fn nonConstSwitch(foo: SwitchStatmentFoo) void { const val = switch (foo) { - SwitchStatmentFoo.A => i32(1), + SwitchStatmentFoo.A => @as(i32, 1), SwitchStatmentFoo.B => 2, SwitchStatmentFoo.C => 3, SwitchStatmentFoo.D => 4, @@ -127,7 +127,7 @@ test "switch with multiple expressions" { const x = switch (returnsFive()) { 1, 2, 3 => 1, 4, 5, 6 => 2, - else => i32(3), + else => @as(i32, 3), }; expect(x == 2); } @@ -186,7 +186,7 @@ fn testSwitchHandleAllCases() void { fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { return switch (x) { - 0 => u2(3), + 0 => @as(u2, 3), 1 => 2, 2 => 1, 3 => 0, @@ -195,7 +195,7 @@ fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { fn testSwitchHandleAllCasesRange(x: u8) u8 { return switch (x) { - 0...100 => u8(0), + 0...100 => @as(u8, 0), 101...200 => 1, 201, 203 => 2, 202 => 4, @@ -406,7 +406,7 @@ test "switch prongs with cases with identical payload types" { fn doTheSwitch1(u: Union) void { switch (u) { .A, .C => |e| { - expect(@typeOf(e) == usize); + expect(@TypeOf(e) == usize); expect(e == 8); }, .B => |e| @panic("fail"), @@ -416,7 +416,7 @@ test "switch prongs with cases with identical payload types" { switch (u) { .A, .C => |e| @panic("fail"), .B => |e| { - expect(@typeOf(e) == isize); + expect(@TypeOf(e) == isize); expect(e == -8); }, } @@ -434,3 +434,48 @@ test "switch with disjoint range" { 126...126 => {}, } } + +var state: u32 = 0; +fn poll() void { + switch (state) { + 0 => { + state = 1; + }, + else => { + state += 1; + }, + } +} + +test "switch on global mutable var isn't constant-folded" { + while (state < 2) { + poll(); + } +} + +test "switch on pointer type" { + const S = struct { + const X = struct { + field: u32, + }; + + const P1 = @intToPtr(*X, 0x400); + const P2 = @intToPtr(*X, 0x800); + const P3 = @intToPtr(*X, 0xC00); + + fn doTheTest(arg: *X) i32 { + switch (arg) { + P1 => return 1, + P2 => return 2, + else => return 3, + } + } + }; + + expect(1 == S.doTheTest(S.P1)); + expect(2 == S.doTheTest(S.P2)); + expect(3 == S.doTheTest(S.P3)); + comptime expect(1 == S.doTheTest(S.P1)); + comptime expect(2 == S.doTheTest(S.P2)); + comptime expect(3 == S.doTheTest(S.P3)); +} diff --git a/test/stage1/behavior/try.zig b/test/stage1/behavior/try.zig index 9c700f626..9e93183c3 100644 --- a/test/stage1/behavior/try.zig +++ b/test/stage1/behavior/try.zig @@ -8,7 +8,7 @@ test "try on error union" { fn tryOnErrorUnionImpl() void { const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { error.ItBroke, error.NoMem => 1, - error.CrappedOut => i32(2), + error.CrappedOut => @as(i32, 2), else => unreachable, }; expect(x == 11); @@ -19,10 +19,10 @@ fn returnsTen() anyerror!i32 { } test "try without vars" { - const result1 = if (failIfTrue(true)) 1 else |_| i32(2); + const result1 = if (failIfTrue(true)) 1 else |_| @as(i32, 2); expect(result1 == 2); - const result2 = if (failIfTrue(false)) 1 else |_| i32(2); + const result2 = if (failIfTrue(false)) 1 else |_| @as(i32, 2); expect(result2 == 1); } diff --git a/test/stage1/behavior/tuple.zig b/test/stage1/behavior/tuple.zig new file mode 100644 index 000000000..13d3d0568 --- /dev/null +++ b/test/stage1/behavior/tuple.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "tuple concatenation" { + const S = struct { + fn doTheTest() void { + var a: i32 = 1; + var b: i32 = 2; + var x = .{a}; + var y = .{b}; + var c = x ++ y; + expect(c[0] == 1); + expect(c[1] == 2); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index b84369a16..67c78b305 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -11,103 +11,163 @@ fn testTypes(comptime types: []const type) void { } test "Type.MetaType" { - testing.expect(type == @Type(TypeInfo { .Type = undefined })); - testTypes([_]type {type}); + testing.expect(type == @Type(TypeInfo{ .Type = undefined })); + testTypes(&[_]type{type}); } test "Type.Void" { - testing.expect(void == @Type(TypeInfo { .Void = undefined })); - testTypes([_]type {void}); + testing.expect(void == @Type(TypeInfo{ .Void = undefined })); + testTypes(&[_]type{void}); } test "Type.Bool" { - testing.expect(bool == @Type(TypeInfo { .Bool = undefined })); - testTypes([_]type {bool}); + testing.expect(bool == @Type(TypeInfo{ .Bool = undefined })); + testTypes(&[_]type{bool}); } test "Type.NoReturn" { - testing.expect(noreturn == @Type(TypeInfo { .NoReturn = undefined })); - testTypes([_]type {noreturn}); + testing.expect(noreturn == @Type(TypeInfo{ .NoReturn = undefined })); + testTypes(&[_]type{noreturn}); } test "Type.Int" { - testing.expect(u1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 1 } })); - testing.expect(i1 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 1 } })); - testing.expect(u8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 8 } })); - testing.expect(i8 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 8 } })); - testing.expect(u64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = false, .bits = 64 } })); - testing.expect(i64 == @Type(TypeInfo { .Int = TypeInfo.Int { .is_signed = true, .bits = 64 } })); - testTypes([_]type {u8,u32,i64}); + testing.expect(u1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 1 } })); + testing.expect(i1 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 1 } })); + testing.expect(u8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 8 } })); + testing.expect(i8 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 8 } })); + testing.expect(u64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = false, .bits = 64 } })); + testing.expect(i64 == @Type(TypeInfo{ .Int = TypeInfo.Int{ .is_signed = true, .bits = 64 } })); + testTypes(&[_]type{ u8, u32, i64 }); } test "Type.Float" { - testing.expect(f16 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 16 } })); - testing.expect(f32 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 32 } })); - testing.expect(f64 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 64 } })); - testing.expect(f128 == @Type(TypeInfo { .Float = TypeInfo.Float { .bits = 128 } })); - testTypes([_]type {f16, f32, f64, f128}); + testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); + testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); + testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); + testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); + testTypes(&[_]type{ f16, f32, f64, f128 }); } test "Type.Pointer" { - testTypes([_]type { + testTypes(&[_]type{ // One Value Pointer Types - *u8, *const u8, - *volatile u8, *const volatile u8, - *align(4) u8, *const align(4) u8, - *volatile align(4) u8, *const volatile align(4) u8, - *align(8) u8, *const align(8) u8, - *volatile align(8) u8, *const volatile align(8) u8, - *allowzero u8, *const allowzero u8, - *volatile allowzero u8, *const volatile allowzero u8, - *align(4) allowzero u8, *const align(4) allowzero u8, - *volatile align(4) allowzero u8, *const volatile align(4) allowzero u8, + *u8, *const u8, + *volatile u8, *const volatile u8, + *align(4) u8, *align(4) const u8, + *align(4) volatile u8, *align(4) const volatile u8, + *align(8) u8, *align(8) const u8, + *align(8) volatile u8, *align(8) const volatile u8, + *allowzero u8, *allowzero const u8, + *allowzero volatile u8, *allowzero const volatile u8, + *allowzero align(4) u8, *allowzero align(4) const u8, + *allowzero align(4) volatile u8, *allowzero align(4) const volatile u8, // Many Values Pointer Types - [*]u8, [*]const u8, - [*]volatile u8, [*]const volatile u8, - [*]align(4) u8, [*]const align(4) u8, - [*]volatile align(4) u8, [*]const volatile align(4) u8, - [*]align(8) u8, [*]const align(8) u8, - [*]volatile align(8) u8, [*]const volatile align(8) u8, - [*]allowzero u8, [*]const allowzero u8, - [*]volatile allowzero u8, [*]const volatile allowzero u8, - [*]align(4) allowzero u8, [*]const align(4) allowzero u8, - [*]volatile align(4) allowzero u8, [*]const volatile align(4) allowzero u8, + [*]u8, [*]const u8, + [*]volatile u8, [*]const volatile u8, + [*]align(4) u8, [*]align(4) const u8, + [*]align(4) volatile u8, [*]align(4) const volatile u8, + [*]align(8) u8, [*]align(8) const u8, + [*]align(8) volatile u8, [*]align(8) const volatile u8, + [*]allowzero u8, [*]allowzero const u8, + [*]allowzero volatile u8, [*]allowzero const volatile u8, + [*]allowzero align(4) u8, [*]allowzero align(4) const u8, + [*]allowzero align(4) volatile u8, [*]allowzero align(4) const volatile u8, // Slice Types - []u8, []const u8, - []volatile u8, []const volatile u8, - []align(4) u8, []const align(4) u8, - []volatile align(4) u8, []const volatile align(4) u8, - []align(8) u8, []const align(8) u8, - []volatile align(8) u8, []const volatile align(8) u8, - []allowzero u8, []const allowzero u8, - []volatile allowzero u8, []const volatile allowzero u8, - []align(4) allowzero u8, []const align(4) allowzero u8, - []volatile align(4) allowzero u8, []const volatile align(4) allowzero u8, + []u8, []const u8, + []volatile u8, []const volatile u8, + []align(4) u8, []align(4) const u8, + []align(4) volatile u8, []align(4) const volatile u8, + []align(8) u8, []align(8) const u8, + []align(8) volatile u8, []align(8) const volatile u8, + []allowzero u8, []allowzero const u8, + []allowzero volatile u8, []allowzero const volatile u8, + []allowzero align(4) u8, []allowzero align(4) const u8, + []allowzero align(4) volatile u8, []allowzero align(4) const volatile u8, // C Pointer Types - [*c]u8, [*c]const u8, - [*c]volatile u8, [*c]const volatile u8, - [*c]align(4) u8, [*c]const align(4) u8, - [*c]volatile align(4) u8, [*c]const volatile align(4) u8, - [*c]align(8) u8, [*c]const align(8) u8, - [*c]volatile align(8) u8, [*c]const volatile align(8) u8, + [*c]u8, [*c]const u8, + [*c]volatile u8, [*c]const volatile u8, + [*c]align(4) u8, [*c]align(4) const u8, + [*c]align(4) volatile u8, [*c]align(4) const volatile u8, + [*c]align(8) u8, [*c]align(8) const u8, + [*c]align(8) volatile u8, [*c]align(8) const volatile u8, }); } test "Type.Array" { - testing.expect([123]u8 == @Type(TypeInfo { .Array = TypeInfo.Array { .len = 123, .child = u8 } })); - testing.expect([2]u32 == @Type(TypeInfo { .Array = TypeInfo.Array { .len = 2, .child = u32 } })); - testTypes([_]type {[1]u8, [30]usize, [7]bool}); + testing.expect([123]u8 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 123, + .child = u8, + .sentinel = null, + }, + })); + testing.expect([2]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = null, + }, + })); + testing.expect([2:0]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = 0, + }, + })); + testTypes(&[_]type{ [1]u8, [30]usize, [7]bool }); } test "Type.ComptimeFloat" { - testTypes([_]type {comptime_float}); + testTypes(&[_]type{comptime_float}); } test "Type.ComptimeInt" { - testTypes([_]type {comptime_int}); + testTypes(&[_]type{comptime_int}); } test "Type.Undefined" { - testTypes([_]type {@typeOf(undefined)}); + testTypes(&[_]type{@TypeOf(undefined)}); } test "Type.Null" { - testTypes([_]type {@typeOf(null)}); + testTypes(&[_]type{@TypeOf(null)}); +} +test "@Type create slice with null sentinel" { + const Slice = @Type(builtin.TypeInfo{ + .Pointer = .{ + .size = .Slice, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .alignment = 8, + .child = *i32, + .sentinel = null, + }, + }); + testing.expect(Slice == []align(8) const *i32); +} +test "@Type picks up the sentinel value from TypeInfo" { + testTypes(&[_]type{ + [11:0]u8, [4:10]u8, + [*:0]u8, [*:0]const u8, + [*:0]volatile u8, [*:0]const volatile u8, + [*:0]align(4) u8, [*:0]align(4) const u8, + [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8, + [*:0]align(8) u8, [*:0]align(8) const u8, + [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8, + [*:0]allowzero u8, [*:0]allowzero const u8, + [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8, + [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8, + [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8, + [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8, + [:0]u8, [:0]const u8, + [:0]volatile u8, [:0]const volatile u8, + [:0]align(4) u8, [:0]align(4) const u8, + [:0]align(4) volatile u8, [:0]align(4) const volatile u8, + [:0]align(8) u8, [:0]align(8) const u8, + [:0]align(8) volatile u8, [:0]align(8) const volatile u8, + [:0]allowzero u8, [:0]allowzero const u8, + [:0]allowzero volatile u8, [:0]allowzero const volatile u8, + [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8, + [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8, + [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8, + }); } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index b86ba27c1..d35cc8831 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -13,7 +13,7 @@ test "type info: tag type, void info" { fn testBasic() void { expect(@TagType(TypeInfo) == TypeId); const void_info = @typeInfo(void); - expect(TypeId(void_info) == TypeId.Void); + expect(@as(TypeId, void_info) == TypeId.Void); expect(void_info.Void == {}); } @@ -24,12 +24,12 @@ test "type info: integer, floating point type info" { fn testIntFloat() void { const u8_info = @typeInfo(u8); - expect(TypeId(u8_info) == TypeId.Int); + expect(@as(TypeId, u8_info) == TypeId.Int); expect(!u8_info.Int.is_signed); expect(u8_info.Int.bits == 8); const f64_info = @typeInfo(f64); - expect(TypeId(f64_info) == TypeId.Float); + expect(@as(TypeId, f64_info) == TypeId.Float); expect(f64_info.Float.bits == 64); } @@ -40,12 +40,13 @@ test "type info: pointer type info" { fn testPointer() void { const u32_ptr_info = @typeInfo(*u32); - expect(TypeId(u32_ptr_info) == TypeId.Pointer); + expect(@as(TypeId, u32_ptr_info) == TypeId.Pointer); expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); expect(u32_ptr_info.Pointer.is_const == false); expect(u32_ptr_info.Pointer.is_volatile == false); expect(u32_ptr_info.Pointer.alignment == @alignOf(u32)); expect(u32_ptr_info.Pointer.child == u32); + expect(u32_ptr_info.Pointer.sentinel == null); } test "type info: unknown length pointer type info" { @@ -55,14 +56,34 @@ test "type info: unknown length pointer type info" { fn testUnknownLenPtr() void { const u32_ptr_info = @typeInfo([*]const volatile f64); - expect(TypeId(u32_ptr_info) == TypeId.Pointer); + expect(@as(TypeId, u32_ptr_info) == TypeId.Pointer); expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); expect(u32_ptr_info.Pointer.is_const == true); expect(u32_ptr_info.Pointer.is_volatile == true); + expect(u32_ptr_info.Pointer.sentinel == null); expect(u32_ptr_info.Pointer.alignment == @alignOf(f64)); expect(u32_ptr_info.Pointer.child == f64); } +test "type info: null terminated pointer type info" { + testNullTerminatedPtr(); + comptime testNullTerminatedPtr(); +} + +fn testNullTerminatedPtr() void { + const ptr_info = @typeInfo([*:0]u8); + expect(@as(TypeId, ptr_info) == TypeId.Pointer); + expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + expect(ptr_info.Pointer.is_const == false); + expect(ptr_info.Pointer.is_volatile == false); + expect(ptr_info.Pointer.sentinel.? == 0); + + expect(@typeInfo([:0]u8).Pointer.sentinel != null); + expect(@typeInfo([10:0]u8).Array.sentinel != null); + expect(@typeInfo([10:0]u8).Array.len == 10); + expect(@sizeOf([10:0]u8) == 11); +} + test "type info: C pointer type info" { testCPtr(); comptime testCPtr(); @@ -70,7 +91,7 @@ test "type info: C pointer type info" { fn testCPtr() void { const ptr_info = @typeInfo([*c]align(4) const i8); - expect(TypeId(ptr_info) == TypeId.Pointer); + expect(@as(TypeId, ptr_info) == TypeId.Pointer); expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C); expect(ptr_info.Pointer.is_const); expect(!ptr_info.Pointer.is_volatile); @@ -85,7 +106,7 @@ test "type info: slice type info" { fn testSlice() void { const u32_slice_info = @typeInfo([]u32); - expect(TypeId(u32_slice_info) == TypeId.Pointer); + expect(@as(TypeId, u32_slice_info) == TypeId.Pointer); expect(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); expect(u32_slice_info.Pointer.is_const == false); expect(u32_slice_info.Pointer.is_volatile == false); @@ -100,7 +121,7 @@ test "type info: array type info" { fn testArray() void { const arr_info = @typeInfo([42]bool); - expect(TypeId(arr_info) == TypeId.Array); + expect(@as(TypeId, arr_info) == TypeId.Array); expect(arr_info.Array.len == 42); expect(arr_info.Array.child == bool); } @@ -112,7 +133,7 @@ test "type info: optional type info" { fn testOptional() void { const null_info = @typeInfo(?void); - expect(TypeId(null_info) == TypeId.Optional); + expect(@as(TypeId, null_info) == TypeId.Optional); expect(null_info.Optional.child == void); } @@ -129,18 +150,18 @@ fn testErrorSet() void { }; const error_set_info = @typeInfo(TestErrorSet); - expect(TypeId(error_set_info) == TypeId.ErrorSet); + expect(@as(TypeId, error_set_info) == TypeId.ErrorSet); expect(error_set_info.ErrorSet.?.len == 3); expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "First")); expect(error_set_info.ErrorSet.?[2].value == @errorToInt(TestErrorSet.Third)); const error_union_info = @typeInfo(TestErrorSet!usize); - expect(TypeId(error_union_info) == TypeId.ErrorUnion); + expect(@as(TypeId, error_union_info) == TypeId.ErrorUnion); expect(error_union_info.ErrorUnion.error_set == TestErrorSet); expect(error_union_info.ErrorUnion.payload == usize); const global_info = @typeInfo(anyerror); - expect(TypeId(global_info) == TypeId.ErrorSet); + expect(@as(TypeId, global_info) == TypeId.ErrorSet); expect(global_info.ErrorSet == null); } @@ -158,7 +179,7 @@ fn testEnum() void { }; const os_info = @typeInfo(Os); - expect(TypeId(os_info) == TypeId.Enum); + expect(@as(TypeId, os_info) == TypeId.Enum); expect(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); expect(os_info.Enum.fields.len == 4); expect(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); @@ -174,14 +195,14 @@ test "type info: union info" { fn testUnion() void { const typeinfo_info = @typeInfo(TypeInfo); - expect(TypeId(typeinfo_info) == TypeId.Union); + expect(@as(TypeId, typeinfo_info) == TypeId.Union); expect(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); expect(typeinfo_info.Union.tag_type.? == TypeId); - expect(typeinfo_info.Union.fields.len == 26); + expect(typeinfo_info.Union.fields.len == 25); expect(typeinfo_info.Union.fields[4].enum_field != null); expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4); - expect(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - expect(typeinfo_info.Union.decls.len == 21); + expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int)); + expect(typeinfo_info.Union.decls.len == 20); const TestNoTagUnion = union { Foo: void, @@ -189,7 +210,7 @@ fn testUnion() void { }; const notag_union_info = @typeInfo(TestNoTagUnion); - expect(TypeId(notag_union_info) == TypeId.Union); + expect(@as(TypeId, notag_union_info) == TypeId.Union); expect(notag_union_info.Union.tag_type == null); expect(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); expect(notag_union_info.Union.fields.len == 2); @@ -214,7 +235,7 @@ test "type info: struct info" { fn testStruct() void { const struct_info = @typeInfo(TestStruct); - expect(TypeId(struct_info) == TypeId.Struct); + expect(@as(TypeId, struct_info) == TypeId.Struct); expect(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); expect(struct_info.Struct.fields.len == 3); expect(struct_info.Struct.fields[1].offset == null); @@ -243,17 +264,17 @@ test "type info: function type info" { } fn testFunction() void { - const fn_info = @typeInfo(@typeOf(foo)); - expect(TypeId(fn_info) == TypeId.Fn); - expect(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + const fn_info = @typeInfo(@TypeOf(foo)); + expect(@as(TypeId, fn_info) == TypeId.Fn); + expect(fn_info.Fn.calling_convention == .Unspecified); expect(fn_info.Fn.is_generic); expect(fn_info.Fn.args.len == 2); expect(fn_info.Fn.is_var_args); expect(fn_info.Fn.return_type == null); const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - expect(TypeId(bound_fn_info) == TypeId.BoundFn); + const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); + expect(@as(TypeId, bound_fn_info) == TypeId.BoundFn); expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); } @@ -275,7 +296,7 @@ test "type info: vectors" { fn testVector() void { const vec_info = @typeInfo(@Vector(4, i32)); - expect(TypeId(vec_info) == TypeId.Vector); + expect(@as(TypeId, vec_info) == TypeId.Vector); expect(vec_info.Vector.len == 4); expect(vec_info.Vector.child == i32); } @@ -288,13 +309,13 @@ test "type info: anyframe and anyframe->T" { fn testAnyFrame() void { { const anyframe_info = @typeInfo(anyframe->i32); - expect(TypeId(anyframe_info) == .AnyFrame); + expect(@as(TypeId, anyframe_info) == .AnyFrame); expect(anyframe_info.AnyFrame.child.? == i32); } { const anyframe_info = @typeInfo(anyframe); - expect(TypeId(anyframe_info) == .AnyFrame); + expect(@as(TypeId, anyframe_info) == .AnyFrame); expect(anyframe_info.AnyFrame.child == null); } } @@ -334,8 +355,20 @@ test "type info: extern fns with and without lib names" { if (std.mem.eql(u8, decl.name, "bar1")) { expect(decl.data.Fn.lib_name == null); } else { - std.testing.expectEqual(([]const u8)("cool"), decl.data.Fn.lib_name.?); + std.testing.expectEqual(@as([]const u8, "cool"), decl.data.Fn.lib_name.?); } } } } + +test "data field is a compile-time value" { + const S = struct { + const Bar = @as(isize, -1); + }; + comptime expect(@typeInfo(S).Struct.decls[0].data.Var == isize); +} + +test "sentinel of opaque pointer type" { + const c_void_info = @typeInfo(*c_void); + expect(c_void_info.Pointer.sentinel == null); +} diff --git a/test/stage1/behavior/undefined.zig b/test/stage1/behavior/undefined.zig index 4f2cbed3b..3506e7e24 100644 --- a/test/stage1/behavior/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -64,5 +64,5 @@ test "assign undefined to struct with method" { test "type name of undefined" { const x = undefined; - expect(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + expect(mem.eql(u8, @typeName(@TypeOf(x)), "(undefined)")); } diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index d340a52d1..a24bee03e 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -14,7 +14,7 @@ const Agg = struct { const v1 = Value{ .Int = 1234 }; const v2 = Value{ .Array = [_]u8{3} ** 9 }; -const err = (anyerror!Agg)(Agg{ +const err = @as(anyerror!Agg, Agg{ .val1 = v1, .val2 = v2, }); @@ -110,11 +110,11 @@ fn doTest() void { } fn bar(value: Payload) i32 { - expect(Letter(value) == Letter.A); + expect(@as(Letter, value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, - Payload.B => |x| if (x == 12.34) i32(20) else 21, - Payload.C => |x| if (x) i32(30) else 31, + Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21, + Payload.C => |x| if (x) @as(i32, 30) else 31, }; } @@ -127,7 +127,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; expect(x == MultipleChoice.C); - expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + expect(@enumToInt(@as(@TagType(MultipleChoice), x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -149,11 +149,11 @@ test "union(enum(u32)) with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + expect(@enumToInt(@as(@TagType(MultipleChoice2), x)) == 60); expect(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, - MultipleChoice2.C => |v| i32(1000) + v, + MultipleChoice2.C => |v| @as(i32, 1000) + v, MultipleChoice2.D => 4, MultipleChoice2.Unspecified1 => 5, MultipleChoice2.Unspecified2 => 6, @@ -208,12 +208,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - expect(TheTag(x) == TheTag.B); + expect(@as(TheTag, x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - expect(Letter2(x) == Letter2.B); + expect(@as(Letter2, x) == Letter2.B); } const Letter2 = enum { A, @@ -241,7 +241,7 @@ pub const PackThis = union(enum) { }; test "constant packed union" { - testConstPackedUnion([_]PackThis{PackThis{ .StringLiteral = 1 }}); + testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }}); } fn testConstPackedUnion(expected_tokens: []const PackThis) void { @@ -297,7 +297,7 @@ const TaggedUnionWithAVoid = union(enum) { fn testTaggedUnionInit(x: var) bool { const y = TaggedUnionWithAVoid{ .A = x }; - return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; + return @as(@TagType(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; } pub const UnionEnumNoPayloads = union(enum) { @@ -326,7 +326,7 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); comptime expect(@TagType(Tag) == u0); - var t = Tag(e); + var t = @as(Tag, e); expect(t == Expr.Literal); } @@ -346,7 +346,7 @@ test "union with only 1 field casted to its enum type which has enum value speci var e = Expr{ .Literal = Literal{ .Bool = true } }; comptime expect(@TagType(Tag) == comptime_int); - var t = Tag(e); + var t = @as(Tag, e); expect(t == Expr.Literal); expect(@enumToInt(t) == 33); comptime expect(@enumToInt(t) == 33); @@ -511,3 +511,121 @@ test "union with comptime_int tag" { }; comptime expect(@TagType(@TagType(Union)) == comptime_int); } + +test "extern union doesn't trigger field check at comptime" { + const U = extern union { + x: u32, + y: u8, + }; + + const x = U{ .x = 0x55AAAA55 }; + comptime expect(x.y == 0x55); +} + +const Foo1 = union(enum) { + f: struct { + x: usize, + }, +}; +var glbl: Foo1 = undefined; + +test "global union with single field is correctly initialized" { + glbl = Foo1{ + .f = @memberType(Foo1, 0){ .x = 123 }, + }; + expect(glbl.f.x == 123); +} + +pub const FooUnion = union(enum) { + U0: usize, + U1: u8, +}; + +var glbl_array: [2]FooUnion = undefined; + +test "initialize global array of union" { + glbl_array[1] = FooUnion{ .U1 = 2 }; + glbl_array[0] = FooUnion{ .U0 = 1 }; + expect(glbl_array[0].U0 == 1); + expect(glbl_array[1].U1 == 2); +} + +test "anonymous union literal syntax" { + const S = struct { + const Number = union { + int: i32, + float: f64, + }; + + fn doTheTest() void { + var i: Number = .{ .int = 42 }; + var f = makeNumber(); + expect(i.int == 42); + expect(f.float == 12.34); + } + + fn makeNumber() Number { + return .{ .float = 12.34 }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "update the tag value for zero-sized unions" { + const S = union(enum) { + U0: void, + U1: void, + }; + var x = S{ .U0 = {} }; + expect(x == .U0); + x = S{ .U1 = {} }; + expect(x == .U1); +} + +test "function call result coerces from tagged union to the tag" { + const S = struct { + const Arch = union(enum) { + One, + Two: usize, + }; + + const ArchTag = @TagType(Arch); + + fn doTheTest() void { + var x: ArchTag = getArch1(); + expect(x == .One); + + var y: ArchTag = getArch2(); + expect(y == .Two); + } + + pub fn getArch1() Arch { + return .One; + } + + pub fn getArch2() Arch { + return .{ .Two = 99 }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "0-sized extern union definition" { + const U = extern union { + a: void, + const f = 1; + }; + + expect(U.f == 1); +} + +test "union initializer generates padding only if needed" { + const U = union(enum) { + A: u24, + }; + + var v = U{ .A = 532 }; + expect(v.A == 532); +} diff --git a/test/stage1/behavior/var_args.zig b/test/stage1/behavior/var_args.zig index 191893ff8..0c8674a7f 100644 --- a/test/stage1/behavior/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,7 +1,7 @@ const expect = @import("std").testing.expect; -fn add(args: ...) i32 { - var sum = i32(0); +fn add(args: var) i32 { + var sum = @as(i32, 0); { comptime var i: usize = 0; inline while (i < args.len) : (i += 1) { @@ -12,43 +12,42 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - expect(add(i32(1), i32(2), i32(3), i32(4)) == 10); - expect(add(i32(1234)) == 1234); - expect(add() == 0); + expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10); + expect(add(.{@as(i32, 1234)}) == 1234); + expect(add(.{}) == 0); } -fn readFirstVarArg(args: ...) void { +fn readFirstVarArg(args: var) void { const value = args[0]; } test "send void arg to var args" { - readFirstVarArg({}); + readFirstVarArg(.{{}}); } test "pass args directly" { - expect(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - expect(addSomeStuff(i32(1234)) == 1234); - expect(addSomeStuff() == 0); + expect(addSomeStuff(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10); + expect(addSomeStuff(.{@as(i32, 1234)}) == 1234); + expect(addSomeStuff(.{}) == 0); } -fn addSomeStuff(args: ...) i32 { +fn addSomeStuff(args: var) i32 { return add(args); } test "runtime parameter before var args" { - expect(extraFn(10) == 0); - expect(extraFn(10, false) == 1); - expect(extraFn(10, false, true) == 2); + expect(extraFn(10, .{}) == 0); + expect(extraFn(10, .{false}) == 1); + expect(extraFn(10, .{ false, true }) == 2); - // TODO issue #313 - //comptime { - // expect(extraFn(10) == 0); - // expect(extraFn(10, false) == 1); - // expect(extraFn(10, false, true) == 2); - //} + comptime { + expect(extraFn(10, .{}) == 0); + expect(extraFn(10, .{false}) == 1); + expect(extraFn(10, .{ false, true }) == 2); + } } -fn extraFn(extra: u32, args: ...) usize { +fn extraFn(extra: u32, args: var) usize { if (args.len >= 1) { expect(args[0] == false); } @@ -58,27 +57,27 @@ fn extraFn(extra: u32, args: ...) usize { return args.len; } -const foos = [_]fn (...) bool{ +const foos = [_]fn (var) bool{ foo1, foo2, }; -fn foo1(args: ...) bool { +fn foo1(args: var) bool { return true; } -fn foo2(args: ...) bool { +fn foo2(args: var) bool { return false; } test "array of var args functions" { - expect(foos[0]()); - expect(!foos[1]()); + expect(foos[0](.{})); + expect(!foos[1](.{})); } test "pass zero length array to var args param" { - doNothingWithFirstArg(""); + doNothingWithFirstArg(.{""}); } -fn doNothingWithFirstArg(args: ...) void { +fn doNothingWithFirstArg(args: var) void { const a = args[0]; } diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index d3a771fca..338f24d80 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -1,13 +1,14 @@ const std = @import("std"); const mem = std.mem; const expect = std.testing.expect; +const builtin = @import("builtin"); test "implicit cast vector to array - bool" { const S = struct { fn doTheTest() void { const a: @Vector(4, bool) = [_]bool{ true, false, true, false }; const result_array: [4]bool = a; - expect(mem.eql(bool, result_array, [4]bool{ true, false, true, false })); + expect(mem.eql(bool, &result_array, &[4]bool{ true, false, true, false })); } }; S.doTheTest(); @@ -19,11 +20,11 @@ test "vector wrap operators" { fn doTheTest() void { var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; - expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ -2147483648, 2147483645, 33, 44 })); - expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 2147483646, 2147483647, 27, 36 })); - expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 2147483647, 2, 90, 160 })); + expect(mem.eql(i32, &@as([4]i32, v +% x), &[4]i32{ -2147483648, 2147483645, 33, 44 })); + expect(mem.eql(i32, &@as([4]i32, v -% x), &[4]i32{ 2147483646, 2147483647, 27, 36 })); + expect(mem.eql(i32, &@as([4]i32, v *% x), &[4]i32{ 2147483647, 2, 90, 160 })); var z: @Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 }; - expect(mem.eql(i32, ([4]i32)(-%z), [4]i32{ -1, -2, -3, -2147483648 })); + expect(mem.eql(i32, &@as([4]i32, -%z), &[4]i32{ -1, -2, -3, -2147483648 })); } }; S.doTheTest(); @@ -35,12 +36,12 @@ test "vector bin compares with mem.eql" { fn doTheTest() void { var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 30, 4 }; - expect(mem.eql(bool, ([4]bool)(v == x), [4]bool{ false, false, true, false })); - expect(mem.eql(bool, ([4]bool)(v != x), [4]bool{ true, true, false, true })); - expect(mem.eql(bool, ([4]bool)(v < x), [4]bool{ false, true, false, false })); - expect(mem.eql(bool, ([4]bool)(v > x), [4]bool{ true, false, false, true })); - expect(mem.eql(bool, ([4]bool)(v <= x), [4]bool{ false, true, true, false })); - expect(mem.eql(bool, ([4]bool)(v >= x), [4]bool{ true, false, true, true })); + expect(mem.eql(bool, &@as([4]bool, v == x), &[4]bool{ false, false, true, false })); + expect(mem.eql(bool, &@as([4]bool, v != x), &[4]bool{ true, true, false, true })); + expect(mem.eql(bool, &@as([4]bool, v < x), &[4]bool{ false, true, false, false })); + expect(mem.eql(bool, &@as([4]bool, v > x), &[4]bool{ true, false, false, true })); + expect(mem.eql(bool, &@as([4]bool, v <= x), &[4]bool{ false, true, true, false })); + expect(mem.eql(bool, &@as([4]bool, v >= x), &[4]bool{ true, false, true, true })); } }; S.doTheTest(); @@ -52,10 +53,10 @@ test "vector int operators" { fn doTheTest() void { var v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; var x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; - expect(mem.eql(i32, ([4]i32)(v + x), [4]i32{ 11, 22, 33, 44 })); - expect(mem.eql(i32, ([4]i32)(v - x), [4]i32{ 9, 18, 27, 36 })); - expect(mem.eql(i32, ([4]i32)(v * x), [4]i32{ 10, 40, 90, 160 })); - expect(mem.eql(i32, ([4]i32)(-v), [4]i32{ -10, -20, -30, -40 })); + expect(mem.eql(i32, &@as([4]i32, v + x), &[4]i32{ 11, 22, 33, 44 })); + expect(mem.eql(i32, &@as([4]i32, v - x), &[4]i32{ 9, 18, 27, 36 })); + expect(mem.eql(i32, &@as([4]i32, v * x), &[4]i32{ 10, 40, 90, 160 })); + expect(mem.eql(i32, &@as([4]i32, -v), &[4]i32{ -10, -20, -30, -40 })); } }; S.doTheTest(); @@ -67,10 +68,10 @@ test "vector float operators" { fn doTheTest() void { var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; - expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 })); - expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 })); - expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 })); - expect(mem.eql(f32, ([4]f32)(-x), [4]f32{ -1, -2, -3, -4 })); + expect(mem.eql(f32, &@as([4]f32, v + x), &[4]f32{ 11, 22, 33, 44 })); + expect(mem.eql(f32, &@as([4]f32, v - x), &[4]f32{ 9, 18, 27, 36 })); + expect(mem.eql(f32, &@as([4]f32, v * x), &[4]f32{ 10, 40, 90, 160 })); + expect(mem.eql(f32, &@as([4]f32, -x), &[4]f32{ -1, -2, -3, -4 })); } }; S.doTheTest(); @@ -82,9 +83,9 @@ test "vector bit operators" { fn doTheTest() void { var v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; var x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; - expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 })); - expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 })); - expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 })); + expect(mem.eql(u8, &@as([4]u8, v ^ x), &[4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 })); + expect(mem.eql(u8, &@as([4]u8, v | x), &[4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 })); + expect(mem.eql(u8, &@as([4]u8, v & x), &[4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 })); } }; S.doTheTest(); @@ -97,7 +98,7 @@ test "implicit cast vector to array" { var a: @Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; var result_array: [4]i32 = a; result_array = a; - expect(mem.eql(i32, result_array, [4]i32{ 1, 2, 3, 4 })); + expect(mem.eql(i32, &result_array, &[4]i32{ 1, 2, 3, 4 })); } }; S.doTheTest(); @@ -111,27 +112,30 @@ test "array to vector" { } test "vector casts of sizes not divisable by 8" { + // https://github.com/ziglang/zig/issues/3563 + if (builtin.os == .dragonfly) return error.SkipZigTest; + const S = struct { fn doTheTest() void { { var v: @Vector(4, u3) = [4]u3{ 5, 2, 3, 0 }; var x: [4]u3 = v; - expect(mem.eql(u3, x, ([4]u3)(v))); + expect(mem.eql(u3, &x, &@as([4]u3, v))); } { var v: @Vector(4, u2) = [4]u2{ 1, 2, 3, 0 }; var x: [4]u2 = v; - expect(mem.eql(u2, x, ([4]u2)(v))); + expect(mem.eql(u2, &x, &@as([4]u2, v))); } { var v: @Vector(4, u1) = [4]u1{ 1, 0, 1, 0 }; var x: [4]u1 = v; - expect(mem.eql(u1, x, ([4]u1)(v))); + expect(mem.eql(u1, &x, &@as([4]u1, v))); } { var v: @Vector(4, bool) = [4]bool{ false, false, true, false }; var x: [4]bool = v; - expect(mem.eql(bool, x, ([4]bool)(v))); + expect(mem.eql(bool, &x, &@as([4]bool, v))); } } }; @@ -144,7 +148,7 @@ test "vector @splat" { fn doTheTest() void { var v: u32 = 5; var x = @splat(4, v); - expect(@typeOf(x) == @Vector(4, u32)); + expect(@TypeOf(x) == @Vector(4, u32)); var array_x: [4]u32 = x; expect(array_x[0] == 5); expect(array_x[1] == 5); @@ -155,3 +159,94 @@ test "vector @splat" { S.doTheTest(); comptime S.doTheTest(); } + +test "load vector elements via comptime index" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; + expect(v[0] == 1); + expect(v[1] == 2); + expect(loadv(&v[2]) == 3); + } + fn loadv(ptr: var) i32 { + return ptr.*; + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "store vector elements via comptime index" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + + v[2] = 42; + expect(v[1] == 5); + v[3] = -364; + expect(v[2] == 42); + expect(-364 == v[3]); + + storev(&v[0], 100); + expect(v[0] == 100); + } + fn storev(ptr: var, x: i32) void { + ptr.* = x; + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "load vector elements via runtime index" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; + var i: u32 = 0; + expect(v[i] == 1); + i += 1; + expect(v[i] == 2); + i += 1; + expect(v[i] == 3); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "store vector elements via runtime index" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + var i: u32 = 2; + v[i] = 1; + expect(v[1] == 5); + expect(v[2] == 1); + i += 1; + v[i] = -364; + expect(-364 == v[3]); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "initialize vector which is a struct field" { + const Vec4Obj = struct { + data: @Vector(4, f32), + }; + + const S = struct { + fn doTheTest() void { + var foo = Vec4Obj{ + .data = [_]f32{ 1, 2, 3, 4 }, + }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/void.zig b/test/stage1/behavior/void.zig index 19e879d15..80df9fe4f 100644 --- a/test/stage1/behavior/void.zig +++ b/test/stage1/behavior/void.zig @@ -26,7 +26,7 @@ test "iterate over a void slice" { } fn times(n: usize) []const void { - return ([*]void)(undefined)[0..n]; + return @as([*]void, undefined)[0..n]; } test "void optional" { diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig index 58ff713c2..0d0ef3a69 100644 --- a/test/stage1/behavior/while.zig +++ b/test/stage1/behavior/while.zig @@ -137,7 +137,7 @@ test "while on optional with else result follow else prong" { const result = while (returnNull()) |value| { break value; } else - i32(2); + @as(i32, 2); expect(result == 2); } @@ -145,7 +145,7 @@ test "while on optional with else result follow break prong" { const result = while (returnOptional(10)) |value| { break value; } else - i32(2); + @as(i32, 2); expect(result == 10); } @@ -153,7 +153,7 @@ test "while on error union with else result follow else prong" { const result = while (returnError()) |value| { break value; } else |err| - i32(2); + @as(i32, 2); expect(result == 2); } @@ -161,23 +161,23 @@ test "while on error union with else result follow break prong" { const result = while (returnSuccess(10)) |value| { break value; } else |err| - i32(2); + @as(i32, 2); expect(result == 10); } test "while on bool with else result follow else prong" { const result = while (returnFalse()) { - break i32(10); + break @as(i32, 10); } else - i32(2); + @as(i32, 2); expect(result == 2); } test "while on bool with else result follow break prong" { const result = while (returnTrue()) { - break i32(10); + break @as(i32, 10); } else - i32(2); + @as(i32, 2); expect(result == 10); } diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig index c2a270ec3..cf21d403f 100644 --- a/test/stage1/c_abi/build.zig +++ b/test/stage1/c_abi/build.zig @@ -4,7 +4,7 @@ pub fn build(b: *Builder) void { const rel_opts = b.standardReleaseOptions(); const c_obj = b.addObject("cfuncs", null); - c_obj.addCSourceFile("cfuncs.c", [_][]const u8{"-std=c99"}); + c_obj.addCSourceFile("cfuncs.c", &[_][]const u8{"-std=c99"}); c_obj.setBuildMode(rel_opts); c_obj.linkSystemLibrary("c"); diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 70688aa09..3c2b73152 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -119,12 +119,12 @@ export fn zig_bool(x: bool) void { extern fn c_array([10]u8) void; test "C ABI array" { - var array: [10]u8 = "1234567890"; + var array: [10]u8 = "1234567890".*; c_array(array); } export fn zig_array(x: [10]u8) void { - expect(std.mem.eql(u8, x, "1234567890")); + expect(std.mem.eql(u8, &x, "1234567890")); } const BigStruct = extern struct { diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index fdc3d4914..443ed7a0e 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -5,8 +5,8 @@ pub fn addCases(ctx: *TestContext) !void { // hello world try ctx.testCompareOutputLibC( \\extern fn puts([*]const u8) void; - \\export fn main() c_int { - \\ puts(c"Hello, world!"); + \\pub export fn main() c_int { + \\ puts("Hello, world!"); \\ return 0; \\} , "Hello, world!" ++ std.cstr.line_sep); @@ -14,8 +14,8 @@ pub fn addCases(ctx: *TestContext) !void { // function calling another function try ctx.testCompareOutputLibC( \\extern fn puts(s: [*]const u8) void; - \\export fn main() c_int { - \\ return foo(c"OK"); + \\pub export fn main() c_int { + \\ return foo("OK"); \\} \\fn foo(s: [*]const u8) c_int { \\ puts(s); diff --git a/test/stage2/test.zig b/test/stage2/test.zig new file mode 100644 index 000000000..f4768cd39 --- /dev/null +++ b/test/stage2/test.zig @@ -0,0 +1,6 @@ +const TestContext = @import("../../src-self-hosted/test.zig").TestContext; + +pub fn addCases(ctx: *TestContext) !void { + try @import("compile_errors.zig").addCases(ctx); + try @import("compare_output.zig").addCases(ctx); +} diff --git a/test/standalone.zig b/test/standalone.zig index f3a1f735d..2c5b9c790 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -1,6 +1,5 @@ +const std = @import("std"); const tests = @import("tests.zig"); -const builtin = @import("builtin"); -const is_windows = builtin.os == builtin.Os.windows; pub fn addCases(cases: *tests.StandaloneContext) void { cases.add("test/standalone/hello_world/hello.zig"); @@ -19,13 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); cases.addBuildFile("test/standalone/empty_env/build.zig"); - if (builtin.os == builtin.Os.linux) { - // TODO hook up the DynLib API for windows using LoadLibraryA - // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it + if (std.Target.current.getOs() != .wasi) { cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); } - - if (builtin.arch == builtin.Arch.x86_64) { // TODO add C ABI support for other architectures + if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures cases.addBuildFile("test/stage1/c_abi/build.zig"); } } diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index c1b8106e0..cbb328541 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -179,10 +179,10 @@ fn expandNode(node: Node, output: *ArrayList(Buffer)) ExpandNodeError!void { } pub fn main() !void { - var stdin_file = try io.getStdIn(); - var stdout_file = try io.getStdOut(); + const stdin_file = io.getStdIn(); + const stdout_file = io.getStdOut(); - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); global_allocator = &arena.allocator; diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig index c57c1e4bc..f0cd9728a 100644 --- a/test/standalone/cat/main.zig +++ b/test/standalone/cat/main.zig @@ -1,7 +1,7 @@ const std = @import("std"); const io = std.io; const process = std.process; -const File = std.fs.File; +const fs = std.fs; const mem = std.mem; const warn = std.debug.warn; const allocator = std.debug.global_allocator; @@ -10,44 +10,44 @@ pub fn main() !void { var args_it = process.args(); const exe = try unwrapArg(args_it.next(allocator).?); var catted_anything = false; - var stdout_file = try io.getStdOut(); + const stdout_file = io.getStdOut(); + + const cwd = fs.cwd(); while (args_it.next(allocator)) |arg_or_err| { const arg = try unwrapArg(arg_or_err); if (mem.eql(u8, arg, "-")) { catted_anything = true; - var stdin_file = try io.getStdIn(); - try cat_file(&stdout_file, &stdin_file); + try cat_file(stdout_file, io.getStdIn()); } else if (arg[0] == '-') { return usage(exe); } else { - var file = File.openRead(arg) catch |err| { - warn("Unable to open file: {}\n", @errorName(err)); + const file = cwd.openFile(arg, .{}) catch |err| { + warn("Unable to open file: {}\n", .{@errorName(err)}); return err; }; defer file.close(); catted_anything = true; - try cat_file(&stdout_file, &file); + try cat_file(stdout_file, file); } } if (!catted_anything) { - var stdin_file = try io.getStdIn(); - try cat_file(&stdout_file, &stdin_file); + try cat_file(stdout_file, io.getStdIn()); } } fn usage(exe: []const u8) !void { - warn("Usage: {} [FILE]...\n", exe); + warn("Usage: {} [FILE]...\n", .{exe}); return error.Invalid; } -fn cat_file(stdout: *File, file: *File) !void { +fn cat_file(stdout: fs.File, file: fs.File) !void { var buf: [1024 * 4]u8 = undefined; while (true) { const bytes_read = file.read(buf[0..]) catch |err| { - warn("Unable to read from stream: {}\n", @errorName(err)); + warn("Unable to read from stream: {}\n", .{@errorName(err)}); return err; }; @@ -56,7 +56,7 @@ fn cat_file(stdout: *File, file: *File) !void { } stdout.write(buf[0..bytes_read]) catch |err| { - warn("Unable to write to stdout: {}\n", @errorName(err)); + warn("Unable to write to stdout: {}\n", .{@errorName(err)}); return err; }; } @@ -64,7 +64,7 @@ fn cat_file(stdout: *File, file: *File) !void { fn unwrapArg(arg: anyerror![]u8) ![]u8 { return arg catch |err| { - warn("Unable to parse command line: {}\n", err); + warn("Unable to parse command line: {}\n", .{err}); return err; }; } diff --git a/test/standalone/guess_number/main.zig b/test/standalone/guess_number/main.zig index b1e557fd2..ce7d58ac1 100644 --- a/test/standalone/guess_number/main.zig +++ b/test/standalone/guess_number/main.zig @@ -4,14 +4,13 @@ const io = std.io; const fmt = std.fmt; pub fn main() !void { - var stdout_file = try io.getStdOut(); - const stdout = &stdout_file.outStream().stream; + const stdout = &io.getStdOut().outStream().stream; - try stdout.print("Welcome to the Guess Number Game in Zig.\n"); + try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{}); var seed_bytes: [@sizeOf(u64)]u8 = undefined; std.crypto.randomBytes(seed_bytes[0..]) catch |err| { - std.debug.warn("unable to seed random number generator: {}", err); + std.debug.warn("unable to seed random number generator: {}", .{err}); return err; }; const seed = std.mem.readIntNative(u64, &seed_bytes); @@ -20,27 +19,27 @@ pub fn main() !void { const answer = prng.random.range(u8, 0, 100) + 1; while (true) { - try stdout.print("\nGuess a number between 1 and 100: "); + try stdout.print("\nGuess a number between 1 and 100: ", .{}); var line_buf: [20]u8 = undefined; const line = io.readLineSlice(line_buf[0..]) catch |err| switch (err) { error.OutOfMemory => { - try stdout.print("Input too long.\n"); + try stdout.print("Input too long.\n", .{}); continue; }, else => return err, }; const guess = fmt.parseUnsigned(u8, line, 10) catch { - try stdout.print("Invalid number.\n"); + try stdout.print("Invalid number.\n", .{}); continue; }; if (guess > answer) { - try stdout.print("Guess lower.\n"); + try stdout.print("Guess lower.\n", .{}); } else if (guess < answer) { - try stdout.print("Guess higher.\n"); + try stdout.print("Guess higher.\n", .{}); } else { - try stdout.print("You win!\n"); + try stdout.print("You win!\n", .{}); return; } } diff --git a/test/standalone/hello_world/hello.zig b/test/standalone/hello_world/hello.zig index cb7d5ef15..e3fc5c0e3 100644 --- a/test/standalone/hello_world/hello.zig +++ b/test/standalone/hello_world/hello.zig @@ -1,8 +1,7 @@ const std = @import("std"); pub fn main() !void { - // If this program is run without stdout attached, exit with an error. - const stdout_file = try std.io.getStdOut(); + const stdout_file = std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); diff --git a/test/standalone/hello_world/hello_libc.zig b/test/standalone/hello_world/hello_libc.zig index 05356bf52..5c0a233f1 100644 --- a/test/standalone/hello_world/hello_libc.zig +++ b/test/standalone/hello_world/hello_libc.zig @@ -5,9 +5,9 @@ const c = @cImport({ @cInclude("string.h"); }); -const msg = c"Hello, world!\n"; +const msg = "Hello, world!\n"; -export fn main(argc: c_int, argv: **u8) c_int { +pub export fn main(argc: c_int, argv: **u8) c_int { if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1; return 0; } diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig index a222f5b31..197e3ca47 100644 --- a/test/standalone/load_dynamic_library/main.zig +++ b/test/standalone/load_dynamic_library/main.zig @@ -9,8 +9,7 @@ pub fn main() !void { var lib = try std.DynLib.open(dynlib_name); defer lib.close(); - const addr = lib.lookup("add") orelse return error.SymbolNotFound; - const addFn = @intToPtr(extern fn (i32, i32) i32, addr); + const addFn = lib.lookup(fn (i32, i32) callconv(.C) i32, "add") orelse return error.SymbolNotFound; const result = addFn(12, 34); std.debug.assert(result == 46); diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig index 7c72cfcc3..d498e2e20 100644 --- a/test/standalone/mix_o_files/build.zig +++ b/test/standalone/mix_o_files/build.zig @@ -4,7 +4,7 @@ pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addExecutable("test", null); - exe.addCSourceFile("test.c", [_][]const u8{"-std=c99"}); + exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); exe.addObject(obj); exe.linkSystemLibrary("c"); diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig index 129c5dc1c..cb7437bca 100644 --- a/test/standalone/shared_library/build.zig +++ b/test/standalone/shared_library/build.zig @@ -4,7 +4,7 @@ pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addExecutable("test", null); - exe.addCSourceFile("test.c", [_][]const u8{"-std=c99"}); + exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); exe.linkLibrary(lib); exe.linkSystemLibrary("c"); diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig index 4af00705a..2b604f5c0 100644 --- a/test/standalone/static_c_lib/build.zig +++ b/test/standalone/static_c_lib/build.zig @@ -4,7 +4,7 @@ pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const foo = b.addStaticLibrary("foo", null); - foo.addCSourceFile("foo.c", [_][]const u8{}); + foo.addCSourceFile("foo.c", &[_][]const u8{}); foo.setBuildMode(mode); foo.addIncludeDir("."); diff --git a/test/standalone/static_c_lib/foo.c b/test/standalone/static_c_lib/foo.c index 2366282d4..78a332a6e 100644 --- a/test/standalone/static_c_lib/foo.c +++ b/test/standalone/static_c_lib/foo.c @@ -2,3 +2,5 @@ uint32_t add(uint32_t a, uint32_t b) { return a + b; } + +uint32_t foo = 12345; diff --git a/test/standalone/static_c_lib/foo.h b/test/standalone/static_c_lib/foo.h index e8ebb9842..6700499fe 100644 --- a/test/standalone/static_c_lib/foo.h +++ b/test/standalone/static_c_lib/foo.h @@ -1,2 +1,3 @@ #include uint32_t add(uint32_t a, uint32_t b); +extern uint32_t foo; diff --git a/test/standalone/static_c_lib/foo.zig b/test/standalone/static_c_lib/foo.zig index edf94539c..a5ba90c95 100644 --- a/test/standalone/static_c_lib/foo.zig +++ b/test/standalone/static_c_lib/foo.zig @@ -6,3 +6,7 @@ test "C add" { const result = c.add(1, 2); expect(result == 3); } + +test "C extern variable" { + expect(c.foo == 12345); +} diff --git a/test/tests.zig b/test/tests.zig index 3c6baeb36..7c97e46e0 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -14,6 +14,7 @@ const builtin = @import("builtin"); const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; +// Cases const compare_output = @import("compare_output.zig"); const standalone = @import("standalone.zig"); const stack_traces = @import("stack_traces.zig"); @@ -21,8 +22,14 @@ const compile_errors = @import("compile_errors.zig"); const assemble_and_link = @import("assemble_and_link.zig"); const runtime_safety = @import("runtime_safety.zig"); const translate_c = @import("translate_c.zig"); +const run_translated_c = @import("run_translated_c.zig"); const gen_h = @import("gen_h.zig"); +// Implementations +pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; +pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; +pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; + const TestTarget = struct { target: Target = .Native, mode: builtin.Mode = .Debug, @@ -70,6 +77,26 @@ const test_targets = [_]TestTarget{ .link_libc = true, }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .i386, + .abi = .none, + }, + }, + }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .i386, + .abi = .musl, + }, + }, + .link_libc = true, + }, + TestTarget{ .target = Target{ .Cross = CrossTarget{ @@ -163,6 +190,16 @@ const test_targets = [_]TestTarget{ .disable_native = true, }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .windows, + .arch = .i386, + .abi = .msvc, + }, + }, + }, + TestTarget{ .target = Target{ .Cross = CrossTarget{ @@ -173,6 +210,17 @@ const test_targets = [_]TestTarget{ }, }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .windows, + .arch = .i386, + .abi = .gnu, + }, + }, + .link_libc = true, + }, + TestTarget{ .target = Target{ .Cross = CrossTarget{ @@ -304,7 +352,7 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M const exe = b.addExecutable("test-cli", "test/cli.zig"); const run_cmd = exe.run(); - run_cmd.addArgs([_][]const u8{ + run_cmd.addArgs(&[_][]const u8{ fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, b.pathFromRoot(b.cache_root), }); @@ -342,6 +390,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St return cases.step; } +pub fn addRunTranslatedCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { + const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; + cases.* = .{ + .b = b, + .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), + .test_index = 0, + .test_filter = test_filter, + }; + + run_translated_c.addCases(cases); + + return cases.step; +} + pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(GenHContext) catch unreachable; cases.* = GenHContext{ @@ -370,7 +432,7 @@ pub fn addPkgTests( is_qemu_enabled: bool, glibc_dir: ?[]const u8, ) *build.Step { - const step = b.step(b.fmt("test-{}", name), desc); + const step = b.step(b.fmt("test-{}", .{name}), desc); for (test_targets) |test_target| { if (skip_non_native and test_target.target != .Native) @@ -390,7 +452,7 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and test_target.target.getOs() == builtin.os and - ArchTag(test_target.target.getArch()) == ArchTag(builtin.arch)) + @as(ArchTag, test_target.target.getArch()) == @as(ArchTag, builtin.arch)) { continue; } @@ -408,19 +470,19 @@ pub fn addPkgTests( "bare"; const triple_prefix = if (test_target.target == .Native) - ([]const u8)("native") + @as([]const u8, "native") else test_target.target.zigTripleNoSubArch(b.allocator) catch unreachable; const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt( - "{}-{}-{}-{}-{} ", + const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; + these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", .{ name, triple_prefix, @tagName(test_target.mode), libc_prefix, - if (test_target.single_threaded) "single" else "multi", - )); + single_threaded_txt, + })); these_tests.single_threaded = test_target.single_threaded; these_tests.setFilter(test_filter); these_tests.setBuildMode(test_target.mode); @@ -438,344 +500,6 @@ pub fn addPkgTests( return step; } -pub const CompareOutputContext = struct { - b: *build.Builder, - step: *build.Step, - test_index: usize, - test_filter: ?[]const u8, - modes: []const Mode, - - const Special = enum { - None, - Asm, - RuntimeSafety, - }; - - const TestCase = struct { - name: []const u8, - sources: ArrayList(SourceFile), - expected_output: []const u8, - link_libc: bool, - special: Special, - cli_args: []const []const u8, - - const SourceFile = struct { - filename: []const u8, - source: []const u8, - }; - - pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { - self.sources.append(SourceFile{ - .filename = filename, - .source = source, - }) catch unreachable; - } - - pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { - self.cli_args = args; - } - }; - - const RunCompareOutputStep = struct { - step: build.Step, - context: *CompareOutputContext, - exe: *LibExeObjStep, - name: []const u8, - expected_output: []const u8, - test_index: usize, - cli_args: []const []const u8, - - pub fn create( - context: *CompareOutputContext, - exe: *LibExeObjStep, - name: []const u8, - expected_output: []const u8, - cli_args: []const []const u8, - ) *RunCompareOutputStep { - const allocator = context.b.allocator; - const ptr = allocator.create(RunCompareOutputStep) catch unreachable; - ptr.* = RunCompareOutputStep{ - .context = context, - .exe = exe, - .name = name, - .expected_output = expected_output, - .test_index = context.test_index, - .step = build.Step.init("RunCompareOutput", allocator, make), - .cli_args = cli_args, - }; - ptr.step.dependOn(&exe.step); - context.test_index += 1; - return ptr; - } - - fn make(step: *build.Step) !void { - const self = @fieldParentPtr(RunCompareOutputStep, "step", step); - const b = self.context.b; - - const full_exe_path = self.exe.getOutputPath(); - var args = ArrayList([]const u8).init(b.allocator); - defer args.deinit(); - - args.append(full_exe_path) catch unreachable; - for (self.cli_args) |arg| { - args.append(arg) catch unreachable; - } - - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); - - const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable; - defer child.deinit(); - - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; - child.env_map = b.env_map; - - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); - - var stdout = Buffer.initNull(b.allocator); - var stderr = Buffer.initNull(b.allocator); - - var stdout_file_in_stream = child.stdout.?.inStream(); - var stderr_file_in_stream = child.stderr.?.inStream(); - - stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; - stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; - - const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - warn("Process {} exited with error code {}\n", full_exe_path, code); - printInvocation(args.toSliceConst()); - return error.TestFailed; - } - }, - else => { - warn("Process {} terminated unexpectedly\n", full_exe_path); - printInvocation(args.toSliceConst()); - return error.TestFailed; - }, - } - - if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) { - warn( - \\ - \\========= Expected this output: ========= - \\{} - \\========= But found: ==================== - \\{} - \\ - , self.expected_output, stdout.toSliceConst()); - return error.TestFailed; - } - warn("OK\n"); - } - }; - - const RuntimeSafetyRunStep = struct { - step: build.Step, - context: *CompareOutputContext, - exe: *LibExeObjStep, - name: []const u8, - test_index: usize, - - pub fn create(context: *CompareOutputContext, exe: *LibExeObjStep, name: []const u8) *RuntimeSafetyRunStep { - const allocator = context.b.allocator; - const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; - ptr.* = RuntimeSafetyRunStep{ - .context = context, - .exe = exe, - .name = name, - .test_index = context.test_index, - .step = build.Step.init("RuntimeSafetyRun", allocator, make), - }; - ptr.step.dependOn(&exe.step); - context.test_index += 1; - return ptr; - } - - fn make(step: *build.Step) !void { - const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step); - const b = self.context.b; - - const full_exe_path = self.exe.getOutputPath(); - - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); - - const child = std.ChildProcess.init([_][]const u8{full_exe_path}, b.allocator) catch unreachable; - defer child.deinit(); - - child.env_map = b.env_map; - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Ignore; - - const term = child.spawnAndWait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); - }; - - const expected_exit_code: u32 = 126; - switch (term) { - .Exited => |code| { - if (code != expected_exit_code) { - warn("\nProgram expected to exit with code {} " ++ "but exited with code {}\n", expected_exit_code, code); - return error.TestFailed; - } - }, - .Signal => |sig| { - warn("\nProgram expected to exit with code {} " ++ "but instead signaled {}\n", expected_exit_code, sig); - return error.TestFailed; - }, - else => { - warn("\nProgram expected to exit with code {}" ++ " but exited in an unexpected way\n", expected_exit_code); - return error.TestFailed; - }, - } - - warn("OK\n"); - } - }; - - pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { - var tc = TestCase{ - .name = name, - .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), - .expected_output = expected_output, - .link_libc = false, - .special = special, - .cli_args = [_][]const u8{}, - }; - const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; - tc.addSourceFile(root_src_name, source); - return tc; - } - - pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { - return createExtra(self, name, source, expected_output, Special.None); - } - - pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - var tc = self.create(name, source, expected_output); - tc.link_libc = true; - self.addCase(tc); - } - - pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - const tc = self.create(name, source, expected_output); - self.addCase(tc); - } - - pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { - const tc = self.createExtra(name, source, expected_output, Special.Asm); - self.addCase(tc); - } - - pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { - const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); - self.addCase(tc); - } - - pub fn addCase(self: *CompareOutputContext, case: TestCase) void { - const b = self.b; - - const root_src = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, case.sources.items[0].filename }, - ) catch unreachable; - - switch (case.special) { - Special.Asm => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const exe = b.addExecutable("test", null); - exe.addAssemblyFile(root_src); - - for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - exe.step.dependOn(&write_src.step); - } - - const run_and_cmp_output = RunCompareOutputStep.create( - self, - exe, - annotated_case_name, - case.expected_output, - case.cli_args, - ); - - self.step.dependOn(&run_and_cmp_output.step); - }, - Special.None => { - for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; - } - - const exe = b.addExecutable("test", root_src); - exe.setBuildMode(mode); - if (case.link_libc) { - exe.linkSystemLibrary("c"); - } - - for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - exe.step.dependOn(&write_src.step); - } - - const run_and_cmp_output = RunCompareOutputStep.create( - self, - exe, - annotated_case_name, - case.expected_output, - case.cli_args, - ); - - self.step.dependOn(&run_and_cmp_output.step); - } - }, - Special.RuntimeSafety => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", case.name) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const exe = b.addExecutable("test", root_src); - if (case.link_libc) { - exe.linkSystemLibrary("c"); - } - - for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - exe.step.dependOn(&write_src.step); - } - - const run_and_cmp_output = RuntimeSafetyRunStep.create(self, exe, annotated_case_name); - - self.step.dependOn(&run_and_cmp_output.step); - }, - } - } -}; - pub const StackTracesContext = struct { b: *build.Builder, step: *build.Step, @@ -793,26 +517,24 @@ pub const StackTracesContext = struct { ) void { const b = self.b; - const source_pathname = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, "source.zig" }, - ) catch unreachable; - for (self.modes) |mode| { const expect_for_mode = expect[@enumToInt(mode)]; if (expect_for_mode.len == 0) continue; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "stack-trace", name, @tagName(mode)) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + "stack-trace", + name, + @tagName(mode), + }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; } - const exe = b.addExecutable("test", source_pathname); + const src_basename = "source.zig"; + const write_src = b.addWriteFile(src_basename, source); + const exe = b.addExecutableFromWriteFileStep("test", write_src, src_basename); exe.setBuildMode(mode); - const write_source = b.addWriteFile(source_pathname, source); - exe.step.dependOn(&write_source.step); - const run_and_compare = RunAndCompareStep.create( self, exe, @@ -866,7 +588,7 @@ pub const StackTracesContext = struct { defer args.deinit(); args.append(full_exe_path) catch unreachable; - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); + warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable; defer child.deinit(); @@ -876,7 +598,7 @@ pub const StackTracesContext = struct { child.stderr_behavior = .Pipe; child.env_map = b.env_map; - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); var stdout = Buffer.initNull(b.allocator); var stderr = Buffer.initNull(b.allocator); @@ -888,30 +610,34 @@ pub const StackTracesContext = struct { stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); }; switch (term) { .Exited => |code| { const expect_code: u32 = 1; if (code != expect_code) { - warn("Process {} exited with error code {} but expected code {}\n", full_exe_path, code, expect_code); + warn("Process {} exited with error code {} but expected code {}\n", .{ + full_exe_path, + code, + expect_code, + }); printInvocation(args.toSliceConst()); return error.TestFailed; } }, .Signal => |signum| { - warn("Process {} terminated on signal {}\n", full_exe_path, signum); + warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum }); printInvocation(args.toSliceConst()); return error.TestFailed; }, .Stopped => |signum| { - warn("Process {} stopped on signal {}\n", full_exe_path, signum); + warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum }); printInvocation(args.toSliceConst()); return error.TestFailed; }, .Unknown => |code| { - warn("Process {} terminated unexpectedly with error code {}\n", full_exe_path, code); + warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code }); printInvocation(args.toSliceConst()); return error.TestFailed; }, @@ -962,10 +688,10 @@ pub const StackTracesContext = struct { \\================================================ \\{} \\ - , self.expect_output, got); + , .{ self.expect_output, got }); return error.TestFailed; } - warn("OK\n"); + warn("OK\n", .{}); } }; }; @@ -1011,6 +737,7 @@ pub const CompileErrorContext = struct { test_index: usize, case: *const TestCase, build_mode: Mode, + write_src: *build.WriteFileStep, const ErrLineIter = struct { lines: mem.SplitIterator, @@ -1030,7 +757,13 @@ pub const CompileErrorContext = struct { } }; - pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { + pub fn create( + context: *CompileErrorContext, + name: []const u8, + case: *const TestCase, + build_mode: Mode, + write_src: *build.WriteFileStep, + ) *CompileCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; ptr.* = CompileCmpOutputStep{ @@ -1040,6 +773,7 @@ pub const CompileErrorContext = struct { .test_index = context.test_index, .case = case, .build_mode = build_mode, + .write_src = write_src, }; context.test_index += 1; @@ -1050,11 +784,6 @@ pub const CompileErrorContext = struct { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; - const root_src = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, self.case.sources.items[0].filename }, - ) catch unreachable; - var zig_args = ArrayList([]const u8).init(b.allocator); zig_args.append(b.zig_exe) catch unreachable; @@ -1065,7 +794,8 @@ pub const CompileErrorContext = struct { } else { try zig_args.append("build-obj"); } - zig_args.append(b.pathFromRoot(root_src)) catch unreachable; + const root_src_basename = self.case.sources.toSliceConst()[0].filename; + try zig_args.append(self.write_src.getOutputPath(root_src_basename)); zig_args.append("--name") catch unreachable; zig_args.append("test") catch unreachable; @@ -1088,7 +818,7 @@ pub const CompileErrorContext = struct { Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, } - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); + warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (b.verbose) { printInvocation(zig_args.toSliceConst()); @@ -1102,7 +832,7 @@ pub const CompileErrorContext = struct { child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err)); + child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); var stdout_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator); @@ -1114,7 +844,7 @@ pub const CompileErrorContext = struct { stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err)); + debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); }; switch (term) { .Exited => |code| { @@ -1124,7 +854,7 @@ pub const CompileErrorContext = struct { } }, else => { - warn("Process {} terminated unexpectedly\n", b.zig_exe); + warn("Process {} terminated unexpectedly\n", .{b.zig_exe}); printInvocation(zig_args.toSliceConst()); return error.TestFailed; }, @@ -1141,7 +871,7 @@ pub const CompileErrorContext = struct { \\{} \\================================================ \\ - , stdout); + , .{stdout}); return error.TestFailed; } @@ -1159,9 +889,9 @@ pub const CompileErrorContext = struct { ok = ok and i == self.case.expected_errors.len; if (!ok) { - warn("\n======== Expected these compile errors: ========\n"); + warn("\n======== Expected these compile errors: ========\n", .{}); for (self.case.expected_errors.toSliceConst()) |expected| { - warn("{}\n", expected); + warn("{}\n", .{expected}); } } } else { @@ -1172,7 +902,7 @@ pub const CompileErrorContext = struct { \\=========== Expected compile error: ============ \\{} \\ - , expected); + , .{expected}); ok = false; break; } @@ -1184,15 +914,20 @@ pub const CompileErrorContext = struct { \\================= Full output: ================= \\{} \\ - , stderr); + , .{stderr}); return error.TestFailed; } - warn("OK\n"); + warn("OK\n", .{}); } }; - pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { + pub fn create( + self: *CompileErrorContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -1205,31 +940,46 @@ pub const CompileErrorContext = struct { }; tc.addSourceFile("tmp.zig", source); - comptime var arg_i = 0; - inline while (arg_i < expected_lines.len) : (arg_i += 1) { + var arg_i: usize = 0; + while (arg_i < expected_lines.len) : (arg_i += 1) { tc.addExpectedError(expected_lines[arg_i]); } return tc; } - pub fn addC(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addC(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void { var tc = self.create(name, source, expected_lines); tc.link_libc = true; self.addCase(tc); } - pub fn addExe(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addExe( + self: *CompileErrorContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { var tc = self.create(name, source, expected_lines); tc.is_exe = true; self.addCase(tc); } - pub fn add(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add( + self: *CompileErrorContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { const tc = self.create(name, source, expected_lines); self.addCase(tc); } - pub fn addTest(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addTest( + self: *CompileErrorContext, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) void { const tc = self.create(name, source, expected_lines); tc.is_test = true; self.addCase(tc); @@ -1238,22 +988,20 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", case.name) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", .{ + case.name, + }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } - - const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, .Debug); - self.step.dependOn(&compile_and_cmp_errors.step); - + const write_src = b.addWriteFiles(); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - compile_and_cmp_errors.step.dependOn(&write_src.step); + write_src.add(src_file.filename, src_file.source); } + + const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, .Debug, write_src); + compile_and_cmp_errors.step.dependOn(&write_src.step); + self.step.dependOn(&compile_and_cmp_errors.step); } }; @@ -1275,7 +1023,7 @@ pub const StandaloneContext = struct { pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8) void { const b = self.b; - const annotated_case_name = b.fmt("build {} (Debug)", build_file); + const annotated_case_name = b.fmt("build {} (Debug)", .{build_file}); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -1296,7 +1044,7 @@ pub const StandaloneContext = struct { const run_cmd = b.addSystemCommand(zig_args.toSliceConst()); - const log_step = b.addLog("PASS {}\n", annotated_case_name); + const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); log_step.step.dependOn(&run_cmd.step); self.step.dependOn(&log_step.step); @@ -1306,7 +1054,10 @@ pub const StandaloneContext = struct { const b = self.b; for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", .{ + root_src, + @tagName(mode), + }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; } @@ -1317,7 +1068,7 @@ pub const StandaloneContext = struct { exe.linkSystemLibrary("c"); } - const log_step = b.addLog("PASS {}\n", annotated_case_name); + const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); log_step.step.dependOn(&exe.step); self.step.dependOn(&log_step.step); @@ -1325,245 +1076,6 @@ pub const StandaloneContext = struct { } }; -pub const TranslateCContext = struct { - b: *build.Builder, - step: *build.Step, - test_index: usize, - test_filter: ?[]const u8, - - const TestCase = struct { - name: []const u8, - sources: ArrayList(SourceFile), - expected_lines: ArrayList([]const u8), - allow_warnings: bool, - stage2: bool, - - const SourceFile = struct { - filename: []const u8, - source: []const u8, - }; - - pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { - self.sources.append(SourceFile{ - .filename = filename, - .source = source, - }) catch unreachable; - } - - pub fn addExpectedLine(self: *TestCase, text: []const u8) void { - self.expected_lines.append(text) catch unreachable; - } - }; - - const TranslateCCmpOutputStep = struct { - step: build.Step, - context: *TranslateCContext, - name: []const u8, - test_index: usize, - case: *const TestCase, - - pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { - const allocator = context.b.allocator; - const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; - ptr.* = TranslateCCmpOutputStep{ - .step = build.Step.init("ParseCCmpOutput", allocator, make), - .context = context, - .name = name, - .test_index = context.test_index, - .case = case, - }; - - context.test_index += 1; - return ptr; - } - - fn make(step: *build.Step) !void { - const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); - const b = self.context.b; - - const root_src = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, self.case.sources.items[0].filename }, - ) catch unreachable; - - var zig_args = ArrayList([]const u8).init(b.allocator); - zig_args.append(b.zig_exe) catch unreachable; - - const translate_c_cmd = if (self.case.stage2) "translate-c-2" else "translate-c"; - zig_args.append(translate_c_cmd) catch unreachable; - zig_args.append(b.pathFromRoot(root_src)) catch unreachable; - - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); - - if (b.verbose) { - printInvocation(zig_args.toSliceConst()); - } - - const child = std.ChildProcess.init(zig_args.toSliceConst(), b.allocator) catch unreachable; - defer child.deinit(); - - child.env_map = b.env_map; - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; - - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err)); - - var stdout_buf = Buffer.initNull(b.allocator); - var stderr_buf = Buffer.initNull(b.allocator); - - var stdout_file_in_stream = child.stdout.?.inStream(); - var stderr_file_in_stream = child.stderr.?.inStream(); - - stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; - stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; - - const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err)); - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - warn("Compilation failed with exit code {}\n", code); - printInvocation(zig_args.toSliceConst()); - return error.TestFailed; - } - }, - .Signal => |code| { - warn("Compilation failed with signal {}\n", code); - printInvocation(zig_args.toSliceConst()); - return error.TestFailed; - }, - else => { - warn("Compilation terminated unexpectedly\n"); - printInvocation(zig_args.toSliceConst()); - return error.TestFailed; - }, - } - - const stdout = stdout_buf.toSliceConst(); - const stderr = stderr_buf.toSliceConst(); - - if (stderr.len != 0 and !self.case.allow_warnings) { - warn( - \\====== translate-c emitted warnings: ======= - \\{} - \\============================================ - \\ - , stderr); - printInvocation(zig_args.toSliceConst()); - return error.TestFailed; - } - - for (self.case.expected_lines.toSliceConst()) |expected_line| { - if (mem.indexOf(u8, stdout, expected_line) == null) { - warn( - \\ - \\========= Expected this output: ================ - \\{} - \\========= But found: =========================== - \\{} - \\ - , expected_line, stdout); - printInvocation(zig_args.toSliceConst()); - return error.TestFailed; - } - } - warn("OK\n"); - } - }; - - fn printInvocation(args: []const []const u8) void { - for (args) |arg| { - warn("{} ", arg); - } - warn("\n"); - } - - pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase) catch unreachable; - tc.* = TestCase{ - .name = name, - .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), - .expected_lines = ArrayList([]const u8).init(self.b.allocator), - .allow_warnings = allow_warnings, - .stage2 = false, - }; - - tc.addSourceFile(filename, source); - comptime var arg_i = 0; - inline while (arg_i < expected_lines.len) : (arg_i += 1) { - tc.addExpectedLine(expected_lines[arg_i]); - } - return tc; - } - - pub fn add(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(false, "source.h", name, source, expected_lines); - self.addCase(tc); - } - - pub fn addC(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(false, "source.c", name, source, expected_lines); - self.addCase(tc); - } - - pub fn add_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - for ([_]bool{ false, true }) |stage2| { - const tc = self.create(false, "source.h", name, source, expected_lines); - tc.stage2 = stage2; - self.addCase(tc); - } - } - - pub fn addC_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - for ([_]bool{ false, true }) |stage2| { - const tc = self.create(false, "source.c", name, source, expected_lines); - tc.stage2 = stage2; - self.addCase(tc); - } - } - - pub fn add_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(false, "source.h", name, source, expected_lines); - tc.stage2 = true; - self.addCase(tc); - } - - pub fn addC_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(false, "source.c", name, source, expected_lines); - tc.stage2 = true; - self.addCase(tc); - } - - pub fn addAllowWarnings(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(true, "source.h", name, source, expected_lines); - self.addCase(tc); - } - - pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { - const b = self.b; - - const translate_c_cmd = if (case.stage2) "translate-c-2" else "translate-c"; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", translate_c_cmd, case.name) catch unreachable; - if (self.test_filter) |filter| { - if (mem.indexOf(u8, annotated_case_name, filter) == null) return; - } - - const translate_c_and_cmp = TranslateCCmpOutputStep.create(self, annotated_case_name, case); - self.step.dependOn(&translate_c_and_cmp.step); - - for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - translate_c_and_cmp.step.dependOn(&write_src.step); - } - } -}; - pub const GenHContext = struct { b: *build.Builder, step: *build.Step, @@ -1625,7 +1137,7 @@ pub const GenHContext = struct { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; - warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); + warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); const full_h_path = self.obj.getOutputHPath(); const actual_h = try io.readFileAlloc(b.allocator, full_h_path); @@ -1639,22 +1151,28 @@ pub const GenHContext = struct { \\========= But found: =========================== \\{} \\ - , expected_line, actual_h); + , .{ expected_line, actual_h }); return error.TestFailed; } } - warn("OK\n"); + warn("OK\n", .{}); } }; fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", arg); + warn("{} ", .{arg}); } - warn("\n"); + warn("\n", .{}); } - pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { + pub fn create( + self: *GenHContext, + filename: []const u8, + name: []const u8, + source: []const u8, + expected_lines: []const []const u8, + ) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -1663,43 +1181,35 @@ pub const GenHContext = struct { }; tc.addSourceFile(filename, source); - comptime var arg_i = 0; - inline while (arg_i < expected_lines.len) : (arg_i += 1) { + var arg_i: usize = 0; + while (arg_i < expected_lines.len) : (arg_i += 1) { tc.addExpectedLine(expected_lines[arg_i]); } return tc; } - pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: []const []const u8) void { const tc = self.create("test.zig", name, source, expected_lines); self.addCase(tc); } pub fn addCase(self: *GenHContext, case: *const TestCase) void { const b = self.b; - const root_src = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, case.sources.items[0].filename }, - ) catch unreachable; const mode = builtin.Mode.Debug; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } - const obj = b.addObject("test", root_src); - obj.setBuildMode(mode); - + const write_src = b.addWriteFiles(); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = fs.path.join( - b.allocator, - [_][]const u8{ b.cache_root, src_file.filename }, - ) catch unreachable; - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - obj.step.dependOn(&write_src.step); + write_src.add(src_file.filename, src_file.source); } + const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); + obj.setBuildMode(mode); + const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); self.step.dependOn(&cmp_h.step); @@ -1708,7 +1218,7 @@ pub const GenHContext = struct { fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", arg); + warn("{} ", .{arg}); } - warn("\n"); + warn("\n", .{}); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 672075e3b..16caa6e32 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,40 +1,196 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); -// add_both - test for stage1 and stage2, in #include mode -// add - test stage1 only, in #include mode -// add_2 - test stage2 only, in #include mode -// addC_both - test for stage1 and stage2, in -c mode -// addC - test stage1 only, in -c mode -// addC_2 - test stage2 only, in -c mode - pub fn addCases(cases: *tests.TranslateCContext) void { - /////////////// Cases that pass for both stage1/stage2 //////////////// - cases.add_both("simple function prototypes", + cases.add("empty declaration", + \\; + , &[_][]const u8{""}); + + cases.add("#define hex literal with capital X", + \\#define VAL 0XF00D + , &[_][]const u8{ + \\pub const VAL = 0xF00D; + }); + + cases.add("anonymous struct & unions", + \\typedef struct { + \\ union { + \\ char x; + \\ struct { int y; }; + \\ }; + \\} outer; + \\void foo(outer *x) { x->y = x->x; } + , &[_][]const u8{ + \\const struct_unnamed_5 = extern struct { + \\ y: c_int, + \\}; + \\const union_unnamed_3 = extern union { + \\ x: u8, + \\ unnamed_4: struct_unnamed_5, + \\}; + \\const struct_unnamed_1 = extern struct { + \\ unnamed_2: union_unnamed_3, + \\}; + \\pub const outer = struct_unnamed_1; + \\pub export fn foo(arg_x: [*c]outer) void { + \\ var x = arg_x; + \\ x.*.unnamed_2.unnamed_4.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_2.x)); + \\} + }); + + cases.add("union initializer", + \\union { int x; char c[4]; } + \\ ua = {1}, + \\ ub = {.c={'a','b','b','a'}}; + , &[_][]const u8{ + \\const union_unnamed_1 = extern union { + \\ x: c_int, + \\ c: [4]u8, + \\}; + \\pub export var ua: union_unnamed_1 = union_unnamed_1{ + \\ .x = @as(c_int, 1), + \\}; + \\pub export var ub: union_unnamed_1 = union_unnamed_1{ + \\ .c = .{ + \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), + \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), + \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), + \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), + \\ }, + \\}; + }); + + cases.add("struct initializer - simple", + \\typedef struct { int x; } foo; + \\struct {double x,y,z;} s0 = {1.2, 1.3}; + \\struct {int sec,min,hour,day,mon,year;} s1 = {.day=31,12,2014,.sec=30,15,17}; + \\struct {int x,y;} s2 = {.y = 2, .x=1}; + \\foo s3 = { 123 }; + , &[_][]const u8{ + \\const struct_unnamed_1 = extern struct { + \\ x: c_int, + \\}; + \\pub const foo = struct_unnamed_1; + \\const struct_unnamed_2 = extern struct { + \\ x: f64, + \\ y: f64, + \\ z: f64, + \\}; + \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{ + \\ .x = 1.2, + \\ .y = 1.3, + \\ .z = 0, + \\}; + \\const struct_unnamed_3 = extern struct { + \\ sec: c_int, + \\ min: c_int, + \\ hour: c_int, + \\ day: c_int, + \\ mon: c_int, + \\ year: c_int, + \\}; + \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{ + \\ .sec = @as(c_int, 30), + \\ .min = @as(c_int, 15), + \\ .hour = @as(c_int, 17), + \\ .day = @as(c_int, 31), + \\ .mon = @as(c_int, 12), + \\ .year = @as(c_int, 2014), + \\}; + \\const struct_unnamed_4 = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{ + \\ .x = @as(c_int, 1), + \\ .y = @as(c_int, 2), + \\}; + \\pub export var s3: foo = foo{ + \\ .x = @as(c_int, 123), + \\}; + }); + + cases.add("simple ptrCast for casts between opaque types", + \\struct opaque; + \\struct opaque_2; + \\void function(struct opaque *opaque) { + \\ struct opaque_2 *cast = (struct opaque_2 *)opaque; + \\} + , &[_][]const u8{ + \\pub const struct_opaque = @OpaqueType(); + \\pub const struct_opaque_2 = @OpaqueType(); + \\pub export fn function(arg_opaque_1: ?*struct_opaque) void { + \\ var opaque_1 = arg_opaque_1; + \\ var cast: ?*struct_opaque_2 = @ptrCast(?*struct_opaque_2, opaque_1); + \\} + }); + + cases.add("struct initializer - packed", + \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2}; + , &[_][]const u8{ + \\const struct_unnamed_1 = packed struct { + \\ x: c_int, + \\ y: c_int, + \\ z: c_int, + \\}; + \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ + \\ .x = @as(c_int, 1), + \\ .y = @as(c_int, 2), + \\ .z = 0, + \\}; + }); + + cases.add("align() attribute", + \\__attribute__ ((aligned(128))) + \\extern char my_array[16]; + \\__attribute__ ((aligned(128))) + \\void my_fn(void) { } + , &[_][]const u8{ + \\pub extern var my_array: [16]u8 align(128); + \\pub export fn my_fn() align(128) void {} + }); + + cases.add("linksection() attribute", + \\// Use the "segment,section" format to make this test pass when + \\// targeting the mach-o binary format + \\__attribute__ ((__section__("NEAR,.data"))) + \\extern char my_array[16]; + \\__attribute__ ((__section__("NEAR,.data"))) + \\void my_fn(void) { } + , &[_][]const u8{ + \\pub extern var my_array: [16]u8 linksection("NEAR,.data"); + \\pub export fn my_fn() linksection("NEAR,.data") void {} + }); + + cases.add("simple function prototypes", \\void __attribute__((noreturn)) foo(void); \\int bar(void); - , + , &[_][]const u8{ \\pub extern fn foo() noreturn; \\pub extern fn bar() c_int; - ); + }); - cases.add_both("simple var decls", + cases.add("simple var decls", \\void foo(void) { \\ int a; \\ char b = 123; \\ const int c; \\ const unsigned d = 440; + \\ int e = 10; + \\ unsigned int f = 10u; \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ var a: c_int = undefined; - \\ var b: u8 = u8(123); + \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); \\ const c: c_int = undefined; - \\ const d: c_uint = c_uint(440); + \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440)); + \\ var e: c_int = 10; + \\ var f: c_uint = 10; \\} - ); + }); - cases.add_both("ignore result, explicit function arguments", + cases.add("ignore result, explicit function arguments", \\void foo(void) { \\ int a; \\ 1; @@ -43,54 +199,139 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ 1 - 1; \\ a = 1; \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ var a: c_int = undefined; - \\ _ = 1; - \\ _ = c"hey"; - \\ _ = (1 + 1); - \\ _ = (1 - 1); + \\ _ = @as(c_int, 1); + \\ _ = "hey"; + \\ _ = (@as(c_int, 1) + @as(c_int, 1)); + \\ _ = (@as(c_int, 1) - @as(c_int, 1)); \\ a = 1; \\} - ); + }); - /////////////// Cases that pass for only stage2 //////////////// - // TODO: restore these tests after removing "import mode" concept - // https://github.com/ziglang/zig/issues/2780 + cases.add("function with no prototype", + \\int foo() { + \\ return 5; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return 5; + \\} + }); - // cases.add_2("Parameterless function prototypes", - // \\void a() {} - // \\void b(void) {} - // \\void c(); - // \\void d(void); - // , - // \\pub export fn a() void {} - // \\pub export fn b() void {} - // \\pub extern fn c(...) void; - // \\pub extern fn d() void; - // ); - - // cases.add_2("simple function definition", - // \\void foo(void) {} - // \\static void bar(void) {} - // , - // \\pub export fn foo() void {} - // \\pub extern fn bar() void {} - // ); - - cases.add_2("parameterless function prototypes", - \\void a() {} - \\void b(void) {} - \\void c(); - \\void d(void); + cases.add("variables", + \\extern int extern_var; + \\static const int int_var = 13; + , &[_][]const u8{ + \\pub extern var extern_var: c_int; , - \\pub fn a(...) void {} - \\pub fn b() void {} - \\pub extern fn c(...) void; - \\pub extern fn d() void; - ); + \\pub const int_var: c_int = 13; + }); - /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// + cases.add("const ptr initializer", + \\static const char *v0 = "0.0.0"; + , &[_][]const u8{ + \\pub var v0: [*c]const u8 = "0.0.0"; + }); + + cases.add("static incomplete array inside function", + \\void foo(void) { + \\ static const char v2[] = "2.2.2"; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ const v2: [*c]const u8 = "2.2.2"; + \\} + }); + + cases.add("simple function definition", + \\void foo(void) {} + \\static void bar(void) {} + , &[_][]const u8{ + \\pub export fn foo() void {} + \\pub fn bar() callconv(.C) void {} + }); + + cases.add("typedef void", + \\typedef void Foo; + \\Foo fun(Foo *a); + , &[_][]const u8{ + \\pub const Foo = c_void; + , + \\pub extern fn fun(a: ?*Foo) Foo; + }); + + cases.add("duplicate typedef", + \\typedef long foo; + \\typedef int bar; + \\typedef long foo; + \\typedef int baz; + , &[_][]const u8{ + \\pub const foo = c_long; + \\pub const bar = c_int; + \\pub const baz = c_int; + }); + + cases.add("casting pointers to ints and ints to pointers", + \\void foo(void); + \\void bar(void) { + \\ void *func_ptr = foo; + \\ void (*typed_func_ptr)(void) = (void (*)(void)) (unsigned long) func_ptr; + \\} + , &[_][]const u8{ + \\pub extern fn foo() void; + \\pub export fn bar() void { + \\ var func_ptr: ?*c_void = @ptrCast(?*c_void, foo); + \\ var typed_func_ptr: ?fn () callconv(.C) void = @intToPtr(?fn () callconv(.C) void, @intCast(c_ulong, @ptrToInt(func_ptr))); + \\} + }); + + cases.add("noreturn attribute", + \\void foo(void) __attribute__((noreturn)); + , &[_][]const u8{ + \\pub extern fn foo() noreturn; + }); + + cases.add("add, sub, mul, div, rem", + \\int s() { + \\ int a, b, c; + \\ c = a + b; + \\ c = a - b; + \\ c = a * b; + \\ c = a / b; + \\ c = a % b; + \\} + \\unsigned u() { + \\ unsigned a, b, c; + \\ c = a + b; + \\ c = a - b; + \\ c = a * b; + \\ c = a / b; + \\ c = a % b; + \\} + , &[_][]const u8{ + \\pub export fn s() c_int { + \\ var a: c_int = undefined; + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = (a + b); + \\ c = (a - b); + \\ c = (a * b); + \\ c = @divTrunc(a, b); + \\ c = @rem(a, b); + \\} + \\pub export fn u() c_uint { + \\ var a: c_uint = undefined; + \\ var b: c_uint = undefined; + \\ var c: c_uint = undefined; + \\ c = (a +% b); + \\ c = (a -% b); + \\ c = (a *% b); + \\ c = (a / b); + \\ c = (a % b); + \\} + }); cases.add("typedef of function in struct field", \\typedef void lws_callback_function(void); @@ -98,13 +339,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ void (*func)(void); \\ lws_callback_function *callback_http; \\}; - , - \\pub const lws_callback_function = extern fn () void; + , &[_][]const u8{ + \\pub const lws_callback_function = fn () callconv(.C) void; \\pub const struct_Foo = extern struct { - \\ func: ?extern fn () void, + \\ func: ?fn () callconv(.C) void, \\ callback_http: ?lws_callback_function, \\}; - ); + }); cases.add("pointer to struct demoted to opaque due to bit fields", \\struct Foo { @@ -113,41 +354,228 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\struct Bar { \\ struct Foo *foo; \\}; - , + , &[_][]const u8{ \\pub const struct_Foo = @OpaqueType(); + , \\pub const struct_Bar = extern struct { \\ foo: ?*struct_Foo, \\}; - ); - - cases.add_both("simple function definition", - \\void foo(void) {} - \\static void bar(void) {} - , - \\pub fn foo() void {} - \\pub fn bar() void {} - ); + }); cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) - , + , &[_][]const u8{ \\pub const REDISMODULE_READ = 1 << 0; + }); + + cases.add("macro with right shift", + \\#define FLASH_SIZE 0x200000UL /* 2 MB */ + \\#define FLASH_BANK_SIZE (FLASH_SIZE >> 1) /* 1 MB */ + , &[_][]const u8{ + \\pub const FLASH_SIZE = @as(c_ulong, 0x200000); + , + \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> 1; + }); + + cases.add("double define struct", + \\typedef struct Bar Bar; + \\typedef struct Foo Foo; + \\ + \\struct Foo { + \\ Foo *a; + \\}; + \\ + \\struct Bar { + \\ Foo *a; + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ a: [*c]Foo, + \\}; + , + \\pub const Foo = struct_Foo; + , + \\pub const struct_Bar = extern struct { + \\ a: [*c]Foo, + \\}; + , + \\pub const Bar = struct_Bar; + }); + + cases.add("simple struct", + \\struct Foo { + \\ int x; + \\ char *y; + \\}; + , &[_][]const u8{ + \\const struct_Foo = extern struct { + \\ x: c_int, + \\ y: [*c]u8, + \\}; + , + \\pub const Foo = struct_Foo; + }); + + cases.add("self referential struct with function pointer", + \\struct Foo { + \\ void (*derp)(struct Foo *foo); + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ derp: ?fn ([*c]struct_Foo) callconv(.C) void, + \\}; + , + \\pub const Foo = struct_Foo; + }); + + cases.add("struct prototype used in func", + \\struct Foo; + \\struct Foo *some_func(struct Foo *foo, int x); + , &[_][]const u8{ + \\pub const struct_Foo = @OpaqueType(); + , + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; + , + \\pub const Foo = struct_Foo; + }); + + cases.add("#define an unsigned integer literal", + \\#define CHANNEL_COUNT 24 + , &[_][]const u8{ + \\pub const CHANNEL_COUNT = 24; + }); + + cases.add("#define referencing another #define", + \\#define THING2 THING1 + \\#define THING1 1234 + , &[_][]const u8{ + \\pub const THING1 = 1234; + , + \\pub const THING2 = THING1; + }); + + cases.add("circular struct definitions", + \\struct Bar; + \\ + \\struct Foo { + \\ struct Bar *next; + \\}; + \\ + \\struct Bar { + \\ struct Foo *next; + \\}; + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ next: [*c]struct_Foo, + \\}; + , + \\pub const struct_Foo = extern struct { + \\ next: [*c]struct_Bar, + \\}; + }); + + cases.add("#define string", + \\#define foo "a string" + , &[_][]const u8{ + \\pub const foo = "a string"; + }); + + cases.add("zig keywords in C code", + \\struct comptime { + \\ int defer; + \\}; + , &[_][]const u8{ + \\pub const struct_comptime = extern struct { + \\ @"defer": c_int, + \\}; + , + \\pub const @"comptime" = struct_comptime; + }); + + cases.add("macro with parens around negative number", + \\#define LUA_GLOBALSINDEX (-10002) + , &[_][]const u8{ + \\pub const LUA_GLOBALSINDEX = -10002; + }); + + cases.add( + "u integer suffix after 0 (zero) in macro definition", + "#define ZERO 0U", + &[_][]const u8{ + "pub const ZERO = @as(c_uint, 0);", + }, ); - cases.add_both("casting pointers to ints and ints to pointers", - \\void foo(void); - \\void bar(void) { - \\ void *func_ptr = foo; - \\ void (*typed_func_ptr)(void) = (void (*)(void)) (unsigned long) func_ptr; - \\} - , - \\pub extern fn foo() void; - \\pub fn bar() void { - \\ var func_ptr: ?*c_void = @ptrCast(?*c_void, foo); - \\ var typed_func_ptr: ?extern fn () void = @intToPtr(?extern fn () void, c_ulong(@ptrToInt(func_ptr))); - \\} + cases.add( + "l integer suffix after 0 (zero) in macro definition", + "#define ZERO 0L", + &[_][]const u8{ + "pub const ZERO = @as(c_long, 0);", + }, ); + cases.add( + "ul integer suffix after 0 (zero) in macro definition", + "#define ZERO 0UL", + &[_][]const u8{ + "pub const ZERO = @as(c_ulong, 0);", + }, + ); + + cases.add( + "lu integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LU", + &[_][]const u8{ + "pub const ZERO = @as(c_ulong, 0);", + }, + ); + + cases.add( + "ll integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LL", + &[_][]const u8{ + "pub const ZERO = @as(c_longlong, 0);", + }, + ); + + cases.add( + "ull integer suffix after 0 (zero) in macro definition", + "#define ZERO 0ULL", + &[_][]const u8{ + "pub const ZERO = @as(c_ulonglong, 0);", + }, + ); + + cases.add( + "llu integer suffix after 0 (zero) in macro definition", + "#define ZERO 0LLU", + &[_][]const u8{ + "pub const ZERO = @as(c_ulonglong, 0);", + }, + ); + + cases.add( + "bitwise not on u-suffixed 0 (zero) in macro definition", + "#define NOT_ZERO (~0U)", + &[_][]const u8{ + "pub const NOT_ZERO = ~@as(c_uint, 0);", + }, + ); + + cases.add("null statements", + \\void foo(void) { + \\ ;;;;; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ {} + \\ {} + \\ {} + \\ {} + \\ {} + \\} + }); + if (builtin.os != builtin.Os.windows) { // Windows treats this as an enum with type c_int cases.add("big negative enum init values when C ABI supports long long enums", @@ -176,7 +604,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1, \\ VAL23 = 0xFFFFFFFFFFFFFFFF, \\}; - , + , &[_][]const u8{ \\pub const enum_EnumWithInits = extern enum(c_longlong) { \\ VAL01 = 0, \\ VAL02 = 1, @@ -202,7 +630,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ VAL22 = 0, \\ VAL23 = -1, \\}; - ); + }); } cases.add("predefined expressions", @@ -211,1182 +639,131 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ __FUNCTION__; \\ __PRETTY_FUNCTION__; \\} - , - \\pub fn foo() void { - \\ _ = c"foo"; - \\ _ = c"foo"; - \\ _ = c"void foo(void)"; + , &[_][]const u8{ + \\pub export fn foo() void { + \\ _ = "foo"; + \\ _ = "foo"; + \\ _ = "void foo(void)"; \\} - ); - - cases.add("ignore result, no function arguments", - \\void foo() { - \\ int a; - \\ 1; - \\ "hey"; - \\ 1 + 1; - \\ 1 - 1; - \\ a = 1; - \\} - , - \\pub fn foo() void { - \\ var a: c_int = undefined; - \\ _ = 1; - \\ _ = c"hey"; - \\ _ = (1 + 1); - \\ _ = (1 - 1); - \\ a = 1; - \\} - ); - - cases.add("for loop with var init but empty body", - \\void foo(void) { - \\ for (int x = 0; x < 10; x++); - \\} - , - \\pub fn foo() void { - \\ { - \\ var x: c_int = 0; - \\ while (x < 10) : (x += 1) {} - \\ } - \\} - ); - - cases.add("do while with empty body", - \\void foo(void) { - \\ do ; while (1); - \\} - , // TODO this should be if (1 != 0) break - \\pub fn foo() void { - \\ while (true) { - \\ {} - \\ if (!1) break; - \\ } - \\} - ); - - cases.add("for with empty body", - \\void foo(void) { - \\ for (;;); - \\} - , - \\pub fn foo() void { - \\ while (true) {} - \\} - ); - - cases.add("while with empty body", - \\void foo(void) { - \\ while (1); - \\} - , - \\pub fn foo() void { - \\ while (1 != 0) {} - \\} - ); - - cases.add("double define struct", - \\typedef struct Bar Bar; - \\typedef struct Foo Foo; - \\ - \\struct Foo { - \\ Foo *a; - \\}; - \\ - \\struct Bar { - \\ Foo *a; - \\}; - , - \\pub const struct_Foo = extern struct { - \\ a: [*c]Foo, - \\}; - \\pub const Foo = struct_Foo; - \\pub const struct_Bar = extern struct { - \\ a: [*c]Foo, - \\}; - ); - - cases.addAllowWarnings("simple data types", - \\#include - \\int foo(char a, unsigned char b, signed char c); - \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype - \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d); - \\void baz(int8_t a, int16_t b, int32_t c, int64_t d); - , - \\pub extern fn foo(a: u8, b: u8, c: i8) c_int; - , - \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void; - , - \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void; - ); - - cases.add_both("noreturn attribute", - \\void foo(void) __attribute__((noreturn)); - , - \\pub extern fn foo() noreturn; - ); - - cases.addC("simple function", - \\int abs(int a) { - \\ return a < 0 ? -a : a; - \\} - , - \\export fn abs(a: c_int) c_int { - \\ return if (a < 0) -a else a; - \\} - ); - - cases.add("enums", - \\enum Foo { - \\ FooA, - \\ FooB, - \\ Foo1, - \\}; - , - \\pub const enum_Foo = extern enum { - \\ A, - \\ B, - \\ @"1", - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - ); - - cases.add("enums", - \\enum Foo { - \\ FooA = 2, - \\ FooB = 5, - \\ Foo1, - \\}; - , - \\pub const enum_Foo = extern enum { - \\ A = 2, - \\ B = 5, - \\ @"1" = 6, - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - ); - - cases.add("restrict -> noalias", - \\void foo(void *restrict bar, void *restrict); - , - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; - ); - - cases.add("simple struct", - \\struct Foo { - \\ int x; - \\ char *y; - \\}; - , - \\const struct_Foo = extern struct { - \\ x: c_int, - \\ y: [*c]u8, - \\}; - , - \\pub const Foo = struct_Foo; - ); - - cases.add("qualified struct and enum", - \\struct Foo { - \\ int x; - \\ int y; - \\}; - \\enum Bar { - \\ BarA, - \\ BarB, - \\}; - \\void func(struct Foo *a, enum Bar **b); - , - \\pub const struct_Foo = extern struct { - \\ x: c_int, - \\ y: c_int, - \\}; - , - \\pub const enum_Bar = extern enum { - \\ A, - \\ B, - \\}; - , - \\pub const BarA = enum_Bar.A; - , - \\pub const BarB = enum_Bar.B; - , - \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; - , - \\pub const Foo = struct_Foo; - , - \\pub const Bar = enum_Bar; - ); + }); cases.add("constant size array", \\void func(int array[20]); - , + , &[_][]const u8{ \\pub extern fn func(array: [*c]c_int) void; - ); - - cases.add("self referential struct with function pointer", - \\struct Foo { - \\ void (*derp)(struct Foo *foo); - \\}; - , - \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn ([*c]struct_Foo) void, - \\}; - , - \\pub const Foo = struct_Foo; - ); - - cases.add("struct prototype used in func", - \\struct Foo; - \\struct Foo *some_func(struct Foo *foo, int x); - , - \\pub const struct_Foo = @OpaqueType(); - , - \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; - , - \\pub const Foo = struct_Foo; - ); - - cases.add("#define a char literal", - \\#define A_CHAR 'a' - , - \\pub const A_CHAR = 97; - ); - - cases.add("#define an unsigned integer literal", - \\#define CHANNEL_COUNT 24 - , - \\pub const CHANNEL_COUNT = 24; - ); - - cases.add("#define referencing another #define", - \\#define THING2 THING1 - \\#define THING1 1234 - , - \\pub const THING1 = 1234; - , - \\pub const THING2 = THING1; - ); - - cases.add("variables", - \\extern int extern_var; - \\static const int int_var = 13; - , - \\pub extern var extern_var: c_int; - , - \\pub const int_var: c_int = 13; - ); - - cases.add("circular struct definitions", - \\struct Bar; - \\ - \\struct Foo { - \\ struct Bar *next; - \\}; - \\ - \\struct Bar { - \\ struct Foo *next; - \\}; - , - \\pub const struct_Bar = extern struct { - \\ next: [*c]struct_Foo, - \\}; - , - \\pub const struct_Foo = extern struct { - \\ next: [*c]struct_Bar, - \\}; - ); - - cases.add("typedef void", - \\typedef void Foo; - \\Foo fun(Foo *a); - , - \\pub const Foo = c_void; - , - \\pub extern fn fun(a: ?*Foo) Foo; - ); - - cases.add("generate inline func for #define global extern fn", - \\extern void (*fn_ptr)(void); - \\#define foo fn_ptr - \\ - \\extern char (*fn_ptr2)(int, float); - \\#define bar fn_ptr2 - , - \\pub extern var fn_ptr: ?extern fn () void; - , - \\pub inline fn foo() void { - \\ return fn_ptr.?(); - \\} - , - \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8; - , - \\pub inline fn bar(arg0: c_int, arg1: f32) u8 { - \\ return fn_ptr2.?(arg0, arg1); - \\} - ); - - cases.add("#define string", - \\#define foo "a string" - , - \\pub const foo = c"a string"; - ); + }); cases.add("__cdecl doesn't mess up function pointers", \\void foo(void (__cdecl *fn_ptr)(void)); - , - \\pub extern fn foo(fn_ptr: ?extern fn () void) void; - ); + , &[_][]const u8{ + \\pub extern fn foo(fn_ptr: ?fn () callconv(.C) void) void; + }); - cases.add("comment after integer literal", - \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = 32; - ); - - cases.add("u integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_uint(32); - ); - - cases.add("l integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_long(32); - ); - - cases.add("ul integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_ulong(32); - ); - - cases.add("lu integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_ulong(32); - ); - - cases.add("ll integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_longlong(32); - ); - - cases.add("ull integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_ulonglong(32); - ); - - cases.add("llu integer suffix after hex literal", - \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ - , - \\pub const SDL_INIT_VIDEO = c_ulonglong(32); - ); - - cases.add("zig keywords in C code", - \\struct comptime { - \\ int defer; - \\}; - , - \\pub const struct_comptime = extern struct { - \\ @"defer": c_int, - \\}; - , - \\pub const @"comptime" = struct_comptime; - ); - - cases.add("macro defines string literal with hex", - \\#define FOO "aoeu\xab derp" - \\#define FOO2 "aoeu\x0007a derp" - \\#define FOO_CHAR '\xfF' - , - \\pub const FOO = c"aoeu\xab derp"; - , - \\pub const FOO2 = c"aoeuz derp"; - , - \\pub const FOO_CHAR = 255; - ); - - cases.add("macro defines string literal with octal", - \\#define FOO "aoeu\023 derp" - \\#define FOO2 "aoeu\0234 derp" - \\#define FOO_CHAR '\077' - , - \\pub const FOO = c"aoeu\x13 derp"; - , - \\pub const FOO2 = c"aoeu\x134 derp"; - , - \\pub const FOO_CHAR = 63; - ); - - cases.add("macro with parens around negative number", - \\#define LUA_GLOBALSINDEX (-10002) - , - \\pub const LUA_GLOBALSINDEX = -10002; - ); - - cases.addC("post increment", - \\unsigned foo1(unsigned a) { - \\ a++; - \\ return a; - \\} - \\int foo2(int a) { - \\ a++; - \\ return a; - \\} - , - \\pub export fn foo1(_arg_a: c_uint) c_uint { - \\ var a = _arg_a; - \\ a +%= 1; - \\ return a; - \\} - \\pub export fn foo2(_arg_a: c_int) c_int { - \\ var a = _arg_a; - \\ a += 1; - \\ return a; - \\} - ); - - cases.addC("shift right assign", - \\int log2(unsigned a) { - \\ int i = 0; - \\ while (a > 0) { - \\ a >>= 1; - \\ } - \\ return i; - \\} - , - \\pub export fn log2(_arg_a: c_uint) c_int { - \\ var a = _arg_a; - \\ var i: c_int = 0; - \\ while (a > c_uint(0)) { - \\ a >>= @import("std").math.Log2Int(c_uint)(1); - \\ } - \\ return i; - \\} - ); - - cases.addC("if statement", - \\int max(int a, int b) { - \\ if (a < b) - \\ return b; - \\ - \\ if (a < b) - \\ return b; - \\ else - \\ return a; - \\ - \\ if (a < b) ; else ; - \\} - , - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (a < b) return b; - \\ if (a < b) return b else return a; - \\ if (a < b) {} else {} - \\} - ); - - cases.addC("==, !=", - \\int max(int a, int b) { - \\ if (a == b) - \\ return a; - \\ if (a != b) - \\ return b; - \\ return a; - \\} - , - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (a == b) return a; - \\ if (a != b) return b; - \\ return a; - \\} - ); - - cases.addC("add, sub, mul, div, rem", - \\int s(int a, int b) { - \\ int c; - \\ c = a + b; - \\ c = a - b; - \\ c = a * b; - \\ c = a / b; - \\ c = a % b; - \\} - \\unsigned u(unsigned a, unsigned b) { - \\ unsigned c; - \\ c = a + b; - \\ c = a - b; - \\ c = a * b; - \\ c = a / b; - \\ c = a % b; - \\} - , - \\pub export fn s(a: c_int, b: c_int) c_int { - \\ var c: c_int = undefined; - \\ c = (a + b); - \\ c = (a - b); - \\ c = (a * b); - \\ c = @divTrunc(a, b); - \\ c = @rem(a, b); - \\} - \\pub export fn u(a: c_uint, b: c_uint) c_uint { - \\ var c: c_uint = undefined; - \\ c = (a +% b); - \\ c = (a -% b); - \\ c = (a *% b); - \\ c = (a / b); - \\ c = (a % b); - \\} - ); - - cases.addC("bitwise binary operators", - \\int max(int a, int b) { - \\ return (a & b) ^ (a | b); - \\} - , - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ return (a & b) ^ (a | b); - \\} - ); - - cases.addC("logical and, logical or", - \\int max(int a, int b) { - \\ if (a < b || a == b) - \\ return b; - \\ if (a >= b && a == b) - \\ return a; - \\ return a; - \\} - , - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if ((a < b) or (a == b)) return b; - \\ if ((a >= b) and (a == b)) return a; - \\ return a; - \\} - ); - - cases.addC("logical and, logical or on none bool values", - \\int and_or_none_bool(int a, float b, void *c) { - \\ if (a && b) return 0; - \\ if (b && c) return 1; - \\ if (a && c) return 2; - \\ if (a || b) return 3; - \\ if (b || c) return 4; - \\ if (a || c) return 5; - \\ return 6; - \\} - , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ if ((a != 0) and (b != 0)) return 0; - \\ if ((b != 0) and (c != null)) return 1; - \\ if ((a != 0) and (c != null)) return 2; - \\ if ((a != 0) or (b != 0)) return 3; - \\ if ((b != 0) or (c != null)) return 4; - \\ if ((a != 0) or (c != null)) return 5; - \\ return 6; - \\} - ); - - cases.addC("assign", - \\int max(int a) { - \\ int tmp; - \\ tmp = a; - \\ a = tmp; - \\} - , - \\pub export fn max(_arg_a: c_int) c_int { - \\ var a = _arg_a; - \\ var tmp: c_int = undefined; - \\ tmp = a; - \\ a = tmp; - \\} - ); - - cases.addC("chaining assign", - \\void max(int a) { - \\ int b, c; - \\ c = b = a; - \\} - , - \\pub export fn max(a: c_int) void { - \\ var b: c_int = undefined; - \\ var c: c_int = undefined; - \\ c = (x: { - \\ const _tmp = a; - \\ b = _tmp; - \\ break :x _tmp; - \\ }); - \\} - ); - - cases.addC("shift right assign with a fixed size type", - \\#include - \\int log2(uint32_t a) { - \\ int i = 0; - \\ while (a > 0) { - \\ a >>= 1; - \\ } - \\ return i; - \\} - , - \\pub export fn log2(_arg_a: u32) c_int { - \\ var a = _arg_a; - \\ var i: c_int = 0; - \\ while (a > c_uint(0)) { - \\ a >>= u5(1); - \\ } - \\ return i; - \\} - ); - - cases.add("anonymous enum", - \\enum { - \\ One, - \\ Two, - \\}; - , - \\pub const One = 0; - \\pub const Two = 1; - ); - - cases.addC("function call", - \\static void bar(void) { } - \\static int baz(void) { return 0; } - \\void foo(void) { - \\ bar(); - \\ baz(); - \\} - , - \\pub fn bar() void {} - \\pub fn baz() c_int { - \\ return 0; - \\} - \\pub export fn foo() void { - \\ bar(); - \\ _ = baz(); - \\} - ); - - cases.addC("field access expression", - \\struct Foo { - \\ int field; - \\}; - \\int read_field(struct Foo *foo) { - \\ return foo->field; - \\} - , - \\pub const struct_Foo = extern struct { - \\ field: c_int, - \\}; - \\pub export fn read_field(foo: [*c]struct_Foo) c_int { - \\ return foo.?.field; - \\} - ); - - cases.addC("null statements", - \\void foo(void) { - \\ ;;;;; - \\} - , - \\pub export fn foo() void { - \\ {} - \\ {} - \\ {} - \\ {} - \\ {} - \\} - ); - - cases.add("undefined array global", - \\int array[100]; - , - \\pub var array: [100]c_int = undefined; - ); - - cases.addC("array access", - \\int array[100]; - \\int foo(int index) { - \\ return array[index]; - \\} - , - \\pub var array: [100]c_int = undefined; - \\pub export fn foo(index: c_int) c_int { - \\ return array[index]; - \\} - ); - - cases.addC("c style cast", - \\int float_to_int(float a) { - \\ return (int)a; - \\} - , - \\pub export fn float_to_int(a: f32) c_int { - \\ return c_int(a); - \\} - ); - - cases.addC("void cast", - \\void foo(int a) { + cases.add("void cast", + \\void foo() { + \\ int a; \\ (void) a; \\} - , - \\pub export fn foo(a: c_int) void { + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = undefined; \\ _ = a; \\} - ); + }); - cases.addC("implicit cast to void *", - \\void *foo(unsigned short *x) { + cases.add("implicit cast to void *", + \\void *foo() { + \\ unsigned short *x; \\ return x; \\} - , - \\pub export fn foo(x: [*c]c_ushort) ?*c_void { + , &[_][]const u8{ + \\pub export fn foo() ?*c_void { + \\ var x: [*c]c_ushort = undefined; \\ return @ptrCast(?*c_void, x); \\} - ); + }); - cases.addC("sizeof", - \\#include - \\size_t size_of(void) { - \\ return sizeof(int); - \\} - , - \\pub export fn size_of() usize { - \\ return @sizeOf(c_int); - \\} - ); - - cases.addC("null pointer implicit cast", + cases.add("null pointer implicit cast", \\int* foo(void) { \\ return 0; \\} - , + , &[_][]const u8{ \\pub export fn foo() [*c]c_int { \\ return null; \\} - ); - - cases.addC("comma operator", - \\int foo(void) { - \\ return 1, 2; - \\} - , - \\pub export fn foo() c_int { - \\ return x: { - \\ _ = 1; - \\ break :x 2; - \\ }; - \\} - ); - - cases.addC("statement expression", - \\int foo(void) { - \\ return ({ - \\ int a = 1; - \\ a; - \\ }); - \\} - , - \\pub export fn foo() c_int { - \\ return x: { - \\ var a: c_int = 1; - \\ break :x a; - \\ }; - \\} - ); - - cases.addC("__extension__ cast", - \\int foo(void) { - \\ return __extension__ 1; - \\} - , - \\pub export fn foo() c_int { - \\ return 1; - \\} - ); - - cases.addC("bitshift", - \\int foo(void) { - \\ return (1 << 2) >> 1; - \\} - , - \\pub export fn foo() c_int { - \\ return (1 << @import("std").math.Log2Int(c_int)(2)) >> @import("std").math.Log2Int(c_int)(1); - \\} - ); - - cases.addC("compound assignment operators", - \\void foo(void) { - \\ int a = 0; - \\ a += (a += 1); - \\ a -= (a -= 1); - \\ a *= (a *= 1); - \\ a &= (a &= 1); - \\ a |= (a |= 1); - \\ a ^= (a ^= 1); - \\ a >>= (a >>= 1); - \\ a <<= (a <<= 1); - \\} - , - \\pub export fn foo() void { - \\ var a: c_int = 0; - \\ a += (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* + 1); - \\ break :x _ref.*; - \\ }); - \\ a -= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* - 1); - \\ break :x _ref.*; - \\ }); - \\ a *= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* * 1); - \\ break :x _ref.*; - \\ }); - \\ a &= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* & 1); - \\ break :x _ref.*; - \\ }); - \\ a |= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* | 1); - \\ break :x _ref.*; - \\ }); - \\ a ^= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* ^ 1); - \\ break :x _ref.*; - \\ }); - \\ a >>= @import("std").math.Log2Int(c_int)((x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* >> @import("std").math.Log2Int(c_int)(1)); - \\ break :x _ref.*; - \\ })); - \\ a <<= @import("std").math.Log2Int(c_int)((x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* << @import("std").math.Log2Int(c_int)(1)); - \\ break :x _ref.*; - \\ })); - \\} - ); - - cases.addC("compound assignment operators unsigned", - \\void foo(void) { - \\ unsigned a = 0; - \\ a += (a += 1); - \\ a -= (a -= 1); - \\ a *= (a *= 1); - \\ a &= (a &= 1); - \\ a |= (a |= 1); - \\ a ^= (a ^= 1); - \\ a >>= (a >>= 1); - \\ a <<= (a <<= 1); - \\} - , - \\pub export fn foo() void { - \\ var a: c_uint = c_uint(0); - \\ a +%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* +% c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a -%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* -% c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a *%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* *% c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a &= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* & c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a |= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* | c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a ^= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* ^ c_uint(1)); - \\ break :x _ref.*; - \\ }); - \\ a >>= @import("std").math.Log2Int(c_uint)((x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* >> @import("std").math.Log2Int(c_uint)(1)); - \\ break :x _ref.*; - \\ })); - \\ a <<= @import("std").math.Log2Int(c_uint)((x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* << @import("std").math.Log2Int(c_uint)(1)); - \\ break :x _ref.*; - \\ })); - \\} - ); - - cases.addC("duplicate typedef", - \\typedef long foo; - \\typedef int bar; - \\typedef long foo; - \\typedef int baz; - , - \\pub const foo = c_long; - \\pub const bar = c_int; - \\pub const baz = c_int; - ); - - cases.addC("post increment/decrement", - \\void foo(void) { - \\ int i = 0; - \\ unsigned u = 0; - \\ i++; - \\ i--; - \\ u++; - \\ u--; - \\ i = i++; - \\ i = i--; - \\ u = u++; - \\ u = u--; - \\} - , - \\pub export fn foo() void { - \\ var i: c_int = 0; - \\ var u: c_uint = c_uint(0); - \\ i += 1; - \\ i -= 1; - \\ u +%= 1; - \\ u -%= 1; - \\ i = (x: { - \\ const _ref = &i; - \\ const _tmp = _ref.*; - \\ _ref.* += 1; - \\ break :x _tmp; - \\ }); - \\ i = (x: { - \\ const _ref = &i; - \\ const _tmp = _ref.*; - \\ _ref.* -= 1; - \\ break :x _tmp; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ const _tmp = _ref.*; - \\ _ref.* +%= 1; - \\ break :x _tmp; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ const _tmp = _ref.*; - \\ _ref.* -%= 1; - \\ break :x _tmp; - \\ }); - \\} - ); - - cases.addC("pre increment/decrement", - \\void foo(void) { - \\ int i = 0; - \\ unsigned u = 0; - \\ ++i; - \\ --i; - \\ ++u; - \\ --u; - \\ i = ++i; - \\ i = --i; - \\ u = ++u; - \\ u = --u; - \\} - , - \\pub export fn foo() void { - \\ var i: c_int = 0; - \\ var u: c_uint = c_uint(0); - \\ i += 1; - \\ i -= 1; - \\ u +%= 1; - \\ u -%= 1; - \\ i = (x: { - \\ const _ref = &i; - \\ _ref.* += 1; - \\ break :x _ref.*; - \\ }); - \\ i = (x: { - \\ const _ref = &i; - \\ _ref.* -= 1; - \\ break :x _ref.*; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ _ref.* +%= 1; - \\ break :x _ref.*; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ _ref.* -%= 1; - \\ break :x _ref.*; - \\ }); - \\} - ); - - cases.addC("do loop", - \\void foo(void) { - \\ int a = 2; - \\ do { - \\ a--; - \\ } while (a != 0); - \\ - \\ int b = 2; - \\ do - \\ b--; - \\ while (b != 0); - \\} - , - \\pub export fn foo() void { - \\ var a: c_int = 2; - \\ while (true) { - \\ a -= 1; - \\ if (!(a != 0)) break; - \\ } - \\ var b: c_int = 2; - \\ while (true) { - \\ b -= 1; - \\ if (!(b != 0)) break; - \\ } - \\} - ); - - cases.addC("deref function pointer", - \\void foo(void) {} - \\int baz(void) { return 0; } - \\void bar(void) { - \\ void(*f)(void) = foo; - \\ int(*b)(void) = baz; - \\ f(); - \\ (*(f))(); - \\ foo(); - \\ b(); - \\ (*(b))(); - \\ baz(); - \\} - , - \\pub export fn foo() void {} - \\pub export fn baz() c_int { - \\ return 0; - \\} - \\pub export fn bar() void { - \\ var f: ?extern fn () void = foo; - \\ var b: ?extern fn () c_int = baz; - \\ f.?(); - \\ f.?(); - \\ foo(); - \\ _ = b.?(); - \\ _ = b.?(); - \\ _ = baz(); - \\} - ); - - cases.addC("normal deref", - \\void foo(int *x) { - \\ *x = 1; - \\} - , - \\pub export fn foo(x: [*c]c_int) void { - \\ x.?.* = 1; - \\} - ); + }); cases.add("simple union", \\union Foo { \\ int x; \\ double y; \\}; - , + , &[_][]const u8{ \\pub const union_Foo = extern union { \\ x: c_int, \\ y: f64, \\}; , \\pub const Foo = union_Foo; - ); - - cases.add("address of operator", - \\int foo(void) { - \\ int x = 1234; - \\ int *ptr = &x; - \\ return *ptr; - \\} - , - \\pub fn foo() c_int { - \\ var x: c_int = 1234; - \\ var ptr: [*c]c_int = &x; - \\ return ptr.?.*; - \\} - ); + }); cases.add("string literal", \\const char *foo(void) { \\ return "bar"; \\} - , - \\pub fn foo() [*c]const u8 { - \\ return c"bar"; + , &[_][]const u8{ + \\pub export fn foo() [*c]const u8 { + \\ return "bar"; \\} - ); + }); cases.add("return void", \\void foo(void) { \\ return; \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ return; \\} - ); + }); cases.add("for loop", \\void foo(void) { - \\ for (int i = 0; i < 10; i += 1) { } + \\ for (int i = 0; i; i++) { } \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ { \\ var i: c_int = 0; - \\ while (i < 10) : (i += 1) {} + \\ while (i != 0) : (i += 1) {} \\ } \\} - ); + }); cases.add("empty for loop", \\void foo(void) { \\ for (;;) { } \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ while (true) {} \\} - ); + }); + + cases.add("for loop with simple init expression", + \\void foo(void) { + \\ int i; + \\ for (i = 3; i; i--) { } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = undefined; + \\ { + \\ i = 3; + \\ while (i != 0) : (i -= 1) {} + \\ } + \\} + }); cases.add("break statement", \\void foo(void) { @@ -1394,13 +771,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ break; \\ } \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ while (true) { \\ break; \\ } \\} - ); + }); cases.add("continue statement", \\void foo(void) { @@ -1408,13 +785,435 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ continue; \\ } \\} - , - \\pub fn foo() void { + , &[_][]const u8{ + \\pub export fn foo() void { \\ while (true) { \\ continue; \\ } \\} - ); + }); + + cases.add("pointer casting", + \\float *ptrcast() { + \\ int *a; + \\ return (float *)a; + \\} + , &[_][]const u8{ + \\pub export fn ptrcast() [*c]f32 { + \\ var a: [*c]c_int = undefined; + \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); + \\} + }); + + cases.add("pointer conversion with different alignment", + \\void test_ptr_cast() { + \\ void *p; + \\ { + \\ char *to_char = (char *)p; + \\ short *to_short = (short *)p; + \\ int *to_int = (int *)p; + \\ long long *to_longlong = (long long *)p; + \\ } + \\ { + \\ char *to_char = p; + \\ short *to_short = p; + \\ int *to_int = p; + \\ long long *to_longlong = p; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn test_ptr_cast() void { + \\ var p: ?*c_void = undefined; + \\ { + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ } + \\ { + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ } + \\} + }); + + cases.add("while on non-bool", + \\int while_none_bool() { + \\ int a; + \\ float b; + \\ void *c; + \\ while (a) return 0; + \\ while (b) return 1; + \\ while (c) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn while_none_bool() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + + cases.add("for on non-bool", + \\int for_none_bool() { + \\ int a; + \\ float b; + \\ void *c; + \\ for (;a;) return 0; + \\ for (;b;) return 1; + \\ for (;c;) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn for_none_bool() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + + cases.add("bitshift", + \\int foo(void) { + \\ return (1 << 2) >> 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return (@as(c_int, 1) << @intCast(@import("std").math.Log2Int(c_int), 2)) >> @intCast(@import("std").math.Log2Int(c_int), 1); + \\} + }); + + cases.add("sizeof", + \\#include + \\size_t size_of(void) { + \\ return sizeof(int); + \\} + , &[_][]const u8{ + \\pub export fn size_of() usize { + \\ return @sizeOf(c_int); + \\} + }); + + cases.add("normal deref", + \\void foo() { + \\ int *x; + \\ *x = 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var x: [*c]c_int = undefined; + \\ x.?.* = 1; + \\} + }); + + cases.add("address of operator", + \\int foo(void) { + \\ int x = 1234; + \\ int *ptr = &x; + \\ return *ptr; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var x: c_int = 1234; + \\ var ptr: [*c]c_int = &x; + \\ return ptr.?.*; + \\} + }); + + cases.add("bin not", + \\int foo() { + \\ int x; + \\ return ~x; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var x: c_int = undefined; + \\ return ~x; + \\} + }); + + cases.add("bool not", + \\int foo() { + \\ int a; + \\ float b; + \\ void *c; + \\ return !(a == 0); + \\ return !a; + \\ return !b; + \\ return !c; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; + \\ return !(a == @as(c_int, 0)); + \\ return !(a != 0); + \\ return !(b != 0); + \\ return !(c != null); + \\} + }); + + cases.add("__extension__ cast", + \\int foo(void) { + \\ return __extension__ 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return 1; + \\} + }); + + if (builtin.os != builtin.Os.windows) { + // sysv_abi not currently supported on windows + cases.add("Macro qualified functions", + \\void __attribute__((sysv_abi)) foo(void); + , &[_][]const u8{ + \\pub extern fn foo() void; + }); + } + + cases.add("Forward-declared enum", + \\extern enum enum_ty my_enum; + \\enum enum_ty { FOO }; + , &[_][]const u8{ + \\pub const FOO = @enumToInt(enum_enum_ty.FOO); + \\pub const enum_enum_ty = extern enum { + \\ FOO, + \\}; + \\pub extern var my_enum: enum_enum_ty; + }); + + cases.add("Parameterless function pointers", + \\typedef void (*fn0)(); + \\typedef void (*fn1)(char); + , &[_][]const u8{ + \\pub const fn0 = ?fn (...) callconv(.C) void; + \\pub const fn1 = ?fn (u8) callconv(.C) void; + }); + + cases.addWithTarget("Calling convention", tests.Target{ + .Cross = .{ .os = .linux, .arch = .i386, .abi = .none }, + }, + \\void __attribute__((fastcall)) foo1(float *a); + \\void __attribute__((stdcall)) foo2(float *a); + \\void __attribute__((vectorcall)) foo3(float *a); + \\void __attribute__((cdecl)) foo4(float *a); + \\void __attribute__((thiscall)) foo5(float *a); + , &[_][]const u8{ + \\pub fn foo1(a: [*c]f32) callconv(.Fastcall) void; + \\pub fn foo2(a: [*c]f32) callconv(.Stdcall) void; + \\pub fn foo3(a: [*c]f32) callconv(.Vectorcall) void; + \\pub extern fn foo4(a: [*c]f32) void; + \\pub fn foo5(a: [*c]f32) callconv(.Thiscall) void; + }); + + cases.addWithTarget("Calling convention", tests.Target{ + .Cross = .{ .os = .linux, .arch = .{ .arm = .v8_5a }, .abi = .none }, + }, + \\void __attribute__((pcs("aapcs"))) foo1(float *a); + \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a); + , &[_][]const u8{ + \\pub fn foo1(a: [*c]f32) callconv(.AAPCS) void; + \\pub fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; + }); + + cases.addWithTarget("Calling convention", tests.Target{ + .Cross = .{ .os = .linux, .arch = .{ .aarch64 = .v8_5a }, .abi = .none }, + }, + \\void __attribute__((aarch64_vector_pcs)) foo1(float *a); + , &[_][]const u8{ + \\pub fn foo1(a: [*c]f32) callconv(.Vectorcall) void; + }); + + cases.add("Parameterless function prototypes", + \\void a() {} + \\void b(void) {} + \\void c(); + \\void d(void); + , &[_][]const u8{ + \\pub export fn a() void {} + \\pub export fn b() void {} + \\pub extern fn c(...) void; + \\pub extern fn d() void; + }); + + cases.add("variable declarations", + \\extern char arr0[] = "hello"; + \\static char arr1[] = "hello"; + \\char arr2[] = "hello"; + , &[_][]const u8{ + \\pub extern var arr0: [*c]u8 = "hello"; + \\pub var arr1: [*c]u8 = "hello"; + \\pub export var arr2: [*c]u8 = "hello"; + }); + + cases.add("array initializer expr", + \\static void foo(void){ + \\ char arr[10] ={1}; + \\ char *arr1[10] ={0}; + \\} + , &[_][]const u8{ + \\pub fn foo() callconv(.C) void { + \\ var arr: [10]u8 = .{ + \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))), + \\ } ++ .{0} ** 9; + \\ var arr1: [10][*c]u8 = .{ + \\ null, + \\ } ++ .{null} ** 9; + \\} + }); + + cases.add("enums", + \\typedef enum { + \\ a, + \\ b, + \\ c, + \\} d; + \\enum { + \\ e, + \\ f = 4, + \\ g, + \\} h = e; + \\struct Baz { + \\ enum { + \\ i, + \\ j, + \\ k, + \\ } l; + \\ d m; + \\}; + \\enum i { + \\ n, + \\ o, + \\ p, + \\}; + , &[_][]const u8{ + \\pub const a = @enumToInt(enum_unnamed_1.a); + \\pub const b = @enumToInt(enum_unnamed_1.b); + \\pub const c = @enumToInt(enum_unnamed_1.c); + \\const enum_unnamed_1 = extern enum { + \\ a, + \\ b, + \\ c, + \\}; + \\pub const d = enum_unnamed_1; + \\pub const e = @enumToInt(enum_unnamed_2.e); + \\pub const f = @enumToInt(enum_unnamed_2.f); + \\pub const g = @enumToInt(enum_unnamed_2.g); + \\const enum_unnamed_2 = extern enum { + \\ e = 0, + \\ f = 4, + \\ g = 5, + \\}; + \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); + \\pub const i = @enumToInt(enum_unnamed_3.i); + \\pub const j = @enumToInt(enum_unnamed_3.j); + \\pub const k = @enumToInt(enum_unnamed_3.k); + \\const enum_unnamed_3 = extern enum { + \\ i, + \\ j, + \\ k, + \\}; + \\pub const struct_Baz = extern struct { + \\ l: enum_unnamed_3, + \\ m: d, + \\}; + \\pub const n = @enumToInt(enum_i.n); + \\pub const o = @enumToInt(enum_i.o); + \\pub const p = @enumToInt(enum_i.p); + \\pub const enum_i = extern enum { + \\ n, + \\ o, + \\ p, + \\}; + , + \\pub const Baz = struct_Baz; + }); + + cases.add("#define a char literal", + \\#define A_CHAR 'a' + , &[_][]const u8{ + \\pub const A_CHAR = 'a'; + }); + + cases.add("comment after integer literal", + \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = 0x00000020; + }); + + cases.add("u integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020); + }); + + cases.add("l integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020); + }); + + cases.add("ul integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020); + }); + + cases.add("lu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020); + }); + + cases.add("ll integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020); + }); + + cases.add("ull integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020); + }); + + cases.add("llu integer suffix after hex literal", + \\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ + , &[_][]const u8{ + \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020); + }); + + cases.add("generate inline func for #define global extern fn", + \\extern void (*fn_ptr)(void); + \\#define foo fn_ptr + \\ + \\extern char (*fn_ptr2)(int, float); + \\#define bar fn_ptr2 + , &[_][]const u8{ + \\pub extern var fn_ptr: ?fn () callconv(.C) void; + , + \\pub inline fn foo() void { + \\ return fn_ptr.?(); + \\} + , + \\pub extern var fn_ptr2: ?fn (c_int, f32) callconv(.C) u8; + , + \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 { + \\ return fn_ptr2.?(arg_1, arg_2); + \\} + }); cases.add("macros with field targets", \\typedef unsigned int GLbitfield; @@ -1429,118 +1228,740 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\extern union OpenGLProcs glProcs; \\#define glClearUnion glProcs.gl.Clear \\#define glClearPFN PFNGLCLEARPROC - , + , &[_][]const u8{ \\pub const GLbitfield = c_uint; - , - \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; - , - \\pub const OpenGLProc = ?extern fn () void; - , + \\pub const PFNGLCLEARPROC = ?fn (GLbitfield) callconv(.C) void; + \\pub const OpenGLProc = ?fn () callconv(.C) void; + \\const struct_unnamed_1 = extern struct { + \\ Clear: PFNGLCLEARPROC, + \\}; \\pub const union_OpenGLProcs = extern union { \\ ptr: [1]OpenGLProc, - \\ gl: extern struct { - \\ Clear: PFNGLCLEARPROC, - \\ }, + \\ gl: struct_unnamed_1, \\}; - , \\pub extern var glProcs: union_OpenGLProcs; , \\pub const glClearPFN = PFNGLCLEARPROC; , - \\pub inline fn glClearUnion(arg0: GLbitfield) void { - \\ return glProcs.gl.Clear.?(arg0); + \\pub inline fn glClearUnion(arg_2: GLbitfield) void { + \\ return glProcs.gl.Clear.?(arg_2); \\} , \\pub const OpenGLProcs = union_OpenGLProcs; - ); - - cases.add("variable name shadowing", - \\int foo(void) { - \\ int x = 1; - \\ { - \\ int x = 2; - \\ x += 1; - \\ } - \\ return x; - \\} - , - \\pub fn foo() c_int { - \\ var x: c_int = 1; - \\ { - \\ var x_0: c_int = 2; - \\ x_0 += 1; - \\ } - \\ return x; - \\} - ); - - cases.add("pointer casting", - \\float *ptrcast(int *a) { - \\ return (float *)a; - \\} - , - \\fn ptrcast(a: [*c]c_int) [*c]f32 { - \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); - \\} - ); - - cases.add("bin not", - \\int foo(int x) { - \\ return ~x; - \\} - , - \\pub fn foo(x: c_int) c_int { - \\ return ~x; - \\} - ); - - cases.add("bool not", - \\int foo(int a, float b, void *c) { - \\ return !(a == 0); - \\ return !a; - \\ return !b; - \\ return !c; - \\} - , - \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { - \\ return !(a == 0); - \\ return !(a != 0); - \\ return !(b != 0); - \\ return !(c != null); - \\} - ); - - cases.add("primitive types included in defined symbols", - \\int foo(int u32) { - \\ return u32; - \\} - , - \\pub fn foo(u32_0: c_int) c_int { - \\ return u32_0; - \\} - ); - - cases.add("const ptr initializer", - \\static const char *v0 = "0.0.0"; - , - \\pub var v0: [*c]const u8 = c"0.0.0"; - ); - - cases.add("static incomplete array inside function", - \\void foo(void) { - \\ static const char v2[] = "2.2.2"; - \\} - , - \\pub fn foo() void { - \\ const v2: [*c]const u8 = c"2.2.2"; - \\} - ); + }); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) + , &[_][]const u8{ + \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == .Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE); + }); + + cases.add("basic macro function", + \\extern int c; + \\#define BASIC(c) (c*2) + , &[_][]const u8{ + \\pub extern var c: c_int; , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*c]NRF_GPIO_Type)(NRF_GPIO_BASE); - ); + \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) { + \\ return c_1 * 2; + \\} + }); + + cases.add("macro defines string literal with hex", + \\#define FOO "aoeu\xab derp" + \\#define FOO2 "aoeu\x0007a derp" + \\#define FOO_CHAR '\xfF' + , &[_][]const u8{ + \\pub const FOO = "aoeu\xab derp"; + , + \\pub const FOO2 = "aoeu\x7a derp"; + , + \\pub const FOO_CHAR = '\xff'; + }); + + cases.add("macro add", + \\#define PERIPH_BASE (0x40000000UL) /*!< Base address of : AHB/APB Peripherals */ + \\#define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL) + \\#define RCC_BASE (D3_AHB1PERIPH_BASE + 0x4400UL) + , &[_][]const u8{ + \\pub const PERIPH_BASE = @as(c_ulong, 0x40000000); + , + \\pub const D3_APB1PERIPH_BASE = PERIPH_BASE + @as(c_ulong, 0x18000000); + , + \\pub const RCC_BASE = D3_AHB1PERIPH_BASE + @as(c_ulong, 0x4400); + }); + + cases.add("variable aliasing", + \\static long a = 2; + \\static long b = 2; + \\static int c = 4; + \\void foo(char c) { + \\ int a; + \\ char b = 123; + \\ b = (char) a; + \\ { + \\ int d = 5; + \\ } + \\ unsigned d = 440; + \\} + , &[_][]const u8{ + \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); + \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); + \\pub var c: c_int = 4; + \\pub export fn foo(arg_c_1: u8) void { + \\ var c_1 = arg_c_1; + \\ var a_2: c_int = undefined; + \\ var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); + \\ b_3 = @bitCast(u8, @truncate(i8, a_2)); + \\ { + \\ var d: c_int = 5; + \\ } + \\ var d: c_uint = @bitCast(c_uint, @as(c_int, 440)); + \\} + }); + + cases.add("comma operator", + \\int foo() { + \\ 2, 4; + \\ return 2, 4, 6; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ _ = @as(c_int, 2); + \\ _ = @as(c_int, 4); + \\ _ = @as(c_int, 2); + \\ _ = @as(c_int, 4); + \\ return @as(c_int, 6); + \\} + }); + + cases.add("worst-case assign", + \\int foo() { + \\ int a; + \\ int b; + \\ a = b = 2; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = undefined; + \\ var b: c_int = undefined; + \\ a = blk: { + \\ const tmp = @as(c_int, 2); + \\ b = tmp; + \\ break :blk tmp; + \\ }; + \\} + }); + + cases.add("while loops", + \\int foo() { + \\ int a = 5; + \\ while (2) + \\ a = 2; + \\ while (4) { + \\ int a = 4; + \\ a = 9; + \\ return 6, a; + \\ } + \\ do { + \\ int a = 2; + \\ a = 12; + \\ } while (4); + \\ do + \\ a = 7; + \\ while (4); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = 5; + \\ while (@as(c_int, 2) != 0) a = 2; + \\ while (@as(c_int, 4) != 0) { + \\ var a_1: c_int = 4; + \\ a_1 = 9; + \\ _ = @as(c_int, 6); + \\ return a_1; + \\ } + \\ while (true) { + \\ var a_1: c_int = 2; + \\ a_1 = 12; + \\ if (!(@as(c_int, 4) != 0)) break; + \\ } + \\ while (true) { + \\ a = 7; + \\ if (!(@as(c_int, 4) != 0)) break; + \\ } + \\} + }); + + cases.add("for loops", + \\int foo() { + \\ for (int i = 2, b = 4; i + 2; i = 2) { + \\ int a = 2; + \\ a = 6, 5, 7; + \\ } + \\ char i = 2; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ { + \\ var i: c_int = 2; + \\ var b: c_int = 4; + \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) { + \\ var a: c_int = 2; + \\ a = 6; + \\ _ = @as(c_int, 5); + \\ _ = @as(c_int, 7); + \\ } + \\ } + \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2))); + \\} + }); + + cases.add("shadowing primitive types", + \\unsigned anyerror = 2; + , &[_][]const u8{ + \\pub export var _anyerror: c_uint = @bitCast(c_uint, @as(c_int, 2)); + }); + + cases.add("floats", + \\float a = 3.1415; + \\double b = 3.1415; + \\int c = 3.1415; + \\double d = 3; + , &[_][]const u8{ + \\pub export var a: f32 = @floatCast(f32, 3.1415); + \\pub export var b: f64 = 3.1415; + \\pub export var c: c_int = @floatToInt(c_int, 3.1415); + \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3)); + }); + + cases.add("conditional operator", + \\int bar(void) { + \\ if (2 ? 5 : 5 ? 4 : 6) 2; + \\ return 2 ? 5 : 5 ? 4 : 6; + \\} + , &[_][]const u8{ + \\pub export fn bar() c_int { + \\ if ((if (@as(c_int, 2) != 0) @as(c_int, 5) else (if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2); + \\ return if (@as(c_int, 2) != 0) @as(c_int, 5) else if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6); + \\} + }); + + cases.add("switch on int", + \\int switch_fn(int i) { + \\ int res = 0; + \\ switch (i) { + \\ case 0: + \\ res = 1; + \\ case 1 ... 3: + \\ res = 2; + \\ default: + \\ res = 3 * i; + \\ break; + \\ case 4: + \\ res = 5; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn switch_fn(arg_i: c_int) c_int { + \\ var i = arg_i; + \\ var res: c_int = 0; + \\ __switch: { + \\ __case_2: { + \\ __default: { + \\ __case_1: { + \\ __case_0: { + \\ switch (i) { + \\ @as(c_int, 0) => break :__case_0, + \\ @as(c_int, 1)...@as(c_int, 3) => break :__case_1, + \\ else => break :__default, + \\ @as(c_int, 4) => break :__case_2, + \\ } + \\ } + \\ res = 1; + \\ } + \\ res = 2; + \\ } + \\ res = (@as(c_int, 3) * i); + \\ break :__switch; + \\ } + \\ res = 5; + \\ } + \\} + }); + + if (builtin.os != .windows) { + // When clang uses the -windows-none triple it behaves as MSVC and + // interprets the inner `struct Bar` as an anonymous structure + cases.add("type referenced struct", + \\struct Foo { + \\ struct Bar{ + \\ int b; + \\ }; + \\ struct Bar c; + \\}; + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ b: c_int, + \\}; + \\pub const struct_Foo = extern struct { + \\ c: struct_Bar, + \\}; + }); + } + + cases.add("undefined array global", + \\int array[100] = {}; + , &[_][]const u8{ + \\pub export var array: [100]c_int = .{0} ** 100; + }); + + cases.add("restrict -> noalias", + \\void foo(void *restrict bar, void *restrict); + , &[_][]const u8{ + \\pub extern fn foo(noalias bar: ?*c_void, noalias ?*c_void) void; + }); + + cases.add("assign", + \\int max(int a) { + \\ int tmp; + \\ tmp = a; + \\ a = tmp; + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int) c_int { + \\ var a = arg_a; + \\ var tmp: c_int = undefined; + \\ tmp = a; + \\ a = tmp; + \\} + }); + + cases.add("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int) void { + \\ var a = arg_a; + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = blk: { + \\ const tmp = a; + \\ b = tmp; + \\ break :blk tmp; + \\ }; + \\} + }); + + cases.add("anonymous enum", + \\enum { + \\ One, + \\ Two, + \\}; + , &[_][]const u8{ + \\pub const One = @enumToInt(enum_unnamed_1.One); + \\pub const Two = @enumToInt(enum_unnamed_1.Two); + \\const enum_unnamed_1 = extern enum { + \\ One, + \\ Two, + \\}; + }); + + cases.add("c style cast", + \\int float_to_int(float a) { + \\ return (int)a; + \\} + , &[_][]const u8{ + \\pub export fn float_to_int(arg_a: f32) c_int { + \\ var a = arg_a; + \\ return @floatToInt(c_int, a); + \\} + }); + + // TODO translate-c should in theory be able to figure out to drop all these casts + cases.add("escape sequences", + \\const char *escapes() { + \\char a = '\'', + \\ b = '\\', + \\ c = '\a', + \\ d = '\b', + \\ e = '\f', + \\ f = '\n', + \\ g = '\r', + \\ h = '\t', + \\ i = '\v', + \\ j = '\0', + \\ k = '\"'; + \\ return "\'\\\a\b\f\n\r\t\v\0\""; + \\} + \\ + , &[_][]const u8{ + \\pub export fn escapes() [*c]const u8 { + \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\''))); + \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\'))); + \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07'))); + \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08'))); + \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c'))); + \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n'))); + \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r'))); + \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t'))); + \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b'))); + \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00'))); + \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"'))); + \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; + \\} + }); + + cases.add("do loop", + \\void foo(void) { + \\ int a = 2; + \\ do { + \\ a = a - 1; + \\ } while (a); + \\ + \\ int b = 2; + \\ do + \\ b = b -1; + \\ while (b); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 2; + \\ while (true) { + \\ a = (a - @as(c_int, 1)); + \\ if (!(a != 0)) break; + \\ } + \\ var b: c_int = 2; + \\ while (true) { + \\ b = (b - @as(c_int, 1)); + \\ if (!(b != 0)) break; + \\ } + \\} + }); + + cases.add("logical and, logical or, on non-bool values, extra parens", + \\enum Foo { + \\ FooA, + \\ FooB, + \\ FooC, + \\}; + \\typedef int SomeTypedef; + \\int and_or_non_bool(int a, float b, void *c) { + \\ enum Foo d = FooA; + \\ int e = (a && b); + \\ int f = (b && c); + \\ int g = (a && c); + \\ int h = (a || b); + \\ int i = (b || c); + \\ int j = (a || c); + \\ int k = (a || d); + \\ int l = (d && b); + \\ int m = (c || d); + \\ SomeTypedef td = 44; + \\ int o = (td || b); + \\ int p = (c && td); + \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); + \\} + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub const SomeTypedef = c_int; + \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ var c = arg_c; + \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); + \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); + \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); + \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); + \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); + \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); + \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); + \\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0))); + \\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0))); + \\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0))); + \\ var td: SomeTypedef = 44; + \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); + \\ var p: c_int = @boolToInt(((c != null) and (td != 0))); + \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); + \\} + , + \\pub const Foo = enum_Foo; + }); + + cases.add("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + , + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; + , + \\pub const Foo = struct_Foo; + \\pub const Bar = enum_Bar; + }); + + cases.add("bitwise binary operators, simpler parens", + \\int max(int a, int b) { + \\ return (a & b) ^ (a | b); + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ return ((a & b) ^ (a | b)); + \\} + }); + + cases.add("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons. + \\int test_comparisons(int a, int b) { + \\ int c = (a < b); + \\ int d = (a > b); + \\ int e = (a <= b); + \\ int f = (a >= b); + \\ int g = (c < d); + \\ int h = (e < f); + \\ int i = (g < h); + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ var c: c_int = @boolToInt((a < b)); + \\ var d: c_int = @boolToInt((a > b)); + \\ var e: c_int = @boolToInt((a <= b)); + \\ var f: c_int = @boolToInt((a >= b)); + \\ var g: c_int = @boolToInt((c < d)); + \\ var h: c_int = @boolToInt((e < f)); + \\ var i: c_int = @boolToInt((g < h)); + \\ return i; + \\} + }); + + cases.add("==, !=", + \\int max(int a, int b) { + \\ if (a == b) + \\ return a; + \\ if (a != b) + \\ return b; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ if (a == b) return a; + \\ if (a != b) return b; + \\ return a; + \\} + }); + + cases.add("typedeffed bool expression", + \\typedef char* yes; + \\void foo(void) { + \\ yes a; + \\ if (a) 2; + \\} + , &[_][]const u8{ + \\pub const yes = [*c]u8; + \\pub export fn foo() void { + \\ var a: yes = undefined; + \\ if (a != null) _ = @as(c_int, 2); + \\} + }); + + cases.add("statement expression", + \\int foo(void) { + \\ return ({ + \\ int a = 1; + \\ a; + \\ a; + \\ }); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return (blk: { + \\ var a: c_int = 1; + \\ _ = a; + \\ break :blk a; + \\ }); + \\} + }); + + cases.add("field access expression", + \\#define ARROW a->b + \\#define DOT a.b + \\extern struct Foo { + \\ int b; + \\}a; + \\float b = 2.0f; + \\int foo(void) { + \\ struct Foo *c; + \\ a.b; + \\ c->b; + \\} + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ b: c_int, + \\}; + \\pub extern var a: struct_Foo; + \\pub export var b: f32 = 2; + \\pub export fn foo() c_int { + \\ var c: [*c]struct_Foo = undefined; + \\ _ = a.b; + \\ _ = c.*.b; + \\} + , + \\pub const DOT = a.b; + , + \\pub const ARROW = a.*.b; + }); + + cases.add("array access", + \\#define ACCESS array[2] + \\int array[100] = {}; + \\int foo(int index) { + \\ return array[index]; + \\} + , &[_][]const u8{ + \\pub export var array: [100]c_int = .{0} ** 100; + \\pub export fn foo(arg_index: c_int) c_int { + \\ var index = arg_index; + \\ return array[@intCast(c_uint, index)]; + \\} + , + \\pub const ACCESS = array[2]; + }); + + cases.add("cast signed array index to unsigned", + \\void foo() { + \\ int a[10], i = 0; + \\ a[i] = 0; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: [10]c_int = undefined; + \\ var i: c_int = 0; + \\ a[@intCast(c_uint, i)] = 0; + \\} + }); + + cases.add("long long array index cast to usize", + \\void foo() { + \\ long long a[10], i = 0; + \\ a[i] = 0; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: [10]c_longlong = undefined; + \\ var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); + \\ a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); + \\} + }); + + cases.add("unsigned array index skips cast", + \\void foo() { + \\ unsigned int a[10], i = 0; + \\ a[i] = 0; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: [10]c_uint = undefined; + \\ var i: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ a[i] = @bitCast(c_uint, @as(c_int, 0)); + \\} + }); + + cases.add("macro call", + \\#define CALL(arg) bar(arg) + , &[_][]const u8{ + \\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) { + \\ return bar(arg); + \\} + }); + + cases.add("logical and, logical or", + \\int max(int a, int b) { + \\ if (a < b || a == b) + \\ return b; + \\ if (a >= b && a == b) + \\ return a; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ if ((a < b) or (a == b)) return b; + \\ if ((a >= b) and (a == b)) return a; + \\ return a; + \\} + }); + + cases.add("simple if statement", + \\int max(int a, int b) { + \\ if (a < b) + \\ return b; + \\ + \\ if (a < b) + \\ return b; + \\ else + \\ return a; + \\ + \\ if (a < b) ; else ; + \\} + , &[_][]const u8{ + \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ if (a < b) return b; + \\ if (a < b) return b else return a; + \\ if (a < b) {} else {} + \\} + }); + + cases.add("if statements", + \\int foo() { + \\ if (2) { + \\ int a = 2; + \\ } + \\ if (2, 5) { + \\ int a = 2; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ if (@as(c_int, 2) != 0) { + \\ var a: c_int = 2; + \\ } + \\ if ((blk: { + \\ _ = @as(c_int, 2); + \\ break :blk @as(c_int, 5); + \\ }) != 0) { + \\ var a: c_int = 2; + \\ } + \\} + }); cases.add("if on non-bool", \\enum SomeEnum { A, B, C }; @@ -1551,147 +1972,357 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ if (d) return 3; \\ return 4; \\} - , - \\pub const A = enum_SomeEnum.A; - \\pub const B = enum_SomeEnum.B; - \\pub const C = enum_SomeEnum.C; + , &[_][]const u8{ \\pub const enum_SomeEnum = extern enum { \\ A, \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int { + \\ var a = arg_a; + \\ var b = arg_b; + \\ var c = arg_c; + \\ var d = arg_d; \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; - \\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3; + \\ if (d != 0) return 3; \\ return 4; \\} - ); + }); - cases.add("while on non-bool", - \\int while_none_bool(int a, float b, void *c) { - \\ while (a) return 0; - \\ while (b) return 1; - \\ while (c) return 2; - \\ return 3; - \\} - , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; - \\} - ); + cases.add("simple data types", + \\#include + \\int foo(char a, unsigned char b, signed char c); + \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype + \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d); + \\void baz(int8_t a, int16_t b, int32_t c, int64_t d); + , &[_][]const u8{ + \\pub extern fn foo(a: u8, b: u8, c: i8) c_int; + \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void; + \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void; + }); - cases.add("for on non-bool", - \\int for_none_bool(int a, float b, void *c) { - \\ for (;a;) return 0; - \\ for (;b;) return 1; - \\ for (;c;) return 2; - \\ return 3; + cases.add("simple function", + \\int abs(int a) { + \\ return a < 0 ? -a : a; \\} - , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; + , &[_][]const u8{ + \\pub export fn abs(arg_a: c_int) c_int { + \\ var a = arg_a; + \\ return if (a < @as(c_int, 0)) -a else a; \\} - ); + }); - cases.add("switch on int", - \\int switch_fn(int i) { - \\ int res = 0; - \\ switch (i) { - \\ case 0: - \\ res = 1; - \\ case 1: - \\ res = 2; - \\ default: - \\ res = 3 * i; - \\ break; - \\ case 2: - \\ res = 5; + cases.add("post increment", + \\unsigned foo1(unsigned a) { + \\ a++; + \\ return a; + \\} + \\int foo2(int a) { + \\ a++; + \\ return a; + \\} + \\int *foo3(int *a) { + \\ a++; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn foo1(arg_a: c_uint) c_uint { + \\ var a = arg_a; + \\ a +%= 1; + \\ return a; + \\} + \\pub export fn foo2(arg_a: c_int) c_int { + \\ var a = arg_a; + \\ a += 1; + \\ return a; + \\} + \\pub export fn foo3(arg_a: [*c]c_int) [*c]c_int { + \\ var a = arg_a; + \\ a += 1; + \\ return a; + \\} + }); + + cases.add("deref function pointer", + \\void foo(void) {} + \\int baz(void) { return 0; } + \\void bar(void) { + \\ void(*f)(void) = foo; + \\ int(*b)(void) = baz; + \\ f(); + \\ (*(f))(); + \\ foo(); + \\ b(); + \\ (*(b))(); + \\ baz(); + \\} + , &[_][]const u8{ + \\pub export fn foo() void {} + \\pub export fn baz() c_int { + \\ return 0; + \\} + \\pub export fn bar() void { + \\ var f: ?fn () callconv(.C) void = foo; + \\ var b: ?fn () callconv(.C) c_int = baz; + \\ f.?(); + \\ (f).?(); + \\ foo(); + \\ _ = b.?(); + \\ _ = (b).?(); + \\ _ = baz(); + \\} + }); + + cases.add("pre increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ ++i; + \\ --i; + \\ ++u; + \\ --u; + \\ i = ++i; + \\ i = --i; + \\ u = ++u; + \\ u = --u; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = (blk: { + \\ const ref = &i; + \\ ref.* += 1; + \\ break :blk ref.*; + \\ }); + \\ i = (blk: { + \\ const ref = &i; + \\ ref.* -= 1; + \\ break :blk ref.*; + \\ }); + \\ u = (blk: { + \\ const ref = &u; + \\ ref.* +%= 1; + \\ break :blk ref.*; + \\ }); + \\ u = (blk: { + \\ const ref = &u; + \\ ref.* -%= 1; + \\ break :blk ref.*; + \\ }); + \\} + }); + + cases.add("shift right assign", + \\int log2(unsigned a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; \\ } + \\ return i; \\} - , - \\pub fn switch_fn(i: c_int) c_int { - \\ var res: c_int = 0; - \\ __switch: { - \\ __case_2: { - \\ __default: { - \\ __case_1: { - \\ __case_0: { - \\ switch (i) { - \\ 0 => break :__case_0, - \\ 1 => break :__case_1, - \\ else => break :__default, - \\ 2 => break :__case_2, - \\ } - \\ } - \\ res = 1; - \\ } - \\ res = 2; - \\ } - \\ res = (3 * i); - \\ break :__switch; - \\ } - \\ res = 5; + , &[_][]const u8{ + \\pub export fn log2(arg_a: c_uint) c_int { + \\ var a = arg_a; + \\ var i: c_int = 0; + \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { + \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1); \\ } + \\ return i; \\} - ); + }); - cases.addC( - "u integer suffix after 0 (zero) in macro definition", - "#define ZERO 0U", - "pub const ZERO = c_uint(0);", - ); + cases.add("shift right assign with a fixed size type", + \\#include + \\int log2(uint32_t a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; + \\ } + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn log2(arg_a: u32) c_int { + \\ var a = arg_a; + \\ var i: c_int = 0; + \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { + \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1); + \\ } + \\ return i; + \\} + }); - cases.addC( - "l integer suffix after 0 (zero) in macro definition", - "#define ZERO 0L", - "pub const ZERO = c_long(0);", - ); + cases.add("compound assignment operators", + \\void foo(void) { + \\ int a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 0; + \\ a += (blk: { + \\ const ref = &a; + \\ ref.* = ref.* + @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a -= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* - @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a *= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* * @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a &= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* & @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a |= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* | @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a ^= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* ^ @as(c_int, 1); + \\ break :blk ref.*; + \\ }); + \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: { + \\ const ref = &a; + \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\ a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: { + \\ const ref = &a; + \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\} + }); - cases.addC( - "ul integer suffix after 0 (zero) in macro definition", - "#define ZERO 0UL", - "pub const ZERO = c_ulong(0);", - ); + cases.add("compound assignment operators unsigned", + \\void foo(void) { + \\ unsigned a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ a +%= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a -%= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a *%= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a &= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a |= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a ^= (blk: { + \\ const ref = &a; + \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: { + \\ const ref = &a; + \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: { + \\ const ref = &a; + \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\} + }); - cases.addC( - "lu integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LU", - "pub const ZERO = c_ulong(0);", - ); + cases.add("post increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ i++; + \\ i--; + \\ u++; + \\ u--; + \\ i = i++; + \\ i = i--; + \\ u = u++; + \\ u = u--; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = (blk: { + \\ const ref = &i; + \\ const tmp = ref.*; + \\ ref.* += 1; + \\ break :blk tmp; + \\ }); + \\ i = (blk: { + \\ const ref = &i; + \\ const tmp = ref.*; + \\ ref.* -= 1; + \\ break :blk tmp; + \\ }); + \\ u = (blk: { + \\ const ref = &u; + \\ const tmp = ref.*; + \\ ref.* +%= 1; + \\ break :blk tmp; + \\ }); + \\ u = (blk: { + \\ const ref = &u; + \\ const tmp = ref.*; + \\ ref.* -%= 1; + \\ break :blk tmp; + \\ }); + \\} + }); - cases.addC( - "ll integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LL", - "pub const ZERO = c_longlong(0);", - ); - - cases.addC( - "ull integer suffix after 0 (zero) in macro definition", - "#define ZERO 0ULL", - "pub const ZERO = c_ulonglong(0);", - ); - - cases.addC( - "llu integer suffix after 0 (zero) in macro definition", - "#define ZERO 0LLU", - "pub const ZERO = c_ulonglong(0);", - ); - - cases.addC( - "bitwise not on u-suffixed 0 (zero) in macro definition", - "#define NOT_ZERO (~0U)", - "pub const NOT_ZERO = ~c_uint(0);", - ); - - cases.addC("implicit casts", + cases.add("implicit casts", \\#include \\ \\void fn_int(int x); @@ -1701,7 +2332,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\void fn_bool(bool x); \\void fn_ptr(void *x); \\ - \\void call(int q) { + \\void call() { \\ fn_int(3.0f); \\ fn_int(3.0); \\ fn_int(3.0L); @@ -1719,107 +2350,233 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ fn_int(&fn_int); \\ fn_ptr(42); \\} - , + , &[_][]const u8{ \\pub extern fn fn_int(x: c_int) void; \\pub extern fn fn_f32(x: f32) void; \\pub extern fn fn_f64(x: f64) void; \\pub extern fn fn_char(x: u8) void; \\pub extern fn fn_bool(x: bool) void; \\pub extern fn fn_ptr(x: ?*c_void) void; - \\pub export fn call(q: c_int) void { - \\ fn_int(@floatToInt(c_int, 3.000000)); - \\ fn_int(@floatToInt(c_int, 3.000000)); - \\ fn_int(@floatToInt(c_int, 3.000000)); - \\ fn_int(1094861636); - \\ fn_f32(@intToFloat(f32, 3)); - \\ fn_f64(@intToFloat(f64, 3)); - \\ fn_char(u8('3')); - \\ fn_char(u8('\x01')); - \\ fn_char(u8(0)); - \\ fn_f32(3.000000); - \\ fn_f64(3.000000); - \\ fn_bool(true); - \\ fn_bool(false); + \\pub export fn call() void { + \\ fn_int(@floatToInt(c_int, 3)); + \\ fn_int(@floatToInt(c_int, 3)); + \\ fn_int(@floatToInt(c_int, 3)); + \\ fn_int(@as(c_int, 1094861636)); + \\ fn_f32(@intToFloat(f32, @as(c_int, 3))); + \\ fn_f64(@intToFloat(f64, @as(c_int, 3))); + \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3')))); + \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01')))); + \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0)))); + \\ fn_f32(3); + \\ fn_f64(3); + \\ fn_bool(@as(c_int, 123) != 0); + \\ fn_bool(@as(c_int, 0) != 0); \\ fn_bool(@ptrToInt(&fn_int) != 0); \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int))); - \\ fn_ptr(@intToPtr(?*c_void, 42)); + \\ fn_ptr(@intToPtr(?*c_void, @as(c_int, 42))); \\} - ); + }); - cases.addC("pointer conversion with different alignment", - \\void test_ptr_cast() { - \\ void *p; - \\ { - \\ char *to_char = (char *)p; - \\ short *to_short = (short *)p; - \\ int *to_int = (int *)p; - \\ long long *to_longlong = (long long *)p; - \\ } - \\ { - \\ char *to_char = p; - \\ short *to_short = p; - \\ int *to_int = p; - \\ long long *to_longlong = p; - \\ } + cases.add("function call", + \\static void bar(void) { } + \\void foo(int *(baz)(void)) { + \\ bar(); + \\ baz(); + \\} + , &[_][]const u8{ + \\pub fn bar() callconv(.C) void {} + \\pub export fn foo(arg_baz: ?fn () callconv(.C) [*c]c_int) void { + \\ var baz = arg_baz; + \\ bar(); + \\ _ = baz.?(); + \\} + }); + + cases.add("macro defines string literal with octal", + \\#define FOO "aoeu\023 derp" + \\#define FOO2 "aoeu\0234 derp" + \\#define FOO_CHAR '\077' + , &[_][]const u8{ + \\pub const FOO = "aoeu\x13 derp"; + , + \\pub const FOO2 = "aoeu\x134 derp"; + , + \\pub const FOO_CHAR = '\x3f'; + }); + + cases.add("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const FooA = @enumToInt(enum_Foo.A); + \\pub const FooB = @enumToInt(enum_Foo.B); + \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const Foo = enum_Foo; + }); + + cases.add("macro cast", + \\#define FOO(bar) baz((void *)(baz)) + , &[_][]const u8{ + \\pub inline fn FOO(bar: var) @TypeOf(baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz))) { + \\ return baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz)); + \\} + }); + + cases.add("macro conditional operator", + \\#define FOO a ? b : c + , &[_][]const u8{ + \\pub const FOO = if (a) b else c; + }); + + cases.add("do while as expr", + \\static void foo(void) { + \\ if (1) + \\ do {} while (0); + \\} + , &[_][]const u8{ + \\pub fn foo() callconv(.C) void { + \\ if (@as(c_int, 1) != 0) while (true) { + \\ if (!(@as(c_int, 0) != 0)) break; + \\ }; + \\} + }); + + cases.add("macro comparisions", + \\#define MIN(a, b) ((b) < (a) ? (b) : (a)) + \\#define MAX(a, b) ((b) > (a) ? (b) : (a)) + , &[_][]const u8{ + \\pub inline fn MIN(a: var, b: var) @TypeOf(if (b < a) b else a) { + \\ return if (b < a) b else a; \\} , - \\pub export fn test_ptr_cast() void { - \\ var p: ?*c_void = undefined; - \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); - \\ } - \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); - \\ } + \\pub inline fn MAX(a: var, b: var) @TypeOf(if (b > a) b else a) { + \\ return if (b > a) b else a; \\} - ); + }); - cases.addC("escape sequences", - \\const char *escapes() { - \\char a = '\'', - \\ b = '\\', - \\ c = '\a', - \\ d = '\b', - \\ e = '\f', - \\ f = '\n', - \\ g = '\r', - \\ h = '\t', - \\ i = '\v', - \\ j = '\0', - \\ k = '\"'; - \\ return "\'\\\a\b\f\n\r\t\v\0\""; + // TODO: detect to use different block labels here + cases.add("nested assignment", + \\int foo(int *p, int x) { + \\ return *p++ = x; \\} - \\ - , - \\pub export fn escapes() [*c]const u8 { - \\ var a: u8 = u8('\''); - \\ var b: u8 = u8('\\'); - \\ var c: u8 = u8('\x07'); - \\ var d: u8 = u8('\x08'); - \\ var e: u8 = u8('\x0c'); - \\ var f: u8 = u8('\n'); - \\ var g: u8 = u8('\r'); - \\ var h: u8 = u8('\t'); - \\ var i: u8 = u8('\x0b'); - \\ var j: u8 = u8('\x00'); - \\ var k: u8 = u8('\"'); - \\ return c"\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; + , &[_][]const u8{ + \\pub export fn foo(arg_p: [*c]c_int, arg_x: c_int) c_int { + \\ var p = arg_p; + \\ var x = arg_x; + \\ return blk: { + \\ const tmp = x; + \\ (blk: { + \\ const ref = &p; + \\ const tmp_1 = ref.*; + \\ ref.* += 1; + \\ break :blk tmp_1; + \\ }).?.* = tmp; + \\ break :blk tmp; + \\ }; \\} - \\ - ); + }); - /////////////// Cases for only stage1 because stage2 behavior is better //////////////// - cases.addC("Parameterless function prototypes", - \\void foo() {} - \\void bar(void) {} + cases.add("widening and truncating integer casting to different signedness", + \\unsigned long foo(void) { + \\ return -1; + \\} + \\unsigned short bar(long x) { + \\ return x; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_ulong { + \\ return @bitCast(c_ulong, @as(c_long, -@as(c_int, 1))); + \\} + \\pub export fn bar(arg_x: c_long) c_ushort { + \\ var x = arg_x; + \\ return @bitCast(c_ushort, @truncate(c_short, x)); + \\} + }); + + cases.add("arg name aliasing decl which comes after", + \\int foo(int bar) { + \\ bar = 2; + \\} + \\int bar = 4; + , &[_][]const u8{ + \\pub export fn foo(arg_bar_1: c_int) c_int { + \\ var bar_1 = arg_bar_1; + \\ bar_1 = 2; + \\} + \\pub export var bar: c_int = 4; + }); + + cases.add("arg name aliasing macro which comes after", + \\int foo(int bar) { + \\ bar = 2; + \\} + \\#define bar 4 + , &[_][]const u8{ + \\pub export fn foo(arg_bar_1: c_int) c_int { + \\ var bar_1 = arg_bar_1; + \\ bar_1 = 2; + \\} , - \\pub export fn foo() void {} - \\pub export fn bar() void {} - ); + \\pub const bar = 4; + }); + + cases.add("don't export inline functions", + \\inline void a(void) {} + \\static void b(void) {} + \\void c(void) {} + \\static void foo() {} + , &[_][]const u8{ + \\pub fn a() callconv(.C) void {} + \\pub fn b() callconv(.C) void {} + \\pub export fn c() void {} + \\pub fn foo() callconv(.C) void {} + }); + + cases.add("casting away const and volatile", + \\void foo(int *a) {} + \\void bar(const int *a) { + \\ foo((int *)a); + \\} + \\void baz(volatile int *a) { + \\ foo((int *)a); + \\} + , &[_][]const u8{ + \\pub export fn foo(arg_a: [*c]c_int) void { + \\ var a = arg_a; + \\} + \\pub export fn bar(arg_a: [*c]const c_int) void { + \\ var a = arg_a; + \\ foo(@intToPtr([*c]c_int, @ptrToInt(a))); + \\} + \\pub export fn baz(arg_a: [*c]volatile c_int) void { + \\ var a = arg_a; + \\ foo(@intToPtr([*c]c_int, @ptrToInt(a))); + \\} + }); + + cases.add("handling of _Bool type", + \\_Bool foo(_Bool x) { + \\ _Bool a = x != 1; + \\ _Bool b = a != 0; + \\ _Bool c = foo; + \\ return foo(c != b); + \\} + , &[_][]const u8{ + \\pub export fn foo(arg_x: bool) bool { + \\ var x = arg_x; + \\ var a: bool = (@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(x)))) != @as(c_int, 1)); + \\ var b: bool = (@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(a)))) != @as(c_int, 0)); + \\ var c: bool = @ptrToInt(foo) != 0; + \\ return foo((@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(c)))) != @intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(b)))))); + \\} + }); } diff --git a/tools/merge_anal_dumps.zig b/tools/merge_anal_dumps.zig new file mode 100644 index 000000000..e0e71696d --- /dev/null +++ b/tools/merge_anal_dumps.zig @@ -0,0 +1,454 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const json = std.json; +const mem = std.mem; +const fieldIndex = std.meta.fieldIndex; +const TypeId = builtin.TypeId; + +pub fn main() anyerror!void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const allocator = &arena.allocator; + + const args = try std.process.argsAlloc(allocator); + + var parser: json.Parser = undefined; + var dump = Dump.init(allocator); + for (args[1..]) |arg| { + parser = json.Parser.init(allocator, false); + const json_text = try std.io.readFileAlloc(allocator, arg); + const tree = try parser.parse(json_text); + try dump.mergeJson(tree.root); + } + + const stdout = try std.io.getStdOut(); + try dump.render(&stdout.outStream().stream); +} + +/// AST source node +const Node = struct { + file: usize, + line: usize, + col: usize, + fields: []usize, + + fn hash(n: Node) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, n.file); + std.hash.autoHash(&hasher, n.line); + std.hash.autoHash(&hasher, n.col); + return @truncate(u32, hasher.final()); + } + + fn eql(a: Node, b: Node) bool { + return a.file == b.file and + a.line == b.line and + a.col == b.col; + } +}; + +const Error = struct { + src: usize, + name: []const u8, + + fn hash(n: Error) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, n.src); + return @truncate(u32, hasher.final()); + } + + fn eql(a: Error, b: Error) bool { + return a.src == b.src; + } +}; + +const simple_types = [_][]const u8{ + "Type", + "Void", + "Bool", + "NoReturn", + "ComptimeFloat", + "ComptimeInt", + "Undefined", + "Null", + "AnyFrame", + "EnumLiteral", +}; + +const Type = union(builtin.TypeId) { + Type, + Void, + Bool, + NoReturn, + ComptimeFloat, + ComptimeInt, + Undefined, + Null, + AnyFrame, + EnumLiteral, + + Int: Int, + Float: usize, // bits + + Vector: Array, + Optional: usize, // payload type index + Pointer: Pointer, + Array: Array, + + Struct, // TODO + ErrorUnion, // TODO + ErrorSet, // TODO + Enum, // TODO + Union, // TODO + Fn, // TODO + BoundFn, // TODO + ArgTuple, // TODO + Opaque, // TODO + Frame, // TODO + + const Int = struct { + bits: usize, + signed: bool, + }; + + const Pointer = struct { + elem: usize, + alignment: usize, + is_const: bool, + is_volatile: bool, + allow_zero: bool, + host_int_bytes: usize, + bit_offset_in_host: usize, + }; + + const Array = struct { + elem: usize, + len: usize, + }; + + fn hash(t: Type) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, builtin.TypeId(t)); + return @truncate(u32, hasher.final()); + } + + fn eql(a: Type, b: Type) bool { + return std.meta.eql(a, b); + } +}; + +const Dump = struct { + zig_id: ?[]const u8 = null, + zig_version: ?[]const u8 = null, + root_name: ?[]const u8 = null, + targets: std.ArrayList([]const u8), + + const FileMap = std.StringHashMap(usize); + file_list: std.ArrayList([]const u8), + file_map: FileMap, + + const NodeMap = std.HashMap(Node, usize, Node.hash, Node.eql); + node_list: std.ArrayList(Node), + node_map: NodeMap, + + const ErrorMap = std.HashMap(Error, usize, Error.hash, Error.eql); + error_list: std.ArrayList(Error), + error_map: ErrorMap, + + const TypeMap = std.HashMap(Type, usize, Type.hash, Type.eql); + type_list: std.ArrayList(Type), + type_map: TypeMap, + + fn init(allocator: *mem.Allocator) Dump { + return Dump{ + .targets = std.ArrayList([]const u8).init(allocator), + .file_list = std.ArrayList([]const u8).init(allocator), + .file_map = FileMap.init(allocator), + .node_list = std.ArrayList(Node).init(allocator), + .node_map = NodeMap.init(allocator), + .error_list = std.ArrayList(Error).init(allocator), + .error_map = ErrorMap.init(allocator), + .type_list = std.ArrayList(Type).init(allocator), + .type_map = TypeMap.init(allocator), + }; + } + + fn mergeJson(self: *Dump, root: json.Value) !void { + const params = &root.Object.get("params").?.value.Object; + const zig_id = params.get("zigId").?.value.String; + const zig_version = params.get("zigVersion").?.value.String; + const root_name = params.get("rootName").?.value.String; + try mergeSameStrings(&self.zig_id, zig_id); + try mergeSameStrings(&self.zig_version, zig_version); + try mergeSameStrings(&self.root_name, root_name); + + for (params.get("builds").?.value.Array.toSliceConst()) |json_build| { + const target = json_build.Object.get("target").?.value.String; + try self.targets.append(target); + } + + // Merge files. If the string matches, it's the same file. + const other_files = root.Object.get("files").?.value.Array.toSliceConst(); + var other_file_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_files) |other_file, i| { + const gop = try self.file_map.getOrPut(other_file.String); + if (!gop.found_existing) { + gop.kv.value = self.file_list.len; + try self.file_list.append(other_file.String); + } + try other_file_to_mine.putNoClobber(i, gop.kv.value); + } + + // Merge AST nodes. If the file id, line, and column all match, it's the same AST node. + const other_ast_nodes = root.Object.get("astNodes").?.value.Array.toSliceConst(); + var other_ast_node_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_ast_nodes) |other_ast_node_json, i| { + const other_file_id = jsonObjInt(other_ast_node_json, "file"); + const other_node = Node{ + .line = jsonObjInt(other_ast_node_json, "line"), + .col = jsonObjInt(other_ast_node_json, "col"), + .file = other_file_to_mine.getValue(other_file_id).?, + .fields = ([*]usize)(undefined)[0..0], + }; + const gop = try self.node_map.getOrPut(other_node); + if (!gop.found_existing) { + gop.kv.value = self.node_list.len; + try self.node_list.append(other_node); + } + try other_ast_node_to_mine.putNoClobber(i, gop.kv.value); + } + // convert fields lists + for (other_ast_nodes) |other_ast_node_json, i| { + const my_node_index = other_ast_node_to_mine.get(i).?.value; + const my_node = &self.node_list.toSlice()[my_node_index]; + if (other_ast_node_json.Object.get("fields")) |fields_json_kv| { + const other_fields = fields_json_kv.value.Array.toSliceConst(); + my_node.fields = try self.a().alloc(usize, other_fields.len); + for (other_fields) |other_field_index, field_i| { + const other_index = @intCast(usize, other_field_index.Integer); + my_node.fields[field_i] = other_ast_node_to_mine.get(other_index).?.value; + } + } + } + + // Merge errors. If the AST Node matches, it's the same error value. + const other_errors = root.Object.get("errors").?.value.Array.toSliceConst(); + var other_error_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_errors) |other_error_json, i| { + const other_src_id = jsonObjInt(other_error_json, "src"); + const other_error = Error{ + .src = other_ast_node_to_mine.getValue(other_src_id).?, + .name = other_error_json.Object.get("name").?.value.String, + }; + const gop = try self.error_map.getOrPut(other_error); + if (!gop.found_existing) { + gop.kv.value = self.error_list.len; + try self.error_list.append(other_error); + } + try other_error_to_mine.putNoClobber(i, gop.kv.value); + } + + // Merge types. Now it starts to get advanced. + // First we identify all the simple types and merge those. + // Example: void, type, noreturn + // We can also do integers and floats. + const other_types = root.Object.get("types").?.value.Array.toSliceConst(); + var other_types_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_types) |other_type_json, i| { + const type_kind = jsonObjInt(other_type_json, "kind"); + switch (type_kind) { + fieldIndex(TypeId, "Int").? => { + var signed: bool = undefined; + var bits: usize = undefined; + if (other_type_json.Object.get("i")) |kv| { + signed = true; + bits = @intCast(usize, kv.value.Integer); + } else if (other_type_json.Object.get("u")) |kv| { + signed = false; + bits = @intCast(usize, kv.value.Integer); + } else { + unreachable; + } + const other_type = Type{ + .Int = Type.Int{ + .bits = bits, + .signed = signed, + }, + }; + try self.mergeOtherType(other_type, i, &other_types_to_mine); + }, + fieldIndex(TypeId, "Float").? => { + const other_type = Type{ + .Float = jsonObjInt(other_type_json, "bits"), + }; + try self.mergeOtherType(other_type, i, &other_types_to_mine); + }, + else => {}, + } + + inline for (simple_types) |simple_type_name| { + if (type_kind == std.meta.fieldIndex(builtin.TypeId, simple_type_name).?) { + const other_type = @unionInit(Type, simple_type_name, {}); + try self.mergeOtherType(other_type, i, &other_types_to_mine); + } + } + } + } + + fn mergeOtherType( + self: *Dump, + other_type: Type, + other_type_index: usize, + other_types_to_mine: *std.AutoHashMap(usize, usize), + ) !void { + const gop = try self.type_map.getOrPut(other_type); + if (!gop.found_existing) { + gop.kv.value = self.type_list.len; + try self.type_list.append(other_type); + } + try other_types_to_mine.putNoClobber(other_type_index, gop.kv.value); + } + + fn render(self: *Dump, stream: var) !void { + var jw = json.WriteStream(@TypeOf(stream).Child, 10).init(stream); + try jw.beginObject(); + + try jw.objectField("typeKinds"); + try jw.beginArray(); + inline for (@typeInfo(builtin.TypeId).Enum.fields) |field| { + try jw.arrayElem(); + try jw.emitString(field.name); + } + try jw.endArray(); + + try jw.objectField("params"); + try jw.beginObject(); + + try jw.objectField("zigId"); + try jw.emitString(self.zig_id.?); + + try jw.objectField("zigVersion"); + try jw.emitString(self.zig_version.?); + + try jw.objectField("rootName"); + try jw.emitString(self.root_name.?); + + try jw.objectField("builds"); + try jw.beginArray(); + for (self.targets.toSliceConst()) |target| { + try jw.arrayElem(); + try jw.beginObject(); + try jw.objectField("target"); + try jw.emitString(target); + try jw.endObject(); + } + try jw.endArray(); + + try jw.endObject(); + + try jw.objectField("types"); + try jw.beginArray(); + for (self.type_list.toSliceConst()) |t| { + try jw.arrayElem(); + try jw.beginObject(); + + try jw.objectField("kind"); + try jw.emitNumber(@enumToInt(builtin.TypeId(t))); + + switch (t) { + .Int => |int| { + if (int.signed) { + try jw.objectField("i"); + } else { + try jw.objectField("u"); + } + try jw.emitNumber(int.bits); + }, + .Float => |bits| { + try jw.objectField("bits"); + try jw.emitNumber(bits); + }, + + else => {}, + } + + try jw.endObject(); + } + try jw.endArray(); + + try jw.objectField("errors"); + try jw.beginArray(); + for (self.error_list.toSliceConst()) |zig_error| { + try jw.arrayElem(); + try jw.beginObject(); + + try jw.objectField("src"); + try jw.emitNumber(zig_error.src); + + try jw.objectField("name"); + try jw.emitString(zig_error.name); + + try jw.endObject(); + } + try jw.endArray(); + + try jw.objectField("astNodes"); + try jw.beginArray(); + for (self.node_list.toSliceConst()) |node| { + try jw.arrayElem(); + try jw.beginObject(); + + try jw.objectField("file"); + try jw.emitNumber(node.file); + + try jw.objectField("line"); + try jw.emitNumber(node.line); + + try jw.objectField("col"); + try jw.emitNumber(node.col); + + if (node.fields.len != 0) { + try jw.objectField("fields"); + try jw.beginArray(); + + for (node.fields) |field_node_index| { + try jw.arrayElem(); + try jw.emitNumber(field_node_index); + } + try jw.endArray(); + } + + try jw.endObject(); + } + try jw.endArray(); + + try jw.objectField("files"); + try jw.beginArray(); + for (self.file_list.toSliceConst()) |file| { + try jw.arrayElem(); + try jw.emitString(file); + } + try jw.endArray(); + + try jw.endObject(); + } + + fn a(self: Dump) *mem.Allocator { + return self.targets.allocator; + } + + fn mergeSameStrings(opt_dest: *?[]const u8, src: []const u8) !void { + if (opt_dest.*) |dest| { + if (!mem.eql(u8, dest, src)) + return error.MismatchedDumps; + } else { + opt_dest.* = src; + } + } +}; + +fn jsonObjInt(json_val: json.Value, field: []const u8) usize { + const uncasted = json_val.Object.get(field).?.value.Integer; + return @intCast(usize, uncasted); +} diff --git a/tools/process_headers.zig b/tools/process_headers.zig index 420c118cb..9952147aa 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -56,113 +56,113 @@ const MultiAbi = union(enum) { const glibc_targets = [_]LibCTarget{ LibCTarget{ .name = "aarch64_be-linux-gnu", - .arch = MultiArch {.specific = Arch.aarch64_be}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.aarch64_be }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "aarch64-linux-gnu", - .arch = MultiArch {.specific = Arch.aarch64}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.aarch64 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "armeb-linux-gnueabi", - .arch = MultiArch {.specific = Arch.armeb}, - .abi = MultiAbi {.specific = Abi.gnueabi}, + .arch = MultiArch{ .specific = Arch.armeb }, + .abi = MultiAbi{ .specific = Abi.gnueabi }, }, LibCTarget{ .name = "armeb-linux-gnueabihf", - .arch = MultiArch {.specific = Arch.armeb}, - .abi = MultiAbi {.specific = Abi.gnueabihf}, + .arch = MultiArch{ .specific = Arch.armeb }, + .abi = MultiAbi{ .specific = Abi.gnueabihf }, }, LibCTarget{ .name = "arm-linux-gnueabi", - .arch = MultiArch {.specific = Arch.arm}, - .abi = MultiAbi {.specific = Abi.gnueabi}, + .arch = MultiArch{ .specific = Arch.arm }, + .abi = MultiAbi{ .specific = Abi.gnueabi }, }, LibCTarget{ .name = "arm-linux-gnueabihf", - .arch = MultiArch {.specific = Arch.arm}, - .abi = MultiAbi {.specific = Abi.gnueabihf}, + .arch = MultiArch{ .specific = Arch.arm }, + .abi = MultiAbi{ .specific = Abi.gnueabihf }, }, LibCTarget{ .name = "i686-linux-gnu", - .arch = MultiArch {.specific = Arch.i386}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.i386 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "mips64el-linux-gnu-n32", - .arch = MultiArch {.specific = Arch.mips64el}, - .abi = MultiAbi {.specific = Abi.gnuabin32}, + .arch = MultiArch{ .specific = Arch.mips64el }, + .abi = MultiAbi{ .specific = Abi.gnuabin32 }, }, LibCTarget{ .name = "mips64el-linux-gnu-n64", - .arch = MultiArch {.specific = Arch.mips64el}, - .abi = MultiAbi {.specific = Abi.gnuabi64}, + .arch = MultiArch{ .specific = Arch.mips64el }, + .abi = MultiAbi{ .specific = Abi.gnuabi64 }, }, LibCTarget{ .name = "mips64-linux-gnu-n32", - .arch = MultiArch {.specific = Arch.mips64}, - .abi = MultiAbi {.specific = Abi.gnuabin32}, + .arch = MultiArch{ .specific = Arch.mips64 }, + .abi = MultiAbi{ .specific = Abi.gnuabin32 }, }, LibCTarget{ .name = "mips64-linux-gnu-n64", - .arch = MultiArch {.specific = Arch.mips64}, - .abi = MultiAbi {.specific = Abi.gnuabi64}, + .arch = MultiArch{ .specific = Arch.mips64 }, + .abi = MultiAbi{ .specific = Abi.gnuabi64 }, }, LibCTarget{ .name = "mipsel-linux-gnu", - .arch = MultiArch {.specific = Arch.mipsel}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.mipsel }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "mips-linux-gnu", - .arch = MultiArch {.specific = Arch.mips}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.mips }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "powerpc64le-linux-gnu", - .arch = MultiArch {.specific = Arch.powerpc64le}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.powerpc64le }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "powerpc64-linux-gnu", - .arch = MultiArch {.specific = Arch.powerpc64}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.powerpc64 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "powerpc-linux-gnu", - .arch = MultiArch {.specific = Arch.powerpc}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.powerpc }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "riscv64-linux-gnu-rv64imac-lp64", - .arch = MultiArch {.specific = Arch.riscv64}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.riscv64 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "s390x-linux-gnu", - .arch = MultiArch {.specific = Arch.s390x}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.s390x }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "sparc64-linux-gnu", - .arch = MultiArch {.specific = Arch.sparc}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.sparc }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "sparcv9-linux-gnu", - .arch = MultiArch {.specific = Arch.sparcv9}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.sparcv9 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "x86_64-linux-gnu", - .arch = MultiArch {.specific = Arch.x86_64}, - .abi = MultiAbi {.specific = Abi.gnu}, + .arch = MultiArch{ .specific = Arch.x86_64 }, + .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "x86_64-linux-gnu-x32", - .arch = MultiArch {.specific = Arch.x86_64}, - .abi = MultiAbi {.specific = Abi.gnux32}, + .arch = MultiArch{ .specific = Arch.x86_64 }, + .abi = MultiAbi{ .specific = Abi.gnux32 }, }, }; @@ -226,8 +226,8 @@ const DestTarget = struct { fn hash(a: DestTarget) u32 { return @enumToInt(a.arch) +% - (@enumToInt(a.os) *% u32(4202347608)) +% - (@enumToInt(a.abi) *% u32(4082223418)); + (@enumToInt(a.os) *% @as(u32, 4202347608)) +% + (@enumToInt(a.abi) *% @as(u32, 4082223418)); } fn eql(a: DestTarget, b: DestTarget) bool { @@ -258,7 +258,7 @@ const LibCVendor = enum { }; pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); const allocator = &arena.allocator; const args = try std.process.argsAlloc(allocator); var search_paths = std.ArrayList([]const u8).init(allocator); @@ -270,7 +270,7 @@ pub fn main() !void { if (std.mem.eql(u8, args[arg_i], "--help")) usageAndExit(args[0]); if (arg_i + 1 >= args.len) { - std.debug.warn("expected argument after '{}'\n", args[arg_i]); + std.debug.warn("expected argument after '{}'\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -283,7 +283,7 @@ pub fn main() !void { assert(opt_abi == null); opt_abi = args[arg_i + 1]; } else { - std.debug.warn("unrecognized argument: {}\n", args[arg_i]); + std.debug.warn("unrecognized argument: {}\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -297,10 +297,10 @@ pub fn main() !void { else if (std.mem.eql(u8, abi_name, "glibc")) LibCVendor.glibc else { - std.debug.warn("unrecognized C ABI: {}\n", abi_name); + std.debug.warn("unrecognized C ABI: {}\n", .{abi_name}); usageAndExit(args[0]); }; - const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", abi_name); + const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name}); // TODO compiler crashed when I wrote this the canonical way var libc_targets: []const LibCTarget = undefined; @@ -340,7 +340,7 @@ pub fn main() !void { try dir_stack.append(target_include_dir); while (dir_stack.popOrNull()) |full_dir_name| { - var dir = std.fs.Dir.open(allocator, full_dir_name) catch |err| switch (err) { + var dir = std.fs.Dir.cwd().openDirList(full_dir_name) catch |err| switch (err) { error.FileNotFound => continue :search, error.AccessDenied => continue :search, else => return err, @@ -365,12 +365,11 @@ pub fn main() !void { if (gop.found_existing) { max_bytes_saved += raw_bytes.len; gop.kv.value.hit_count += 1; - std.debug.warn( - "duplicate: {} {} ({Bi:2})\n", + std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{ libc_target.name, rel_path, raw_bytes.len, - ); + }); } else { gop.kv.value = Contents{ .bytes = trimmed, @@ -388,16 +387,16 @@ pub fn main() !void { }; assert((try target_to_hash.put(dest_target, hash)) == null); }, - else => std.debug.warn("warning: weird file: {}\n", full_path), + else => std.debug.warn("warning: weird file: {}\n", .{full_path}), } } } break; } else { - std.debug.warn("warning: libc target not found: {}\n", libc_target.name); + std.debug.warn("warning: libc target not found: {}\n", .{libc_target.name}); } } - std.debug.warn("summary: {Bi:2} could be reduced to {Bi:2}\n", total_bytes, total_bytes - max_bytes_saved); + std.debug.warn("summary: {Bi:2} could be reduced to {Bi:2}\n", .{total_bytes, total_bytes - max_bytes_saved}); try std.fs.makePath(allocator, out_dir); var missed_opportunity_bytes: usize = 0; @@ -426,7 +425,7 @@ pub fn main() !void { if (contender.hit_count > 1) { const this_missed_bytes = contender.hit_count * contender.bytes.len; missed_opportunity_bytes += this_missed_bytes; - std.debug.warn("Missed opportunity ({Bi:2}): {}\n", this_missed_bytes, path_kv.key); + std.debug.warn("Missed opportunity ({Bi:2}): {}\n", .{this_missed_bytes, path_kv.key}); } else break; } } @@ -440,13 +439,11 @@ pub fn main() !void { .specific => |a| @tagName(a), else => @tagName(dest_target.arch), }; - const out_subpath = try std.fmt.allocPrint( - allocator, - "{}-{}-{}", + const out_subpath = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ arch_name, @tagName(dest_target.os), @tagName(dest_target.abi), - ); + }); const full_path = try std.fs.path.join(allocator, [_][]const u8{ out_dir, out_subpath, path_kv.key }); try std.fs.makePath(allocator, std.fs.path.dirname(full_path).?); try std.io.writeFile(full_path, contents.bytes); @@ -455,10 +452,10 @@ pub fn main() !void { } fn usageAndExit(arg0: []const u8) noreturn { - std.debug.warn("Usage: {} [--search-path ] --out --abi \n", arg0); - std.debug.warn("--search-path can be used any number of times.\n"); - std.debug.warn(" subdirectories of search paths look like, e.g. x86_64-linux-gnu\n"); - std.debug.warn("--out is a dir that will be created, and populated with the results\n"); - std.debug.warn("--abi is either musl or glibc\n"); + std.debug.warn("Usage: {} [--search-path ] --out --abi \n", .{arg0}); + std.debug.warn("--search-path can be used any number of times.\n", .{}); + std.debug.warn(" subdirectories of search paths look like, e.g. x86_64-linux-gnu\n", .{}); + std.debug.warn("--out is a dir that will be created, and populated with the results\n", .{}); + std.debug.warn("--abi is either musl or glibc\n", .{}); std.process.exit(1); } diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index c0a3153ae..b97f81d28 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -131,7 +131,7 @@ const Function = struct { }; pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); const allocator = &arena.allocator; const args = try std.process.argsAlloc(allocator); const in_glibc_dir = args[1]; // path to the unzipped tarball of glibc, e.g. ~/downloads/glibc-2.25 @@ -155,7 +155,7 @@ pub fn main() !void { const fn_set = &target_funcs_gop.kv.value.list; for (lib_names) |lib_name, lib_name_index| { - const basename = try fmt.allocPrint(allocator, "lib{}.abilist", lib_name); + const basename = try fmt.allocPrint(allocator, "lib{}.abilist", .{lib_name}); const abi_list_filename = blk: { if (abi_list.targets[0].abi == .gnuabi64 and std.mem.eql(u8, lib_name, "c")) { break :blk try fs.path.join(allocator, [_][]const u8{ prefix, abi_list.path, "n64", basename }); @@ -177,7 +177,7 @@ pub fn main() !void { break :blk try fs.path.join(allocator, [_][]const u8{ prefix, abi_list.path, basename }); }; const contents = std.io.readFileAlloc(allocator, abi_list_filename) catch |err| { - std.debug.warn("unable to open {}: {}\n", abi_list_filename, err); + std.debug.warn("unable to open {}: {}\n", .{abi_list_filename, err}); std.process.exit(1); }; var lines_it = std.mem.tokenize(contents, "\n"); @@ -235,7 +235,7 @@ pub fn main() !void { const vers_txt = &buffered.stream; for (global_ver_list) |name, i| { _ = global_ver_set.put(name, i) catch unreachable; - try vers_txt.print("{}\n", name); + try vers_txt.print("{}\n", .{name}); } try buffered.flush(); } @@ -248,7 +248,7 @@ pub fn main() !void { for (global_fn_list) |name, i| { const kv = global_fn_set.get(name).?; kv.value.index = i; - try fns_txt.print("{} {}\n", name, kv.value.lib); + try fns_txt.print("{} {}\n", .{name, kv.value.lib}); } try buffered.flush(); } @@ -282,7 +282,7 @@ pub fn main() !void { const fn_vers_list = &target_functions.get(@ptrToInt(abi_list)).?.value.fn_vers_list; for (abi_list.targets) |target, it_i| { if (it_i != 0) try abilist_txt.writeByte(' '); - try abilist_txt.print("{}-linux-{}", @tagName(target.arch), @tagName(target.abi)); + try abilist_txt.print("{}-linux-{}", .{@tagName(target.arch), @tagName(target.abi)}); } try abilist_txt.writeByte('\n'); // next, each line implicitly corresponds to a function @@ -293,7 +293,7 @@ pub fn main() !void { }; for (kv.value.toSliceConst()) |ver_index, it_i| { if (it_i != 0) try abilist_txt.writeByte(' '); - try abilist_txt.print("{d}", ver_index); + try abilist_txt.print("{d}", .{ver_index}); } try abilist_txt.writeByte('\n'); }