diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 0000000000..5e94a83a3a --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,43 @@ +image: freebsd/latest +packages: + - cmake + - ninja + - llvm70 +sources: + - https://github.com/ziglang/zig +tasks: + - build: | + cd zig && mkdir build && cd build + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release + ninja install + - test: | + cd zig/build + bin/zig test ../test/stage1/behavior.zig + bin/zig test ../std/special/compiler_rt/index.zig + + bin/zig test ../test/stage1/behavior.zig --library c + bin/zig test ../std/special/compiler_rt/index.zig --library c + + bin/zig test ../test/stage1/behavior.zig --release-fast + bin/zig test ../std/special/compiler_rt/index.zig --release-fast + + bin/zig test ../test/stage1/behavior.zig --release-fast --library c + bin/zig test ../std/special/compiler_rt/index.zig --release-fast --library c + + bin/zig test ../test/stage1/behavior.zig --release-small --library c + bin/zig test ../std/special/compiler_rt/index.zig --release-small --library c + + bin/zig test ../test/stage1/behavior.zig --release-small + bin/zig test ../std/special/compiler_rt/index.zig --release-small + + bin/zig test ../test/stage1/behavior.zig --release-safe + bin/zig test ../std/special/compiler_rt/index.zig --release-safe + + bin/zig test ../test/stage1/behavior.zig --release-safe --library c + bin/zig test ../std/special/compiler_rt/index.zig --release-safe --library c + # TODO enable all tests + #bin/zig build --build-file ../build.zig test + # TODO integrate with the download page updater and make a + # static build available to download for FreeBSD. + # This will require setting up a cache of LLVM/Clang built + # statically. diff --git a/CMakeLists.txt b/CMakeLists.txt index 0817c09f2a..b633e01978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -429,6 +429,7 @@ set(BLAKE_SOURCES ) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) @@ -454,10 +455,10 @@ set(ZIG_STD_FILES "crypto/hmac.zig" "crypto/index.zig" "crypto/md5.zig" + "crypto/poly1305.zig" "crypto/sha1.zig" "crypto/sha2.zig" "crypto/sha3.zig" - "crypto/poly1305.zig" "crypto/x25519.zig" "cstr.zig" "debug/failing_allocator.zig" @@ -482,6 +483,7 @@ set(ZIG_STD_FILES "fmt/errol/index.zig" "fmt/errol/lookup.zig" "fmt/index.zig" + "fmt/parse_float.zig" "hash/adler.zig" "hash/crc.zig" "hash/fnv.zig" @@ -566,9 +568,9 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "mem.zig" "meta/index.zig" "meta/trait.zig" - "mem.zig" "mutex.zig" "net.zig" "os/child_process.zig" @@ -576,20 +578,19 @@ set(ZIG_STD_FILES "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" + "os/freebsd/errno.zig" + "os/freebsd/index.zig" "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" + "os/linux/arm64.zig" "os/linux/errno.zig" "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" - "os/linux/arm64.zig" - "os/freebsd/errno.zig" - "os/freebsd/index.zig" - "os/freebsd/syscall.zig" - "os/freebsd/x86_64.zig" "os/path.zig" "os/time.zig" + "os/uefi.zig" "os/windows/advapi32.zig" "os/windows/error.zig" "os/windows/index.zig" @@ -597,6 +598,7 @@ set(ZIG_STD_FILES "os/windows/ntdll.zig" "os/windows/ole32.zig" "os/windows/shell32.zig" + "os/windows/tls.zig" "os/windows/util.zig" "os/zen.zig" "pdb.zig" @@ -608,11 +610,22 @@ set(ZIG_STD_FILES "special/bootstrap_lib.zig" "special/build_runner.zig" "special/builtin.zig" + "special/compiler_rt/addXf3.zig" "special/compiler_rt/aulldiv.zig" "special/compiler_rt/aullrem.zig" "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" "special/compiler_rt/extendXfYf2.zig" + "special/compiler_rt/fixdfdi.zig" + "special/compiler_rt/fixdfsi.zig" + "special/compiler_rt/fixdfti.zig" + "special/compiler_rt/fixint.zig" + "special/compiler_rt/fixsfdi.zig" + "special/compiler_rt/fixsfsi.zig" + "special/compiler_rt/fixsfti.zig" + "special/compiler_rt/fixtfdi.zig" + "special/compiler_rt/fixtfsi.zig" + "special/compiler_rt/fixtfti.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" @@ -640,6 +653,7 @@ set(ZIG_STD_FILES "special/compiler_rt/udivmodti4.zig" "special/compiler_rt/udivti3.zig" "special/compiler_rt/umodti3.zig" + "special/fmt_runner.zig" "special/init-exe/build.zig" "special/init-exe/src/main.zig" "special/init-lib/build.zig" @@ -647,6 +661,8 @@ set(ZIG_STD_FILES "special/panic.zig" "special/test_runner.zig" "spinlock.zig" + "statically_initialized_mutex.zig" + "testing.zig" "unicode.zig" "zig/ast.zig" "zig/index.zig" @@ -891,3 +907,7 @@ foreach(file ${ZIG_STD_FILES}) get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") endforeach() + +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") diff --git a/README.md b/README.md index a19e9eb67a..3ac4f429f2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A programming language designed for robustness, optimality, and clarity. -[ziglang.org](https://ziglang.org) +[Download & Documentation](https://ziglang.org/download/) ## Feature Highlights @@ -84,29 +84,32 @@ clarity. * LLVM may have the target as an experimental target, which means that you need to use Zig-provided binaries for the target to be available, or build LLVM from source with special configure flags. + * This target may be considered deprecated by an official party, + [such as macosx/i386](https://support.apple.com/en-us/HT208436) in which + case this target will remain forever stuck in Tier 4. #### Support Table -| | freestanding | linux | macosx | windows | freebsd | other | -|--------|--------------|--------|--------|---------|---------|--------| -|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | -|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | -|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | -|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | -|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | -|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | +| | freestanding | linux | macosx | windows | freebsd | UEFI | other | +|--------|--------------|--------|--------|---------|---------|--------|--------| +|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | +|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | +|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | +|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | +|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | +|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | ## Community @@ -171,7 +174,8 @@ See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows *Note: Stage 2 compiler is not complete. Beta users of Zig should use the Stage 1 compiler for now.* -Dependencies are the same as Stage 1, except now you have a working zig compiler. +Dependencies are the same as Stage 1, except now you can use stage 1 to compile +Zig code. ``` bin/zig build --build-file ../build.zig --prefix $(pwd)/stage2 install @@ -183,11 +187,13 @@ binary. ### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler -This is the actual compiler binary that we will install to the system. - *Note: Stage 2 compiler is not yet able to build Stage 3. Building Stage 3 is not yet supported.* +Once the self-hosted compiler can build itself, this will be the actual +compiler binary that we will install to the system. Until then, users should +use stage 1. + #### Debug / Development Build ``` diff --git a/build.zig b/build.zig index e411ae8b21..5c7c5b8a18 100644 --- a/build.zig +++ b/build.zig @@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void { var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); - const langref_out_path = os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable; + const langref_out_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, "langref.html" }, + ) catch unreachable; var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ docgen_exe.getOutputPath(), rel_zig_exe, @@ -104,7 +107,7 @@ pub fn build(b: *Builder) !void { } const modes = chosen_modes[0..chosen_mode_index]; - test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); @@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { for (dep.libdirs.toSliceConst()) |lib_dir| { lib_exe_obj.addLibPath(lib_dir); } - const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable; + const lib_dir = os.path.join( + b.allocator, + [][]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") else b.fmt("lib{}.a", lib); - const static_lib_name = os.path.join(b.allocator, lib_dir, static_bare_name) catch unreachable; + const static_lib_name = os.path.join( + b.allocator, + [][]const u8{ lib_dir, static_bare_name }, + ) catch unreachable; const have_static = fileExists(static_lib_name) catch unreachable; if (have_static) { lib_exe_obj.addObjectFile(static_lib_name); @@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool { fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; - lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); + lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{ + cmake_binary_dir, + "zig_cpp", + b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()), + }) catch unreachable); } const LibraryDep = struct { @@ -189,14 +202,14 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { const prefix_output = try b.exec([][]const u8{ llvm_config_exe, "--prefix" }); var result = LibraryDep{ - .prefix = mem.split(prefix_output, " \r\n").next().?, + .prefix = mem.tokenize(prefix_output, " \r\n").next().?, .libs = ArrayList([]const u8).init(b.allocator), .system_libs = ArrayList([]const u8).init(b.allocator), .includes = ArrayList([]const u8).init(b.allocator), .libdirs = ArrayList([]const u8).init(b.allocator), }; { - var it = mem.split(libs_output, " \r\n"); + var it = mem.tokenize(libs_output, " \r\n"); while (it.next()) |lib_arg| { if (mem.startsWith(u8, lib_arg, "-l")) { try result.system_libs.append(lib_arg[2..]); @@ -210,7 +223,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } } { - var it = mem.split(includes_output, " \r\n"); + var it = mem.tokenize(includes_output, " \r\n"); while (it.next()) |include_arg| { if (mem.startsWith(u8, include_arg, "-I")) { try result.includes.append(include_arg[2..]); @@ -220,7 +233,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } } { - var it = mem.split(libdir_output, " \r\n"); + var it = mem.tokenize(libdir_output, " \r\n"); while (it.next()) |libdir| { if (mem.startsWith(u8, libdir, "-L")) { try result.libdirs.append(libdir[2..]); @@ -233,19 +246,25 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { - var it = mem.split(stdlib_files, ";"); + var it = mem.tokenize(stdlib_files, ";"); while (it.next()) |stdlib_file| { - const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable; - const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable; + const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable; + const dest_path = os.path.join( + b.allocator, + [][]const u8{ "lib", "zig", "std", stdlib_file }, + ) catch unreachable; b.installFile(src_path, dest_path); } } pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { - var it = mem.split(c_header_files, ";"); + var it = mem.tokenize(c_header_files, ";"); while (it.next()) |c_header_file| { - const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable; - const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable; + const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable; + const dest_path = os.path.join( + b.allocator, + [][]const u8{ "lib", "zig", "include", c_header_file }, + ) catch unreachable; b.installFile(src_path, dest_path); } } @@ -277,7 +296,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); if (ctx.lld_include_dir.len != 0) { exe.addIncludeDir(ctx.lld_include_dir); - var it = mem.split(ctx.lld_libraries, ";"); + var it = mem.tokenize(ctx.lld_libraries, ";"); while (it.next()) |lib| { exe.addObjectFile(lib); } @@ -293,11 +312,13 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { try addCxxKnownPath(b, ctx, exe, "libstdc++.a", \\Unable to determine path to libstdc++.a \\On Fedora, install libstdc++-static and try again. - \\ ); exe.linkSystemLibrary("pthread"); - } else if (exe.target.isDarwin() or exe.target.isFreeBSD()) { + } else if (exe.target.isFreeBSD()) { + try addCxxKnownPath(b, ctx, exe, "libc++.a", null); + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isDarwin()) { if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) { // Compiler is GCC. try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null); @@ -332,7 +353,7 @@ fn addCxxKnownPath( ctx.cxx_compiler, b.fmt("-print-file-name={}", objname), }); - const path_unpadded = mem.split(path_padded, "\r\n").next().?; + const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { warn("{}", msg); diff --git a/ci/azure/windows_install b/ci/azure/windows_install index 952e9f78cb..8a279210ff 100755 --- a/ci/azure/windows_install +++ b/ci/azure/windows_install @@ -3,6 +3,7 @@ set -x 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-7.0.0-win64-msvc-release.tar.xz" diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index b847813682..2f0afa09b7 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -15,6 +15,51 @@ find_program(LLVM_CONFIG_EXE "c:/msys64/mingw64/bin" "C:/Libraries/llvm-7.0.0/bin") +if ("${LLVM_CONFIG_EXE}" STREQUAL "LLVM_CONFIG_EXE-NOTFOUND") + message(FATAL_ERROR "unable to find llvm-config") +endif() + +execute_process( + COMMAND ${LLVM_CONFIG_EXE} --version + OUTPUT_VARIABLE LLVM_CONFIG_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if("${LLVM_CONFIG_VERSION}" VERSION_LESS 7) + message(FATAL_ERROR "expected LLVM 7.x but found ${LLVM_CONFIG_VERSION}") +endif() +if("${LLVM_CONFIG_VERSION}" VERSION_EQUAL 8) + message(FATAL_ERROR "expected LLVM 7.x but found ${LLVM_CONFIG_VERSION}") +endif() +if("${LLVM_CONFIG_VERSION}" VERSION_GREATER 8) + message(FATAL_ERROR "expected LLVM 7.x but found ${LLVM_CONFIG_VERSION}") +endif() + +execute_process( + COMMAND ${LLVM_CONFIG_EXE} --targets-built + OUTPUT_VARIABLE LLVM_TARGETS_BUILT_SPACES + OUTPUT_STRIP_TRAILING_WHITESPACE) +string(REPLACE " " ";" LLVM_TARGETS_BUILT "${LLVM_TARGETS_BUILT_SPACES}") +function(NEED_TARGET TARGET_NAME) + list (FIND LLVM_TARGETS_BUILT "${TARGET_NAME}" _index) + if (${_index} EQUAL -1) + message(FATAL_ERROR "LLVM is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.") + endif() +endfunction(NEED_TARGET) +NEED_TARGET("AArch64") +NEED_TARGET("AMDGPU") +NEED_TARGET("ARM") +NEED_TARGET("BPF") +NEED_TARGET("Hexagon") +NEED_TARGET("Lanai") +NEED_TARGET("Mips") +NEED_TARGET("MSP430") +NEED_TARGET("NVPTX") +NEED_TARGET("PowerPC") +NEED_TARGET("Sparc") +NEED_TARGET("SystemZ") +NEED_TARGET("X86") +NEED_TARGET("XCore") + if(NOT(CMAKE_BUILD_TYPE STREQUAL "Debug") OR ZIG_STATIC) execute_process( COMMAND ${LLVM_CONFIG_EXE} --libfiles --link-static diff --git a/doc/docgen.zig b/doc/docgen.zig index 2489e034bc..082f308a57 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -4,7 +4,7 @@ const io = std.io; const os = std.os; const warn = std.debug.warn; const mem = std.mem; -const assert = std.debug.assert; +const testing = std.testing; const max_doc_file_size = 10 * 1024 * 1024; @@ -620,7 +620,7 @@ const TermState = enum { test "term color" { const input_bytes = "A\x1b[32;1mgreen\x1b[0mB"; const result = try termColor(std.debug.global_allocator, input_bytes); - assert(mem.eql(u8, result, "AgreenB")); + testing.expectEqualSlices(u8, "AgreenB", result); } fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { @@ -770,6 +770,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok std.zig.Token.Id.Keyword_suspend, std.zig.Token.Id.Keyword_switch, std.zig.Token.Id.Keyword_test, + std.zig.Token.Id.Keyword_threadlocal, std.zig.Token.Id.Keyword_try, std.zig.Token.Id.Keyword_union, std.zig.Token.Id.Keyword_unreachable, @@ -915,6 +916,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok std.zig.Token.Id.AngleBracketAngleBracketRightEqual, std.zig.Token.Id.Tilde, std.zig.Token.Id.BracketStarBracket, + std.zig.Token.Id.BracketStarCBracket, => try writeEscaped(out, src[token.start..token.end]), std.zig.Token.Id.Invalid => return parseError( @@ -990,13 +992,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try tokenizeAndPrint(tokenizer, out, code.source_token); try out.write(""); const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); - const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext); + const tmp_source_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_ext }, + ); try io.writeFile(tmp_source_file_name, trimmed_raw_source); switch (code.id) { Code.Id.Exe => |expected_outcome| { const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext); - const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext); + const tmp_bin_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_bin_ext }, + ); var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); try build_args.appendSlice([][]const u8{ @@ -1024,7 +1032,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } for (code.link_objects) |link_object| { const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext); - const full_path_object = try os.path.join(allocator, tmp_dir_name, name_with_ext); + const full_path_object = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_with_ext }, + ); try build_args.append("--object"); try build_args.append(full_path_object); try out.print(" --object {}", name_with_ext); @@ -1216,12 +1227,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }, Code.Id.Obj => |maybe_error_match| { const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext); - const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext); + const tmp_obj_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_obj_ext }, + ); var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name); - const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext); + const output_h_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_h_ext }, + ); try build_args.appendSlice([][]const u8{ zig_exe, diff --git a/doc/langref.html.in b/doc/langref.html.in index a1903331a4..1341bf1be5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -165,7 +165,7 @@ const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. - var stdout_file = try std.io.getStdOut(); + const stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); @@ -1531,6 +1531,29 @@ test "array initialization with function calls" { {#code_end#} {#see_also|for|Slices#} {#header_close#} + + {#header_open|Vectors#} +

+ A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on + in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin + function {#link|@Vector#}. +

+

+ TODO talk about C ABI interop +

+ {#header_open|SIMD#} +

+ TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the + docs with: + * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector? + * How to convert to/from vectors/arrays + * How to access individual elements from vectors, how to loop over the elements + * "shuffle" + * Advice on writing high perf software, how to abstract the best way +

+ {#header_close#} + {#header_close#} + {#header_open|Pointers#}

Zig has two kinds of pointers: @@ -1671,7 +1694,7 @@ test "comptime @intToPtr" { } } {#code_end#} - {#see_also|Optional Pointers#} + {#see_also|Optional Pointers|@intToPtr|@ptrToInt#} {#header_open|volatile#}

Loads and stores are assumed to not have side effects. If a given load or store should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}. @@ -1800,7 +1823,9 @@ fn foo(bytes: []u8) u32 { } {#code_end#} {#header_close#} + {#see_also|C Pointers#} {#header_close#} + {#header_open|Slices#} {#code_begin|test_safety|index out of bounds#} const assert = @import("std").debug.assert; @@ -3169,7 +3194,16 @@ fn foo() void { } {#code_end#} {#header_open|Pass-by-value Parameters#}

- In Zig, structs, unions, and enums with payloads can be passed directly to a function: + Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters + are copied, and then the copy is available in the function body. This is called "passing by value". + Copying a primitive type is essentially free and typically involves nothing more than + setting a register. +

+

+ Structs, unions, and arrays can sometimes be more efficiently passed as a reference, since a copy + could be arbitrarily expensive depending on the size. When these types are passed + as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way + Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.

{#code_begin|test#} const Point = struct { @@ -3178,20 +3212,20 @@ const Point = struct { }; fn foo(point: Point) i32 { + // Here, `point` could be a reference, or a copy. The function body + // can ignore the difference and treat it as a value. Be very careful + // taking the address of the parameter - it should be treated as if + // the address will become invalid when the function returns. return point.x + point.y; } const assert = @import("std").debug.assert; -test "pass aggregate type by non-copy value to function" { +test "pass struct to function" { assert(foo(Point{ .x = 1, .y = 2 }) == 3); } {#code_end#}

- In this case, the value may be passed by reference, or by value, whichever way - Zig decides will be faster. -

-

For extern functions, Zig follows the C ABI for passing structs and unions by value.

{#header_close#} @@ -3949,7 +3983,7 @@ test "implicit cast - invoke a type as a function" { {#code_end#}

Implicit casts are only allowed when it is completely unambiguous how to get from one type to another, - and the transformation is guaranteed to be safe. + and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.

{#header_open|Implicit Cast: Stricter Qualification#}

@@ -4327,7 +4361,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {

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

- {#code_begin|test_err|unable to evaluate constant expression#} + {#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; } @@ -4595,7 +4629,7 @@ test "fibonacci" {

What if we fix the base case, but put the wrong value in the {#syntax#}assert{#endsyntax#} line?

- {#code_begin|test_err|encountered @panic at compile-time#} + {#code_begin|test_err|unable to evaluate constant expression#} const assert = @import("std").debug.assert; fn fibonacci(index: i32) i32 { @@ -5169,6 +5203,34 @@ fn seq(c: u8) void { If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

{#header_close#} + {#header_open|@alignCast#} +
{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}
+

+ {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#}, + {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#} + except with the alignment adjusted to the new value. +

+

A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added + to the generated code to make sure the pointer is aligned as promised.

+ + {#header_close#} + {#header_open|@alignOf#} +
{#syntax#}@alignOf(comptime T: type) comptime_int{#endsyntax#}
+

+ This function returns the number of bytes that this type should be aligned to + for the current target to match the C ABI. When the child type of a pointer has + this alignment, the alignment can be omitted from the type. +

+
{#syntax#}const assert = @import("std").debug.assert;
+comptime {
+    assert(*u32 == *align(@alignOf(u32)) u32);
+}{#endsyntax#}
+

+ The result is a target-specific compile time constant. It is guaranteed to be + less than or equal to {#link|@sizeOf(T)|@sizeOf#}. +

+ {#see_also|Alignment#} + {#header_close#} {#header_open|@ArgType#}
{#syntax#}@ArgType(comptime T: type, comptime n: usize) type{#endsyntax#}

@@ -5241,6 +5303,7 @@ fn seq(c: u8) void { Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time. It's a compile error to bitcast a struct to a scalar type of the same size since structs have undefined layout. However if the struct is packed then it works.

{#header_close#} + {#header_open|@bitOffsetOf#}
{#syntax#}@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}

@@ -5253,52 +5316,6 @@ fn seq(c: u8) void {

{#see_also|@byteOffsetOf#} {#header_close#} - {#header_open|@breakpoint#} -
{#syntax#}@breakpoint(){#endsyntax#}
-

- This function inserts a platform-specific debug trap instruction which causes - debuggers to break there. -

-

- This function is only valid within function scope. -

- - {#header_close#} - {#header_open|@byteOffsetOf#} -
{#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}
-

- Returns the byte offset of a field relative to its containing struct. -

- {#see_also|@bitOffsetOf#} - {#header_close#} - {#header_open|@alignCast#} -
{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}
-

- {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#}, - {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#} - except with the alignment adjusted to the new value. -

-

A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added - to the generated code to make sure the pointer is aligned as promised.

- - {#header_close#} - {#header_open|@alignOf#} -
{#syntax#}@alignOf(comptime T: type) comptime_int{#endsyntax#}
-

- This function returns the number of bytes that this type should be aligned to - for the current target to match the C ABI. When the child type of a pointer has - this alignment, the alignment can be omitted from the type. -

-
{#syntax#}const assert = @import("std").debug.assert;
-comptime {
-    assert(*u32 == *align(@alignOf(u32)) u32);
-}{#endsyntax#}
-

- The result is a target-specific compile time constant. It is guaranteed to be - less than or equal to {#link|@sizeOf(T)|@sizeOf#}. -

- {#see_also|Alignment#} - {#header_close#} {#header_open|@boolToInt#}
{#syntax#}@boolToInt(value: bool) u1{#endsyntax#}
@@ -5312,6 +5329,47 @@ comptime {

{#header_close#} + {#header_open|@breakpoint#} +
{#syntax#}@breakpoint(){#endsyntax#}
+

+ This function inserts a platform-specific debug trap instruction which causes + debuggers to break there. +

+

+ This function is only valid within function scope. +

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

{#syntax#}T{#endsyntax#} must be an integer type with bit count evenly divisible by 8.

+

+ Swaps the byte order of the integer. This converts a big endian integer to a little endian integer, + and converts a little endian integer to a big endian integer. +

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

{#syntax#}T{#endsyntax#} accepts any integer type.

+

+ Reverses the bitpattern of an integer value, including the sign bit if applicable. +

+

+ For example 0b10110110 ({#syntax#}u8 = 182{#endsyntax#}, {#syntax#}i8 = -74{#endsyntax#}) + becomes 0b01101101 ({#syntax#}u8 = 109{#endsyntax#}, {#syntax#}i8 = 109{#endsyntax#}). +

+ {#header_close#} + + {#header_open|@byteOffsetOf#} +
{#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}
+

+ Returns the byte offset of a field relative to its containing struct. +

+ {#see_also|@bitOffsetOf#} + {#header_close#} + {#header_open|@bytesToSlice#}
{#syntax#}@bytesToSlice(comptime Element: type, bytes: []u8) []Element{#endsyntax#}

@@ -5378,17 +5436,7 @@ comptime {

{#see_also|Import from C Header File|@cImport|@cDefine|@cUndef#} {#header_close#} - {#header_open|@cUndef#} -
{#syntax#}@cUndef(comptime name: []u8){#endsyntax#}
-

- This function can only occur inside {#syntax#}@cImport{#endsyntax#}. -

-

- This appends #undef $name to the {#syntax#}@cImport{#endsyntax#} - temporary buffer. -

- {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} - {#header_close#} + {#header_open|@clz#}
{#syntax#}@clz(x: T) U{#endsyntax#}

@@ -5404,6 +5452,7 @@ comptime {

{#see_also|@ctz|@popCount#} {#header_close#} + {#header_open|@cmpxchgStrong#}
{#syntax#}@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}

@@ -5459,6 +5508,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val

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

{#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} + {#header_open|@compileError#}
{#syntax#}@compileError(comptime msg: []u8){#endsyntax#}

@@ -5471,6 +5521,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val and {#syntax#}comptime{#endsyntax#} functions.

{#header_close#} + {#header_open|@compileLog#}
{#syntax#}@compileLog(args: ...){#endsyntax#}

@@ -5525,6 +5576,7 @@ test "main" { } {#code_end#} {#header_close#} + {#header_open|@ctz#}

{#syntax#}@ctz(x: T) U{#endsyntax#}

@@ -5540,6 +5592,19 @@ test "main" {

{#see_also|@clz|@popCount#} {#header_close#} + + {#header_open|@cUndef#} +
{#syntax#}@cUndef(comptime name: []u8){#endsyntax#}
+

+ This function can only occur inside {#syntax#}@cImport{#endsyntax#}. +

+

+ This appends #undef $name to the {#syntax#}@cImport{#endsyntax#} + temporary buffer. +

+ {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} + {#header_close#} + {#header_open|@divExact#}
{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}

@@ -5606,14 +5671,6 @@ test "main" { {#see_also|@intToEnum#} {#header_close#} - {#header_open|@errSetCast#} -

{#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}
-

- Converts an error value from one error set to another error set. Attempting to convert an error - which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}. -

- {#header_close#} - {#header_open|@errorName#}
{#syntax#}@errorName(err: anyerror) []const u8{#endsyntax#}

@@ -5656,6 +5713,14 @@ test "main" { {#see_also|@intToError#} {#header_close#} + {#header_open|@errSetCast#} +

{#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}
+

+ Converts an error value from one error set to another error set. Attempting to convert an error + which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}. +

+ {#header_close#} + {#header_open|@export#}
{#syntax#}@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8{#endsyntax#}

@@ -5724,6 +5789,7 @@ test "main" { This function is only valid within function scope.

{#header_close#} + {#header_open|@handle#}
{#syntax#}@handle(){#endsyntax#}

@@ -5734,6 +5800,7 @@ test "main" { This function is only valid within an async function scope.

{#header_close#} + {#header_open|@import#}
{#syntax#}@import(comptime path: []u8) (namespace){#endsyntax#}

@@ -5754,6 +5821,7 @@ test "main" { {#see_also|Compile Variables|@embedFile#} {#header_close#} + {#header_open|@inlineCall#}

{#syntax#}@inlineCall(function: X, args: ...) Y{#endsyntax#}

@@ -5833,6 +5901,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } bit count for an integer type is {#syntax#}65535{#endsyntax#}.

{#header_close#} + {#header_open|@memberCount#}
{#syntax#}@memberCount(comptime T: type) comptime_int{#endsyntax#}

@@ -5859,6 +5928,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }

{#syntax#}@memberType(comptime T: type, comptime index: usize) type{#endsyntax#}

Returns the field type of a struct or union.

{#header_close#} + {#header_open|@memcpy#}
{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}

@@ -5869,14 +5939,15 @@ fn add(a: i32, b: i32) i32 { return a + b; } This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}
+
{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memcpy.

There is also a standard library function for this:

{#syntax#}const mem = @import("std").mem;
-mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
+mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} {#header_close#} + {#header_open|@memset#}
{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize){#endsyntax#}

@@ -5886,7 +5957,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#} This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}
+
{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memset.

@@ -5894,6 +5965,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
{#syntax#}const mem = @import("std").mem;
 mem.set(u8, dest, c);{#endsyntax#}
{#header_close#} + {#header_open|@mod#}
{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}

@@ -5907,6 +5979,7 @@ mem.set(u8, dest, c);{#endsyntax#}

For a function that returns an error code, see {#syntax#}@import("std").math.mod{#endsyntax#}.

{#see_also|@rem#} {#header_close#} + {#header_open|@mulWithOverflow#}
{#syntax#}@mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}

@@ -5915,6 +5988,7 @@ mem.set(u8, dest, c);{#endsyntax#} If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

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

@@ -5951,6 +6025,7 @@ fn targetFunction(x: i32) usize { } {#code_end#} {#header_close#} + {#header_open|@noInlineCall#}

{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}

@@ -5973,6 +6048,7 @@ fn add(a: i32, b: i32) i32 {

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

@@ -5996,6 +6072,7 @@ test "call foo" { } {#code_end#} {#header_close#} + {#header_open|@panic#}

{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}

@@ -6012,6 +6089,7 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} + {#header_open|@popCount#}

{#syntax#}@popCount(integer: var) var{#endsyntax#}

Counts the number of bits set in an integer.

@@ -6022,12 +6100,18 @@ test "call foo" {

{#see_also|@ctz|@clz#} {#header_close#} + {#header_open|@ptrCast#}
{#syntax#}@ptrCast(comptime DestType: type, value: var) DestType{#endsyntax#}

Converts a pointer of one type to a pointer of another type.

+

+ {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} + to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. +

{#header_close#} + {#header_open|@ptrToInt#}
{#syntax#}@ptrToInt(value: var) usize{#endsyntax#}

@@ -6042,6 +6126,7 @@ test "call foo" {

To convert the other way, use {#link|@intToPtr#}

{#header_close#} + {#header_open|@rem#}
{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}

@@ -6055,6 +6140,7 @@ test "call foo" {

For a function that returns an error code, see {#syntax#}@import("std").math.rem{#endsyntax#}.

{#see_also|@mod#} {#header_close#} + {#header_open|@returnAddress#}
{#syntax#}@returnAddress(){#endsyntax#}

@@ -6075,19 +6161,14 @@ test "call foo" { Ensures that a function will have a stack alignment of at least {#syntax#}alignment{#endsyntax#} bytes.

{#header_close#} + {#header_open|@setCold#}
{#syntax#}@setCold(is_cold: bool){#endsyntax#}

Tells the optimizer that a function is rarely called.

{#header_close#} - {#header_open|@setRuntimeSafety#} -
{#syntax#}@setRuntimeSafety(safety_on: bool){#endsyntax#}
-

- Sets whether runtime safety checks are on for the scope that contains the function call. -

- {#header_close#} {#header_open|@setEvalBranchQuota#}
{#syntax#}@setEvalBranchQuota(new_quota: usize){#endsyntax#}

@@ -6122,6 +6203,7 @@ test "foo" { {#see_also|comptime#} {#header_close#} + {#header_open|@setFloatMode#}

{#syntax#}@setFloatMode(mode: @import("builtin").FloatMode){#endsyntax#}

@@ -6156,6 +6238,7 @@ pub const FloatMode = enum {

{#see_also|Floating Point Operations#} {#header_close#} + {#header_open|@setGlobalLinkage#}
{#syntax#}@setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage){#endsyntax#}

@@ -6163,6 +6246,15 @@ pub const FloatMode = enum {

{#see_also|Compile Variables#} {#header_close#} + + {#header_open|@setRuntimeSafety#} +
{#syntax#}@setRuntimeSafety(safety_on: bool){#endsyntax#}
+

+ Sets whether runtime safety checks are on for the scope that contains the function call. +

+ + {#header_close#} + {#header_open|@shlExact#}
{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}

@@ -6175,6 +6267,7 @@ pub const FloatMode = enum {

{#see_also|@shrExact|@shlWithOverflow#} {#header_close#} + {#header_open|@shlWithOverflow#}
{#syntax#}@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool{#endsyntax#}

@@ -6188,6 +6281,7 @@ pub const FloatMode = enum {

{#see_also|@shlExact|@shrExact#} {#header_close#} + {#header_open|@shrExact#}
{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}

@@ -6205,10 +6299,15 @@ pub const FloatMode = enum {

{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}

This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory. -

-

The result is a target-specific compile time constant.

+

+ This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset + in bytes between element at index 0 and the element at index 1. For {#link|integer|Integers#}, + consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or + {#syntax#}@typeInfo(T).Int.bits{#endsyntax#}. +

+ {#see_also|@typeInfo#} {#header_close#} {#header_open|@sliceToBytes#} @@ -6229,6 +6328,7 @@ pub const FloatMode = enum { This is a low-level intrinsic. Most code can use {#syntax#}std.math.sqrt{#endsyntax#} instead.

{#header_close#} + {#header_open|@subWithOverflow#}
{#syntax#}@subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}

@@ -6237,12 +6337,14 @@ pub const FloatMode = enum { If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

{#header_close#} + {#header_open|@tagName#}
{#syntax#}@tagName(value: var) []const u8{#endsyntax#}

Converts an enum value or union value to a slice of bytes representing the name.

{#header_close#} + {#header_open|@TagType#}
{#syntax#}@TagType(T: type) type{#endsyntax#}

@@ -6252,6 +6354,7 @@ pub const FloatMode = enum { For a union, returns the enum type that is used to store the tag value.

{#header_close#} + {#header_open|@This#}
{#syntax#}@This() type{#endsyntax#}

@@ -6287,15 +6390,16 @@ fn List(comptime T: type) type { #1047 for details.

{#header_close#} + {#header_open|@truncate#} -
{#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}
+
{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}

This function truncates bits from an integer type, resulting in a smaller integer type.

- The following produces a crash in debug mode and undefined behavior in - release mode: + The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in + {#link|ReleaseFast#} mode:

{#syntax#}const a: u16 = 0xabcd;
 const b: u8 = u8(a);{#endsyntax#}
@@ -6309,8 +6413,12 @@ const b: u8 = @truncate(u8, a); 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|@typeId#}
{#syntax#}@typeId(comptime T: type) @import("builtin").TypeId{#endsyntax#}

@@ -6345,6 +6453,7 @@ pub const TypeId = enum { }; {#code_end#} {#header_close#} + {#header_open|@typeInfo#}

{#syntax#}@typeInfo(comptime T: type) @import("builtin").TypeInfo{#endsyntax#}

@@ -6527,13 +6636,16 @@ pub const TypeInfo = union(TypeId) { }; {#code_end#} {#header_close#} + {#header_open|@typeName#} -

{#syntax#}@typeName(T: type) []u8{#endsyntax#}
+
{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}

- This function returns the string representation of a type. + This function returns the string representation of a type, as + an array. It is equivalent to a string literal of the type name.

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

@@ -6542,7 +6654,19 @@ pub const TypeInfo = union(TypeId) {

{#header_close#} + + {#header_open|@Vector#} +
{#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}
+

+ This function returns a vector type for {#link|SIMD#}. +

+

+ {#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a + {#link|pointer|Pointers#}. +

{#header_close#} + {#header_close#} + {#header_open|Build Mode#}

Zig has four build modes: @@ -6613,6 +6737,25 @@ pub fn build(b: *Builder) void { {#header_close#} {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} {#header_close#} + + {#header_open|Single Threaded Builds#} +

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

+

+ {#header_close#} + {#header_open|Undefined Behavior#}

Zig has many instances of undefined behavior. If undefined behavior is @@ -7213,10 +7356,27 @@ fn bar(f: *Foo) void { {#code_end#} {#header_close#} - {#header_open|Out of Bounds Float To Integer Cast#} + {#header_open|Out of Bounds Float to Integer Cast#}

TODO

{#header_close#} + {#header_open|Pointer Cast Invalid Null#} +

At compile-time:

+ {#code_begin|test_err|null pointer casted to type#} +comptime { + const opt_ptr: ?*i32 = null; + const ptr = @ptrCast(*i32, opt_ptr); +} + {#code_end#} +

At runtime:

+ {#code_begin|exe_err#} +pub fn main() void { + var opt_ptr: ?*i32 = null; + var ptr = @ptrCast(*i32, opt_ptr); +} + {#code_end#} + {#header_close#} + {#header_close#} {#header_open|Memory#}

TODO: explain no default allocator in zig

@@ -7307,6 +7467,7 @@ pub fn main() void { {#code_end#} {#see_also|String Literals#} {#header_close#} + {#header_open|Import from C Header File#}

The {#syntax#}@cImport{#endsyntax#} builtin function can be used @@ -7345,6 +7506,36 @@ const c = @cImport({ {#code_end#} {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} {#header_close#} + + {#header_open|C Pointers#} +

+ This type is to be avoided whenever possible. The only valid reason for using a C pointer is in + auto-generated code from translating C code. +

+

+ When importing C header files, it is ambiguous whether pointers should be translated as + single-item pointers ({#syntax#}*T{#endsyntax#}) or unknown-length pointers ({#syntax#}[*]T{#endsyntax#}). + C pointers are a compromise so that Zig code can utilize translated header files directly. +

+

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

+ + {#header_close#} + {#header_open|Exporting a C Library#}

One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages @@ -7741,7 +7932,7 @@ TopLevelComptime <- KEYWORD_comptime BlockExpr TopLevelDecl <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block) - / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl + / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl / KEYWORD_use Expr SEMICOLON FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) @@ -8032,7 +8223,8 @@ ArrayTypeStart <- LBRACKET Expr? RBRACKET PtrTypeStart <- ASTERISK / ASTERISK2 - / LBRACKET ASTERISK RBRACKET + / PTRUNKNOWN + / PTRC # ContainerDecl specific ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE @@ -8130,7 +8322,7 @@ LARROW2 <- '<<' ![=] skip LARROW2EQUAL <- '<<=' skip LARROWEQUAL <- '<=' skip LBRACE <- '{' skip -LBRACKET <- '[' skip +LBRACKET <- '[' ![*] skip LPAREN <- '(' skip MINUS <- '-' ![%=>] skip MINUSEQUAL <- '-=' skip @@ -8147,6 +8339,8 @@ PLUS2 <- '++' skip PLUSEQUAL <- '+=' skip PLUSPERCENT <- '+%' ![=] skip PLUSPERCENTEQUAL <- '+%=' skip +PTRC <- '[*c]' skip +PTRUNKNOWN <- '[*]' skip QUESTIONMARK <- '?' skip RARROW <- '>' ![>=] skip RARROW2 <- '>>' ![=] skip @@ -8201,6 +8395,7 @@ KEYWORD_struct <- 'struct' end_of_word KEYWORD_suspend <- 'suspend' end_of_word KEYWORD_switch <- 'switch' end_of_word KEYWORD_test <- 'test' end_of_word +KEYWORD_threadlocal <- 'threadlocal' end_of_word KEYWORD_true <- 'true' end_of_word KEYWORD_try <- 'try' end_of_word KEYWORD_undefined <- 'undefined' end_of_word @@ -8221,7 +8416,7 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm / KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection / KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend - / KEYWORD_switch / KEYWORD_test / KEYWORD_true / KEYWORD_try + / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try / KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable / KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while {#header_close#} diff --git a/doc/targets.md b/doc/targets.md deleted file mode 100644 index e5c6ab5534..0000000000 --- a/doc/targets.md +++ /dev/null @@ -1,15 +0,0 @@ -# How to Add Support For More Targets - -Create bootstrap code in std/bootstrap.zig and add conditional compilation -logic. This code is responsible for the real executable entry point, calling -main() and making the exit syscall when main returns. - -How to pass a byvalue struct parameter in the C calling convention is -target-specific. Add logic for how to do function prototypes and function calls -for the target when an exported or external function has a byvalue struct. - -Write the target-specific code in the standard library. - -Update the C integer types to be the correct size for the target. - -Make sure that `c_longdouble` codegens the correct floating point value. diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 66264666bc..b4eb1c292a 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -15,7 +15,7 @@ pub fn main() !void { std.debug.warn("unable to seed random number generator: {}", err); return err; }; - const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); + const seed = std.mem.readIntNative(u64, &seed_bytes); var prng = std.rand.DefaultPrng.init(seed); const answer = prng.random.range(u8, 0, 100) + 1; diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 8e65e06a96..cb7d5ef157 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. - var stdout_file = try std.io.getStdOut(); + const stdout_file = try 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/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index 99e6ecc17a..7bbd233a75 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -1,5 +1,6 @@ const std = @import("std"); const debug = std.debug; +const testing = std.testing; const mem = std.mem; const Allocator = mem.Allocator; @@ -272,21 +273,21 @@ test "parse arguments" { var args = try Args.parse(std.debug.global_allocator, spec1, cliargs); - debug.assert(args.present("help")); - debug.assert(!args.present("help2")); - debug.assert(!args.present("init")); + testing.expect(args.present("help")); + testing.expect(!args.present("help2")); + testing.expect(!args.present("init")); - debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig")); - debug.assert(mem.eql(u8, args.single("color").?, "on")); + 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").?; - debug.assert(mem.eql(u8, objects[0], "obj1")); - debug.assert(mem.eql(u8, objects[1], "obj2")); + testing.expect(mem.eql(u8, objects[0], "obj1")); + testing.expect(mem.eql(u8, objects[1], "obj2")); - debug.assert(mem.eql(u8, args.single("library").?, "lib2")); + testing.expect(mem.eql(u8, args.single("library").?, "lib2")); const pos = args.positionals.toSliceConst(); - debug.assert(mem.eql(u8, pos[0], "build")); - debug.assert(mem.eql(u8, pos[1], "pos1")); - debug.assert(mem.eql(u8, pos[2], "pos2")); + 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/codegen.zig b/src-self-hosted/codegen.zig index 45cfa98942..1c671b61e2 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -137,10 +137,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) pub const ObjectFile = struct { comp: *Compilation, - module: llvm.ModuleRef, - builder: llvm.BuilderRef, + module: *llvm.Module, + builder: *llvm.Builder, dibuilder: *llvm.DIBuilder, - context: llvm.ContextRef, + context: *llvm.Context, lock: event.Lock, arena: *std.mem.Allocator, @@ -323,7 +323,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) fn addLLVMAttr( ofile: *ObjectFile, - val: llvm.ValueRef, + val: *llvm.Value, attr_index: llvm.AttributeIndex, attr_name: []const u8, ) !void { @@ -335,7 +335,7 @@ fn addLLVMAttr( fn addLLVMAttrStr( ofile: *ObjectFile, - val: llvm.ValueRef, + val: *llvm.Value, attr_index: llvm.AttributeIndex, attr_name: []const u8, attr_val: []const u8, @@ -351,7 +351,7 @@ fn addLLVMAttrStr( } fn addLLVMAttrInt( - val: llvm.ValueRef, + val: *llvm.Value, attr_index: llvm.AttributeIndex, attr_name: []const u8, attr_val: u64, @@ -362,25 +362,25 @@ fn addLLVMAttrInt( llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); } -fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void { +fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8) !void { return addLLVMAttr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name); } -fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void { +fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: []const u8) !void { return addLLVMAttrStr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val); } -fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { +fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: u64) !void { return addLLVMAttrInt(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val); } fn renderLoadUntyped( ofile: *ObjectFile, - ptr: llvm.ValueRef, + ptr: *llvm.Value, alignment: Type.Pointer.Align, vol: Type.Pointer.Vol, name: [*]const u8, -) !llvm.ValueRef { +) !*llvm.Value { const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory; switch (vol) { Type.Pointer.Vol.Non => {}, @@ -390,11 +390,11 @@ fn renderLoadUntyped( return result; } -fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef { +fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value { return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name); } -pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef { +pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer) !?*llvm.Value { const child_type = ptr_type.key.child_type; if (!child_type.hasBits()) { return null; @@ -407,11 +407,11 @@ pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Po pub fn renderStoreUntyped( ofile: *ObjectFile, - value: llvm.ValueRef, - ptr: llvm.ValueRef, + value: *llvm.Value, + ptr: *llvm.Value, alignment: Type.Pointer.Align, vol: Type.Pointer.Vol, -) !llvm.ValueRef { +) !*llvm.Value { const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory; switch (vol) { Type.Pointer.Vol.Non => {}, @@ -423,10 +423,10 @@ pub fn renderStoreUntyped( pub fn renderStore( ofile: *ObjectFile, - value: llvm.ValueRef, - ptr: llvm.ValueRef, + value: *llvm.Value, + ptr: *llvm.Value, ptr_type: *Type.Pointer, -) !llvm.ValueRef { +) !*llvm.Value { return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol); } @@ -435,7 +435,7 @@ pub fn renderAlloca( var_type: *Type, name: []const u8, alignment: Type.Pointer.Align, -) !llvm.ValueRef { +) !*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; @@ -443,7 +443,7 @@ pub fn renderAlloca( return result; } -pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 { +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, diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index a8c3e13e33..de956f1525 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -37,7 +37,7 @@ 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.ContextRef), + llvm_handle_pool: std.atomic.Stack(*llvm.Context), lld_lock: event.Lock, /// TODO pool these so that it doesn't have to lock @@ -55,12 +55,12 @@ pub const ZigCompiler = struct { var seed_bytes: [@sizeOf(u64)]u8 = undefined; try std.os.getRandomBytes(seed_bytes[0..]); - const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); + const seed = mem.readIntNative(u64, &seed_bytes); return ZigCompiler{ .loop = loop, .lld_lock = event.Lock.init(loop), - .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).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), }; @@ -70,7 +70,7 @@ pub const ZigCompiler = struct { fn deinit(self: *ZigCompiler) void { self.lld_lock.deinit(); while (self.llvm_handle_pool.pop()) |node| { - c.LLVMContextDispose(node.data); + llvm.ContextDispose(node.data); self.loop.allocator.destroy(node); } } @@ -80,13 +80,14 @@ pub const ZigCompiler = struct { pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle { if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; - const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; - errdefer c.LLVMContextDispose(context_ref); + 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.ContextRef).Node{ + const node = try self.loop.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); return LlvmHandle{ .node = node }; @@ -113,7 +114,7 @@ pub const ZigCompiler = struct { }; pub const LlvmHandle = struct { - node: *std.atomic.Stack(llvm.ContextRef).Node, + node: *std.atomic.Stack(*llvm.Context).Node, pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void { zig_compiler.llvm_handle_pool.push(self.node); @@ -127,7 +128,7 @@ pub const Compilation = struct { llvm_triple: Buffer, root_src_path: ?[]const u8, target: Target, - llvm_target: llvm.TargetRef, + llvm_target: *llvm.Target, build_mode: builtin.Mode, zig_lib_dir: []const u8, zig_std_dir: []const u8, @@ -211,8 +212,8 @@ pub const Compilation = struct { false_value: *Value.Bool, noreturn_value: *Value.NoReturn, - target_machine: llvm.TargetMachineRef, - target_data_ref: llvm.TargetDataRef, + target_machine: *llvm.TargetMachine, + target_data_ref: *llvm.TargetData, target_layout_str: [*]u8, target_ptr_bits: u32, @@ -300,6 +301,7 @@ pub const Compilation = struct { UserResourceLimitReached, InvalidUtf8, BadPathName, + DeviceBusy, }; pub const Event = union(enum) { @@ -485,7 +487,7 @@ 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.os.path.join(comp.arena(), zig_lib_dir, "std"); + comp.zig_std_dir = try std.os.path.join(comp.arena(), [][]const u8{ zig_lib_dir, "std" }); const opt_level = switch (build_mode) { builtin.Mode.Debug => llvm.CodeGenLevelNone, @@ -595,7 +597,8 @@ pub const Compilation = struct { } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.arena().create(Type.MetaType{ + comp.meta_type = try comp.arena().create(Type.MetaType); + comp.meta_type.* = Type.MetaType{ .base = Type{ .name = "type", .base = Value{ @@ -607,12 +610,13 @@ pub const Compilation = struct { .abi_alignment = Type.AbiAlignment.init(comp.loop), }, .value = undefined, - }); + }; comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typ = &comp.meta_type.base; assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null); - comp.void_type = try comp.arena().create(Type.Void{ + comp.void_type = try comp.arena().create(Type.Void); + comp.void_type.* = Type.Void{ .base = Type{ .name = "void", .base = Value{ @@ -623,10 +627,11 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); - comp.noreturn_type = try comp.arena().create(Type.NoReturn{ + comp.noreturn_type = try comp.arena().create(Type.NoReturn); + comp.noreturn_type.* = Type.NoReturn{ .base = Type{ .name = "noreturn", .base = Value{ @@ -637,10 +642,11 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); - comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{ + comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt); + comp.comptime_int_type.* = Type.ComptimeInt{ .base = Type{ .name = "comptime_int", .base = Value{ @@ -651,10 +657,11 @@ pub const Compilation = struct { .id = builtin.TypeId.ComptimeInt, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); - comp.bool_type = try comp.arena().create(Type.Bool{ + comp.bool_type = try comp.arena().create(Type.Bool); + comp.bool_type.* = Type.Bool{ .base = Type{ .name = "bool", .base = Value{ @@ -665,45 +672,50 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); - comp.void_value = try comp.arena().create(Value.Void{ + comp.void_value = try comp.arena().create(Value.Void); + comp.void_value.* = Value.Void{ .base = Value{ .id = Value.Id.Void, .typ = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - }); + }; - comp.true_value = try comp.arena().create(Value.Bool{ + comp.true_value = try comp.arena().create(Value.Bool); + comp.true_value.* = Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = true, - }); + }; - comp.false_value = try comp.arena().create(Value.Bool{ + comp.false_value = try comp.arena().create(Value.Bool); + comp.false_value.* = Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = false, - }); + }; - comp.noreturn_value = try comp.arena().create(Value.NoReturn{ + comp.noreturn_value = try comp.arena().create(Value.NoReturn); + comp.noreturn_value.* = Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typ = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - }); + }; for (CInt.list) |cint, i| { - const c_int_type = try comp.arena().create(Type.Int{ + const c_int_type = try comp.arena().create(Type.Int); + c_int_type.* = Type.Int{ .base = Type{ .name = cint.zig_name, .base = Value{ @@ -719,11 +731,12 @@ pub const Compilation = struct { .bit_count = comp.target.cIntTypeSizeInBits(cint.id), }, .garbage_node = undefined, - }); + }; comp.c_int_types[i] = c_int_type; assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null); } - comp.u8_type = try comp.arena().create(Type.Int{ + comp.u8_type = try comp.arena().create(Type.Int); + comp.u8_type.* = Type.Int{ .base = Type{ .name = "u8", .base = Value{ @@ -739,7 +752,7 @@ pub const Compilation = struct { .bit_count = 8, }, .garbage_node = undefined, - }); + }; assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); } @@ -828,7 +841,7 @@ pub const Compilation = struct { }; errdefer self.gpa().free(source_code); - const tree = try self.gpa().createOne(ast.Tree); + const tree = try self.gpa().create(ast.Tree); tree.* = try std.zig.parse(self.gpa(), source_code); errdefer { tree.deinit(); @@ -924,7 +937,8 @@ pub const Compilation = struct { } } else { // add new decl - const fn_decl = try self.gpa().create(Decl.Fn{ + const fn_decl = try self.gpa().create(Decl.Fn); + fn_decl.* = Decl.Fn{ .base = Decl{ .id = Decl.Id.Fn, .name = name, @@ -935,7 +949,7 @@ pub const Compilation = struct { }, .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, - }); + }; tree_scope.base.ref(); errdefer self.gpa().destroy(fn_decl); @@ -1139,12 +1153,13 @@ pub const Compilation = struct { } } - const link_lib = try self.gpa().create(LinkLib{ + const link_lib = try self.gpa().create(LinkLib); + link_lib.* = LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, .symbols = ArrayList([]u8).init(self.gpa()), - }); + }; try self.link_libs_list.append(link_lib); if (is_libc) { self.libc_link_lib = link_lib; @@ -1183,7 +1198,7 @@ pub const Compilation = struct { const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); defer self.gpa().free(file_name); - const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); + const full_path = try os.path.join(self.gpa(), [][]const u8{ tmp_dir, file_name[0..] }); errdefer self.gpa().free(full_path); return Buffer.fromOwnedSlice(self.gpa(), full_path); @@ -1204,7 +1219,7 @@ pub const Compilation = struct { const zig_dir_path = try getZigDir(self.gpa()); defer self.gpa().free(zig_dir_path); - const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); + const tmp_dir = try os.path.join(self.arena(), [][]const u8{ zig_dir_path, comp_dir_name[0..] }); try os.makePath(self.gpa(), tmp_dir); return tmp_dir; } diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 0e552fde7e..fc49fad410 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -118,7 +118,8 @@ pub const Msg = struct { const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath); errdefer comp.gpa().free(realpath); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = text, .realpath = realpath, .data = Data{ @@ -128,7 +129,7 @@ pub const Msg = struct { .span = span, }, }, - }); + }; tree_scope.base.ref(); return msg; } @@ -139,13 +140,14 @@ pub const Msg = struct { const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath); errdefer comp.gpa().free(realpath_copy); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = text, .realpath = realpath_copy, .data = Data{ .Cli = Cli{ .allocator = comp.gpa() }, }, - }); + }; return msg; } @@ -164,7 +166,8 @@ pub const Msg = struct { var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; try parse_error.render(&tree_scope.tree.tokens, out_stream); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = undefined, .realpath = realpath_copy, .data = Data{ @@ -177,7 +180,7 @@ pub const Msg = struct { }, }, }, - }); + }; tree_scope.base.ref(); msg.text = text_buf.toOwnedSlice(); return msg; @@ -203,7 +206,8 @@ pub const Msg = struct { var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; try parse_error.render(&tree.tokens, out_stream); - const msg = try allocator.create(Msg{ + const msg = try allocator.create(Msg); + msg.* = Msg{ .text = undefined, .realpath = realpath_copy, .data = Data{ @@ -216,7 +220,7 @@ pub const Msg = struct { }, }, }, - }); + }; msg.text = text_buf.toOwnedSlice(); errdefer allocator.destroy(msg); diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index d41f82f755..0a7f63b4f1 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 os.path.join(allocator, test_path, "lib", "zig"); + const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); errdefer allocator.free(test_zig_dir); - const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); + const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" }); defer allocator.free(test_index_file); var file = try os.File.openRead(test_index_file); diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 562765b354..dc1b0dc943 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -67,7 +67,7 @@ pub const Inst = struct { parent: ?*Inst, /// populated durign codegen - llvm_value: ?llvm.ValueRef, + llvm_value: ?*llvm.Value, pub fn cast(base: *Inst, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { @@ -129,7 +129,7 @@ pub const Inst = struct { } } - pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { + 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), @@ -313,10 +313,10 @@ pub const Inst = struct { return new_inst; } - pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value { const fn_ref = self.params.fn_ref.llvm_value.?; - const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len); + const args = try ofile.arena.alloc(*llvm.Value, self.params.args.len); for (self.params.args) |arg, i| { args[i] = arg.llvm_value.?; } @@ -360,7 +360,7 @@ pub const Inst = struct { return new_inst; } - pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value { return self.base.val.KnownValue.getLlvmConst(ofile); } }; @@ -392,7 +392,7 @@ pub const Inst = struct { return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); } - pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value { const value = self.params.return_value.llvm_value; const return_type = self.params.return_value.getKnownType(); @@ -540,7 +540,7 @@ pub const Inst = struct { } } - pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef { + 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, @@ -596,7 +596,7 @@ pub const Inst = struct { return new_inst; } - pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value { const child_type = self.base.getKnownType(); if (!child_type.hasBits()) { return null; @@ -935,8 +935,8 @@ pub const BasicBlock = struct { ref_instruction: ?*Inst, /// for codegen - llvm_block: llvm.BasicBlockRef, - llvm_exit_block: llvm.BasicBlockRef, + llvm_block: *llvm.BasicBlock, + llvm_exit_block: *llvm.BasicBlock, /// the basic block that is derived from this one in analysis child: ?*BasicBlock, @@ -1021,12 +1021,13 @@ pub const Builder = struct { pub const Error = Analyze.Error; pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder { - const code = try comp.gpa().create(Code{ + const code = try comp.gpa().create(Code); + code.* = Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(comp.gpa()), .return_type = null, .tree_scope = tree_scope, - }); + }; code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); errdefer code.destroy(comp.gpa()); @@ -1052,7 +1053,8 @@ 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 { - const basic_block = try self.arena().create(BasicBlock{ + const basic_block = try self.arena().create(BasicBlock); + basic_block.* = BasicBlock{ .ref_count = 0, .name_hint = name_hint, .debug_id = self.next_debug_id, @@ -1063,7 +1065,7 @@ pub const Builder = struct { .ref_instruction = null, .llvm_block = undefined, .llvm_exit_block = undefined, - }); + }; self.next_debug_id += 1; return basic_block; } @@ -1774,7 +1776,8 @@ pub const Builder = struct { params: I.Params, is_generated: bool, ) !*Inst { - const inst = try self.arena().create(I{ + const inst = try self.arena().create(I); + inst.* = I{ .base = Inst{ .id = Inst.typeToId(I), .is_generated = is_generated, @@ -1793,7 +1796,7 @@ pub const Builder = struct { .owner_bb = self.current_basic_block, }, .params = params, - }); + }; // Look at the params and ref() other instructions comptime var i = 0; diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 1c5d111c5a..d3a461bcd9 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -57,10 +57,10 @@ pub const LibCInstallation = struct { const contents = try std.io.readFileAlloc(allocator, libc_file); defer allocator.free(contents); - var it = std.mem.split(contents, "\n"); + var it = std.mem.tokenize(contents, "\n"); while (it.next()) |line| { if (line.len == 0 or line[0] == '#') continue; - var line_it = std.mem.split(line, "="); + var line_it = std.mem.separate(line, "="); const name = line_it.next() orelse { try stderr.print("missing equal sign after field name\n"); return error.ParseError; @@ -154,8 +154,8 @@ pub const LibCInstallation = struct { c.ZigFindWindowsSdkError.None => { windows_sdk = sdk; - if (sdk.msvc_lib_dir_ptr) |ptr| { - self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); + 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]); } try group.call(findNativeKernel32LibDir, self, loop, sdk); try group.call(findNativeIncludeDirWindows, self, loop, sdk); @@ -213,7 +213,7 @@ pub const LibCInstallation = struct { }, } - var it = std.mem.split(exec_result.stderr, "\n\r"); + var it = std.mem.tokenize(exec_result.stderr, "\n\r"); var search_paths = std.ArrayList([]const u8).init(loop.allocator); defer search_paths.deinit(); while (it.next()) |line| { @@ -230,7 +230,7 @@ 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 std.os.path.join(loop.allocator, search_path, "stdlib.h"); + const stdlib_path = try std.os.path.join(loop.allocator, [][]const u8{ search_path, "stdlib.h" }); defer loop.allocator.free(stdlib_path); if (try fileExists(stdlib_path)) { @@ -254,7 +254,10 @@ pub const LibCInstallation = struct { const stream = &std.io.BufferOutStream.init(&result_buf).stream; try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); - const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); + const stdlib_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "stdlib.h" }, + ); defer loop.allocator.free(stdlib_path); if (try fileExists(stdlib_path)) { @@ -283,7 +286,10 @@ pub const LibCInstallation = struct { builtin.Arch.aarch64v8 => try stream.write("arm"), else => return error.UnsupportedArchitecture, } - const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); + const ucrt_lib_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }, + ); defer loop.allocator.free(ucrt_lib_path); if (try fileExists(ucrt_lib_path)) { self.lib_dir = result_buf.toOwnedSlice(); @@ -358,7 +364,10 @@ pub const LibCInstallation = struct { builtin.Arch.aarch64v8 => try stream.write("arm\\"), else => return error.UnsupportedArchitecture, } - const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); + const kernel32_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }, + ); defer loop.allocator.free(kernel32_path); if (try fileExists(kernel32_path)) { self.kernel32_lib_dir = result_buf.toOwnedSlice(); @@ -410,7 +419,7 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo return error.CCompilerCrashed; }, } - var it = std.mem.split(exec_result.stdout, "\n\r"); + var it = std.mem.tokenize(exec_result.stdout, "\n\r"); const line = it.next() orelse return error.LibCRuntimeNotFound; const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound; @@ -428,20 +437,20 @@ const Search = struct { fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { var search_end: usize = 0; - if (sdk.path10_ptr) |path10_ptr| { - if (sdk.version10_ptr) |ver10_ptr| { + if (sdk.path10_ptr != 0) { + if (sdk.version10_ptr != 0) { search_buf[search_end] = Search{ - .path = path10_ptr[0..sdk.path10_len], - .version = ver10_ptr[0..sdk.version10_len], + .path = sdk.path10_ptr[0..sdk.path10_len], + .version = sdk.version10_ptr[0..sdk.version10_len], }; search_end += 1; } } - if (sdk.path81_ptr) |path81_ptr| { - if (sdk.version81_ptr) |ver81_ptr| { + if (sdk.path81_ptr != 0) { + if (sdk.version81_ptr != 0) { search_buf[search_end] = Search{ - .path = path81_ptr[0..sdk.path81_len], - .version = ver81_ptr[0..sdk.version81_len], + .path = sdk.path81_ptr[0..sdk.path81_len], + .version = sdk.version81_ptr[0..sdk.version81_len], }; search_end += 1; } diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 1b32533ebc..fadc9b0189 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { } fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { - const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename); + const full_path = try std.os.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); } diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 778d3fae07..5cb95682ab 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -11,45 +11,31 @@ const assert = @import("std").debug.assert; pub const AttributeIndex = c_uint; pub const Bool = c_int; -pub const BuilderRef = removeNullability(c.LLVMBuilderRef); -pub const ContextRef = removeNullability(c.LLVMContextRef); -pub const ModuleRef = removeNullability(c.LLVMModuleRef); -pub const ValueRef = removeNullability(c.LLVMValueRef); -pub const TypeRef = removeNullability(c.LLVMTypeRef); -pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); -pub const AttributeRef = removeNullability(c.LLVMAttributeRef); -pub const TargetRef = removeNullability(c.LLVMTargetRef); -pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); -pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); +pub const Builder = c.LLVMBuilderRef.Child.Child; +pub const Context = c.LLVMContextRef.Child.Child; +pub const Module = c.LLVMModuleRef.Child.Child; +pub const Value = c.LLVMValueRef.Child.Child; +pub const Type = c.LLVMTypeRef.Child.Child; +pub const BasicBlock = c.LLVMBasicBlockRef.Child.Child; +pub const Attribute = c.LLVMAttributeRef.Child.Child; +pub const Target = c.LLVMTargetRef.Child.Child; +pub const TargetMachine = c.LLVMTargetMachineRef.Child.Child; +pub const TargetData = c.LLVMTargetDataRef.Child.Child; pub const DIBuilder = c.ZigLLVMDIBuilder; +pub const DIFile = c.ZigLLVMDIFile; +pub const DICompileUnit = c.ZigLLVMDICompileUnit; pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; -pub const AddFunction = c.LLVMAddFunction; -pub const AddGlobal = c.LLVMAddGlobal; pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; -pub const ArrayType = c.LLVMArrayType; -pub const BuildLoad = c.LLVMBuildLoad; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstArray = c.LLVMConstArray; pub const ConstBitCast = c.LLVMConstBitCast; -pub const ConstInt = c.LLVMConstInt; pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision; pub const ConstNeg = c.LLVMConstNeg; -pub const ConstNull = c.LLVMConstNull; -pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStructInContext = c.LLVMConstStructInContext; -pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData; -pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; -pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit; -pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder; -pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; -pub const CreateFile = c.ZigLLVMCreateFile; -pub const CreateStringAttribute = c.LLVMCreateStringAttribute; -pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout; -pub const CreateTargetMachine = c.LLVMCreateTargetMachine; pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; pub const DisposeBuilder = c.LLVMDisposeBuilder; pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; @@ -62,9 +48,7 @@ pub const DumpModule = c.LLVMDumpModule; pub const FP128TypeInContext = c.LLVMFP128TypeInContext; pub const FloatTypeInContext = c.LLVMFloatTypeInContext; pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; -pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; -pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; pub const GetUndef = c.LLVMGetUndef; pub const HalfTypeInContext = c.LLVMHalfTypeInContext; pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; @@ -81,14 +65,11 @@ pub const Int64TypeInContext = c.LLVMInt64TypeInContext; pub const Int8TypeInContext = c.LLVMInt8TypeInContext; pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext; pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext; -pub const IntTypeInContext = c.LLVMIntTypeInContext; pub const LabelTypeInContext = c.LLVMLabelTypeInContext; pub const MDNodeInContext = c.LLVMMDNodeInContext; pub const MDStringInContext = c.LLVMMDStringInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; -pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; -pub const PointerType = c.LLVMPointerType; pub const SetAlignment = c.LLVMSetAlignment; pub const SetDataLayout = c.LLVMSetDataLayout; pub const SetGlobalConstant = c.LLVMSetGlobalConstant; @@ -99,50 +80,146 @@ pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; pub const SetVolatile = c.LLVMSetVolatile; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; -pub const VoidTypeInContext = c.LLVMVoidTypeInContext; 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; + +pub const ConstStringInContext = LLVMConstStringInContext; +extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value; + +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; + +pub const ConstNull = LLVMConstNull; +extern fn LLVMConstNull(Ty: *Type) ?*Value; + +pub const CreateStringAttribute = LLVMCreateStringAttribute; +extern fn LLVMCreateStringAttribute( + C: *Context, + K: [*]const u8, + KLength: c_uint, + V: [*]const u8, + VLength: c_uint, +) ?*Attribute; + +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; + +pub const CreateCompileUnit = ZigLLVMCreateCompileUnit; +extern fn ZigLLVMCreateCompileUnit( + dibuilder: *DIBuilder, + lang: c_uint, + difile: *DIFile, + producer: [*]const u8, + is_optimized: bool, + flags: [*]const u8, + runtime_version: c_uint, + split_name: [*]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; + +pub const ArrayType = LLVMArrayType; +extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type; + +pub const CreateDIBuilder = ZigLLVMCreateDIBuilder; +extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) ?*DIBuilder; + +pub const PointerType = LLVMPointerType; +extern fn LLVMPointerType(ElementType: *Type, AddressSpace: c_uint) ?*Type; + +pub const CreateBuilderInContext = LLVMCreateBuilderInContext; +extern fn LLVMCreateBuilderInContext(C: *Context) ?*Builder; + +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; + +pub const VoidTypeInContext = LLVMVoidTypeInContext; +extern fn LLVMVoidTypeInContext(C: *Context) ?*Type; + +pub const ContextCreate = LLVMContextCreate; +extern fn LLVMContextCreate() ?*Context; + +pub const ContextDispose = LLVMContextDispose; +extern fn LLVMContextDispose(C: *Context) void; + +pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData; +extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*]u8; + +pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout; +extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData; + +pub const CreateTargetMachine = LLVMCreateTargetMachine; +extern fn LLVMCreateTargetMachine( + T: *Target, + Triple: [*]const u8, + CPU: [*]const u8, + Features: [*]const u8, + Level: CodeGenOptLevel, + Reloc: RelocMode, + CodeModel: CodeModel, +) ?*TargetMachine; + +pub const GetHostCPUName = LLVMGetHostCPUName; +extern fn LLVMGetHostCPUName() ?[*]u8; + +pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; +extern fn ZigLLVMGetNativeFeatures() ?[*]u8; + pub const GetElementType = LLVMGetElementType; -extern fn LLVMGetElementType(Ty: TypeRef) TypeRef; +extern fn LLVMGetElementType(Ty: *Type) *Type; pub const TypeOf = LLVMTypeOf; -extern fn LLVMTypeOf(Val: ValueRef) TypeRef; +extern fn LLVMTypeOf(Val: *Value) *Type; pub const BuildStore = LLVMBuildStore; -extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef; +extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value; pub const BuildAlloca = LLVMBuildAlloca; -extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef; +extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*]const u8) ?*Value; pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; -pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef; +pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value; pub const GetTargetFromTriple = LLVMGetTargetFromTriple; -extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; +extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: **Target, ErrorMessage: ?*[*]u8) Bool; pub const VerifyModule = LLVMVerifyModule; -extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; +extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; pub const GetInsertBlock = LLVMGetInsertBlock; -extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef; +extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock; pub const FunctionType = LLVMFunctionType; extern fn LLVMFunctionType( - ReturnType: TypeRef, - ParamTypes: [*]TypeRef, + ReturnType: *Type, + ParamTypes: [*]*Type, ParamCount: c_uint, IsVarArg: Bool, -) ?TypeRef; +) ?*Type; pub const GetParam = LLVMGetParam; -extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef; +extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value; pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; -extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef; +extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*]const u8) ?*BasicBlock; pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; -extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void; +extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void; pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction; pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; @@ -190,17 +267,17 @@ pub const FnInline = extern enum { }; fn removeNullability(comptime T: type) type { - comptime assert(@typeId(T) == builtin.TypeId.Optional); - return T.Child; + comptime assert(@typeInfo(T).Pointer.size == @import("builtin").TypeInfo.Pointer.Size.C); + return *T.Child; } pub const BuildRet = LLVMBuildRet; -extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef; +extern fn LLVMBuildRet(arg0: *Builder, V: ?*Value) ?*Value; pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; extern fn ZigLLVMTargetMachineEmitToFile( - targ_machine_ref: TargetMachineRef, - module_ref: ModuleRef, + targ_machine_ref: *TargetMachine, + module_ref: *Module, filename: [*]const u8, output_type: EmitOutputType, error_message: *[*]u8, @@ -209,6 +286,6 @@ extern fn ZigLLVMTargetMachineEmitToFile( ) bool; pub const BuildCall = ZigLLVMBuildCall; -extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef; +extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?*Value; pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 0742cbfe65..42556beaed 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined; var stderr: *io.OutStream(os.File.WriteError) = undefined; var stdout: *io.OutStream(os.File.WriteError) = undefined; -const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB +pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB const usage = \\usage: zig [command] [options] @@ -351,7 +351,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co const root_name = if (provided_name) |n| n else blk: { if (root_source_file) |file| { const basename = os.path.basename(file); - var it = mem.split(basename, "."); + var it = mem.separate(basename, "."); break :blk it.next() orelse basename; } else { try stderr.write("--name [name] not provided and unable to infer\n"); @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { return buildOutputType(allocator, args, Compilation.Kind.Obj); } -const usage_fmt = +pub const usage_fmt = \\usage: zig fmt [file]... \\ \\ Formats the input files and modifies them in-place. @@ -527,7 +527,7 @@ const usage_fmt = \\ ; -const args_fmt_spec = []Flag{ +pub const args_fmt_spec = []Flag{ Flag.Bool("--help"), Flag.Bool("--check"), Flag.Option("--color", []const []const u8{ @@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro var group = event.Group(FmtError!void).init(fmt.loop); while (try dir.next()) |entry| { if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name); + const full_path = try os.path.join(fmt.loop.allocator, [][]const u8{ file_path, entry.name }); try group.call(fmtPath, fmt, full_path, check_mode); } } @@ -944,12 +944,13 @@ const CliPkg = struct { parent: ?*CliPkg, pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { - var pkg = try allocator.create(CliPkg{ + var pkg = try allocator.create(CliPkg); + pkg.* = CliPkg{ .name = name, .path = path, .children = ArrayList(*CliPkg).init(allocator), .parent = parent, - }); + }; return pkg; } diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig index 720b279651..0d31731b55 100644 --- a/src-self-hosted/package.zig +++ b/src-self-hosted/package.zig @@ -15,11 +15,13 @@ pub const Package = struct { /// makes internal copies of root_src_dir and root_src_path /// allocator should be an arena allocator because Package never frees anything pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { - return allocator.create(Package{ + const ptr = try allocator.create(Package); + ptr.* = Package{ .root_src_dir = try Buffer.init(allocator, root_src_dir), .root_src_path = try Buffer.init(allocator, root_src_path), .table = Table.init(allocator), - }); + }; + return ptr; } pub fn add(self: *Package, name: []const u8, package: *Package) !void { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 43d3b5a784..9a84ad256e 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -120,7 +120,7 @@ pub const Scope = struct { /// Creates a Root scope with 1 reference /// Takes ownership of realpath pub fn create(comp: *Compilation, realpath: []u8) !*Root { - const self = try comp.gpa().createOne(Root); + const self = try comp.gpa().create(Root); self.* = Root{ .base = Scope{ .id = Id.Root, @@ -150,7 +150,7 @@ pub const Scope = struct { /// Creates a scope with 1 reference /// Takes ownership of tree, will deinit and destroy when done. pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree { - const self = try comp.gpa().createOne(AstTree); + const self = try comp.gpa().create(AstTree); self.* = AstTree{ .base = undefined, .tree = tree, @@ -182,7 +182,7 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Decls { - const self = try comp.gpa().createOne(Decls); + const self = try comp.gpa().create(Decls); self.* = Decls{ .base = undefined, .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), @@ -235,7 +235,7 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Block { - const self = try comp.gpa().createOne(Block); + const self = try comp.gpa().create(Block); self.* = Block{ .base = undefined, .incoming_values = undefined, @@ -262,7 +262,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { - const self = try comp.gpa().createOne(FnDef); + const self = try comp.gpa().create(FnDef); self.* = FnDef{ .base = undefined, .fn_val = null, @@ -281,7 +281,7 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { - const self = try comp.gpa().createOne(CompTime); + const self = try comp.gpa().create(CompTime); self.* = CompTime{ .base = undefined }; self.base.init(Id.CompTime, parent); return self; @@ -309,7 +309,7 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.gpa().createOne(Defer); + const self = try comp.gpa().create(Defer); self.* = Defer{ .base = undefined, .defer_expr_scope = defer_expr_scope, @@ -333,7 +333,7 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.gpa().createOne(DeferExpr); + const self = try comp.gpa().create(DeferExpr); self.* = DeferExpr{ .base = undefined, .expr_node = expr_node, @@ -362,7 +362,7 @@ pub const Scope = struct { pub const Param = struct { index: usize, typ: *Type, - llvm_value: llvm.ValueRef, + llvm_value: *llvm.Value, }; pub fn createParam( @@ -398,7 +398,7 @@ pub const Scope = struct { } fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { - const self = try comp.gpa().createOne(Var); + const self = try comp.gpa().create(Var); self.* = Var{ .base = undefined, .name = name, diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 218353c9d7..121242b505 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -457,8 +457,8 @@ pub const Target = union(enum) { } } - pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { - var result: llvm.TargetRef = undefined; + 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); @@ -520,7 +520,7 @@ pub const Target = union(enum) { => return 64, }, - builtin.Os.windows => switch (id) { + builtin.Os.windows, builtin.Os.uefi => switch (id) { CInt.Id.Short, CInt.Id.UShort, => return 16, diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index ff5b96df84..4be6d53932 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -4,7 +4,7 @@ const builtin = @import("builtin"); const Target = @import("target.zig").Target; const Compilation = @import("compilation.zig").Compilation; const introspect = @import("introspect.zig"); -const assertOrPanic = std.debug.assertOrPanic; +const testing = std.testing; const errmsg = @import("errmsg.zig"); const ZigCompiler = @import("compilation.zig").ZigCompiler; @@ -87,7 +87,7 @@ pub const TestContext = struct { ) !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.os.path.join(allocator, tmp_dir_name, file_index, file1); + const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 }); if (std.os.path.dirname(file1_path)) |dirname| { try std.os.makePath(allocator, dirname); @@ -120,7 +120,7 @@ pub const TestContext = struct { ) !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.os.path.join(allocator, tmp_dir_name, file_index, file1); + const file1_path = try std.os.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()); if (std.os.path.dirname(file1_path)) |dirname| { @@ -210,7 +210,7 @@ pub const TestContext = struct { @panic("build incorrectly failed"); }, Compilation.Event.Fail => |msgs| { - assertOrPanic(msgs.len != 0); + testing.expect(msgs.len != 0); for (msgs) |msg| { if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) { const span = msg.getSpan(); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index aa00bb876d..7d611bb787 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -44,14 +44,15 @@ pub const Type = struct { 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), } } pub fn getLlvmType( base: *Type, allocator: *Allocator, - llvm_context: llvm.ContextRef, - ) (error{OutOfMemory}!llvm.TypeRef) { + llvm_context: *llvm.Context, + ) (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), @@ -77,6 +78,7 @@ pub const Type = struct { 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), } } @@ -103,6 +105,7 @@ pub const Type = struct { Id.Enum, Id.Fn, Id.Promise, + Id.Vector, => return false, Id.Struct => @panic("TODO"), @@ -135,6 +138,7 @@ pub const Type = struct { Id.Float, Id.Fn, Id.Promise, + Id.Vector, => return true, Id.Pointer => { @@ -192,7 +196,7 @@ pub const Type = struct { } /// If you have an llvm conext handy, you can use it here. - pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { + 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.*; base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); @@ -201,7 +205,7 @@ pub const Type = struct { } /// Lower level function that does the work. See getAbiAlignment. - async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { + async 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)); } @@ -214,7 +218,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -409,7 +413,7 @@ pub const Type = struct { key.ref(); errdefer key.deref(comp); - const self = try comp.gpa().createOne(Fn); + const self = try comp.gpa().create(Fn); self.* = Fn{ .base = undefined, .key = key, @@ -492,13 +496,13 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + 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, else => try normal.return_type.getLlvmType(allocator, llvm_context), }; - const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len); + const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len); defer allocator.free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context); @@ -555,7 +559,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -611,11 +615,12 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Int{ + const self = try comp.gpa().create(Int); + self.* = Int{ .base = undefined, .key = key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; @@ -653,7 +658,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type { return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory; } }; @@ -665,7 +670,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -777,17 +782,19 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Pointer{ + const self = try comp.gpa().create(Pointer); + self.* = Pointer{ .base = undefined, .key = normal_key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const size_str = switch (self.key.size) { Size.One => "*", Size.Many => "[*]", Size.Slice => "[]", + Size.C => "[*c]", }; const mut_str = switch (self.key.mut) { Mut.Const => "const ", @@ -829,7 +836,7 @@ pub const Type = struct { return self; } - pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type { const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context); return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory; } @@ -875,11 +882,12 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Array{ + const self = try comp.gpa().create(Array); + self.* = Array{ .base = undefined, .key = key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name); @@ -896,12 +904,24 @@ pub const Type = struct { return self; } - pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type { const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context); return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory; } }; + pub const Vector = struct { + base: Type, + + pub fn destroy(self: *Vector, comp: *Compilation) void { + comp.gpa().destroy(self); + } + + pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { + @panic("TODO"); + } + }; + pub const ComptimeFloat = struct { base: Type, @@ -947,7 +967,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -959,7 +979,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -971,7 +991,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -983,7 +1003,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -995,7 +1015,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -1015,7 +1035,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -1035,7 +1055,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -1047,7 +1067,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type { @panic("TODO"); } }; @@ -1069,6 +1089,7 @@ fn hashAny(x: var, comptime seed: u64) u32 { 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, } }, builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e6dca4eff7..d8c0f7b5c8 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -57,7 +57,7 @@ pub const Value = struct { std.debug.warn("{}", @tagName(base.id)); } - pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { + 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), @@ -135,14 +135,15 @@ pub const Value = struct { symbol_name: Buffer, pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto { - const self = try comp.gpa().create(FnProto{ + const self = try comp.gpa().create(FnProto); + self.* = FnProto{ .base = Value{ .id = Value.Id.FnProto, .typ = &fn_type.base, .ref_count = std.atomic.Int(usize).init(1), }, .symbol_name = symbol_name, - }); + }; fn_type.base.base.ref(); return self; } @@ -152,7 +153,7 @@ pub const Value = struct { comp.gpa().destroy(self); } - pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef { + pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?*llvm.Value { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, @@ -190,14 +191,16 @@ pub const Value = struct { /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { - const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ + const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node); + link_set_node.* = Compilation.FnLinkSet.Node{ .data = null, .next = undefined, .prev = undefined, - }); + }; errdefer comp.gpa().destroy(link_set_node); - const self = try comp.gpa().create(Fn{ + const self = try comp.gpa().create(Fn); + self.* = Fn{ .base = Value{ .id = Value.Id.Fn, .typ = &fn_type.base, @@ -209,7 +212,7 @@ pub const Value = struct { .symbol_name = symbol_name, .containing_object = Buffer.initNull(comp.gpa()), .link_set_node = link_set_node, - }); + }; fn_type.base.base.ref(); fndef_scope.fn_val = self; fndef_scope.base.ref(); @@ -235,7 +238,7 @@ pub const Value = struct { /// We know that the function definition will end up in an .o file somewhere. /// Here, all we have to do is generate a global prototype. /// TODO cache the prototype per ObjectFile - pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef { + pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?*llvm.Value { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, @@ -280,8 +283,8 @@ pub const Value = struct { comp.gpa().destroy(self); } - pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { - const llvm_type = llvm.Int1TypeInContext(ofile.context); + pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) !?*llvm.Value { + const llvm_type = llvm.Int1TypeInContext(ofile.context) orelse return error.OutOfMemory; if (self.x) { return llvm.ConstAllOnes(llvm_type); } else { @@ -353,7 +356,8 @@ pub const Value = struct { var ptr_type_consumed = false; errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); - const self = try comp.gpa().create(Value.Ptr{ + const self = try comp.gpa().create(Value.Ptr); + self.* = Value.Ptr{ .base = Value{ .id = Value.Id.Ptr, .typ = &ptr_type.base, @@ -366,7 +370,7 @@ pub const Value = struct { }, }, .mut = Mut.CompTimeConst, - }); + }; ptr_type_consumed = true; errdefer comp.gpa().destroy(self); @@ -377,7 +381,7 @@ pub const Value = struct { comp.gpa().destroy(self); } - pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef { + pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?*llvm.Value { 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) { @@ -387,7 +391,7 @@ pub const Value = struct { 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.ValueRef{ + const 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, }; @@ -430,14 +434,15 @@ pub const Value = struct { }) catch unreachable); errdefer array_type.base.base.deref(comp); - const self = try comp.gpa().create(Value.Array{ + const self = try comp.gpa().create(Value.Array); + self.* = Value.Array{ .base = Value{ .id = Value.Id.Array, .typ = &array_type.base, .ref_count = std.atomic.Int(usize).init(1), }, .special = Special{ .OwnedBuffer = buffer }, - }); + }; errdefer comp.gpa().destroy(self); return self; @@ -454,7 +459,7 @@ pub const Value = struct { comp.gpa().destroy(self); } - pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef { + pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value { switch (self.special) { Special.Undefined => { const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); @@ -509,14 +514,15 @@ pub const Value = struct { big_int: std.math.big.Int, pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int { - const self = try comp.gpa().create(Value.Int{ + const self = try comp.gpa().create(Value.Int); + self.* = Value.Int{ .base = Value{ .id = Value.Id.Int, .typ = typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, - }); + }; typ.base.ref(); errdefer comp.gpa().destroy(self); @@ -528,7 +534,7 @@ pub const Value = struct { return self; } - pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef { + pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value { switch (self.base.typ.id) { Type.Id.Int => { const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context); @@ -557,14 +563,15 @@ pub const Value = struct { old.base.typ.base.ref(); errdefer old.base.typ.base.deref(comp); - const new = try comp.gpa().create(Value.Int{ + const new = try comp.gpa().create(Value.Int); + new.* = Value.Int{ .base = Value{ .id = Value.Id.Int, .typ = old.base.typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, - }); + }; errdefer comp.gpa().destroy(new); new.big_int = try old.big_int.clone(); diff --git a/src/all_types.hpp b/src/all_types.hpp index f7ada09a57..6fbd987b9e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -56,9 +56,6 @@ struct IrExecutable { size_t next_debug_id; size_t *backward_branch_count; size_t backward_branch_quota; - bool invalid; - bool is_inline; - bool is_generic_instantiation; ZigFn *fn_entry; Buf *c_import_buf; AstNode *source_node; @@ -78,6 +75,10 @@ struct IrExecutable { IrBasicBlock *coro_suspend_block; IrBasicBlock *coro_final_cleanup_block; ZigVar *coro_allocator_var; + + bool invalid; + bool is_inline; + bool is_generic_instantiation; }; enum OutType { @@ -90,6 +91,9 @@ enum OutType { enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, + ConstParentIdErrUnionCode, + ConstParentIdErrUnionPayload, + ConstParentIdOptionalPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -107,6 +111,15 @@ struct ConstParent { ConstExprValue *struct_val; size_t field_index; } p_struct; + struct { + ConstExprValue *err_union_val; + } p_err_union_code; + struct { + ConstExprValue *err_union_val; + } p_err_union_payload; + struct { + ConstExprValue *optional_val; + } p_optional_payload; struct { ConstExprValue *union_val; } p_union; @@ -118,13 +131,11 @@ struct ConstParent { struct ConstStructValue { ConstExprValue *fields; - ConstParent parent; }; struct ConstUnionValue { BigInt tag; ConstExprValue *payload; - ConstParent parent; }; enum ConstArraySpecial { @@ -138,7 +149,6 @@ struct ConstArrayValue { union { struct { ConstExprValue *elements; - ConstParent parent; } s_none; Buf *s_buf; } data; @@ -153,19 +163,29 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, + // The pointer points to the error set field of an error union + ConstPtrSpecialBaseErrorUnionCode, + // The pointer points to the payload field of an error union + ConstPtrSpecialBaseErrorUnionPayload, + // The pointer points to the payload field of an optional + ConstPtrSpecialBaseOptionalPayload, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. - // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all optionals of pointer types use x_ptr - // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. ConstPtrSpecialDiscard, // This is actually a function. ConstPtrSpecialFunction, + // This means the pointer is null. This is only allowed when the type is ?*T. + // We use this instead of ConstPtrSpecialHardCodedAddr because often we check + // for that value to avoid doing comptime work. + // We need the data layout for ConstCastOnly == true + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional. + ConstPtrSpecialNull, }; enum ConstPtrMut { @@ -199,6 +219,15 @@ struct ConstPtrValue { ConstExprValue *struct_val; size_t field_index; } base_struct; + struct { + ConstExprValue *err_union_val; + } base_err_union_code; + struct { + ConstExprValue *err_union_val; + } base_err_union_payload; + struct { + ConstExprValue *optional_val; + } base_optional_payload; struct { uint64_t addr; } hard_coded_addr; @@ -209,7 +238,7 @@ struct ConstPtrValue { }; struct ConstErrValue { - ErrorTableEntry *err; + ConstExprValue *error_set; ConstExprValue *payload; }; @@ -265,6 +294,7 @@ struct ConstGlobalRefs { struct ConstExprValue { ZigType *type; ConstValSpecial special; + ConstParent parent; ConstGlobalRefs *global_refs; union { @@ -433,7 +463,7 @@ enum NodeType { NodeTypeArrayType, NodeTypeErrorType, NodeTypeIfErrorExpr, - NodeTypeTestExpr, + NodeTypeIfOptional, NodeTypeErrorSetDecl, NodeTypeCancel, NodeTypeResume, @@ -514,12 +544,7 @@ struct AstNodeDefer { }; struct AstNodeVariableDeclaration { - VisibMod visib_mod; Buf *symbol; - bool is_const; - bool is_comptime; - bool is_export; - bool is_extern; // one or both of type and expr will be non null AstNode *type; AstNode *expr; @@ -529,6 +554,13 @@ struct AstNodeVariableDeclaration { AstNode *align_expr; // populated if the "section(S)" is present AstNode *section_expr; + Token *threadlocal_tok; + + VisibMod visib_mod; + bool is_const; + bool is_comptime; + bool is_export; + bool is_extern; }; struct AstNodeTestDecl { @@ -605,7 +637,6 @@ enum CastOp { CastOpFloatToInt, CastOpBoolToInt, CastOpResizeSlice, - CastOpBytesToSlice, CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, @@ -660,15 +691,17 @@ struct AstNodePointerType { AstNode *align_expr; BigInt *bit_offset_start; BigInt *host_int_bytes; + AstNode *op_expr; + Token *allow_zero_token; bool is_const; bool is_volatile; - AstNode *op_expr; }; struct AstNodeArrayType { AstNode *size; AstNode *child_type; AstNode *align_expr; + Token *allow_zero_token; bool is_const; bool is_volatile; }; @@ -678,7 +711,7 @@ struct AstNodeUse { AstNode *expr; TldResolution resolution; - IrInstruction *value; + ConstExprValue *value; }; struct AstNodeIfBoolExpr { @@ -1007,6 +1040,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); enum PtrLen { PtrLenUnknown, PtrLenSingle, + PtrLenC, }; struct ZigTypePointer { @@ -1018,6 +1052,7 @@ struct ZigTypePointer { uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned bool is_const; bool is_volatile; + bool allow_zero; }; struct ZigTypeInt { @@ -1167,6 +1202,7 @@ struct ZigTypeFn { FnGenParamInfo *gen_param_info; LLVMTypeRef raw_type_ref; + ZigLLVMDIType *raw_di_type; ZigType *bound_fn_parent; }; @@ -1180,6 +1216,12 @@ struct ZigTypePromise { ZigType *result_type; }; +struct ZigTypeVector { + // The type must be a pointer, integer, or float + ZigType *elem_type; + uint32_t len; +}; + enum ZigTypeId { ZigTypeIdInvalid, ZigTypeIdMetaType, @@ -1206,6 +1248,7 @@ enum ZigTypeId { ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdPromise, + ZigTypeIdVector, }; struct ZigType { @@ -1232,6 +1275,7 @@ struct ZigType { ZigTypeFn fn; ZigTypeBoundFn bound_fn; ZigTypePromise promise; + ZigTypeVector vector; } data; // use these fields to make sure we don't duplicate type table entries for the same type @@ -1385,6 +1429,7 @@ enum BuiltinFnId { BuiltinFnIdEnumToInt, BuiltinFnIdIntToEnum, BuiltinFnIdIntType, + BuiltinFnIdVectorType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, BuiltinFnIdSetFloatMode, @@ -1415,6 +1460,8 @@ enum BuiltinFnId { BuiltinFnIdErrorReturnTrace, BuiltinFnIdAtomicRmw, BuiltinFnIdAtomicLoad, + BuiltinFnIdBswap, + BuiltinFnIdBitReverse, }; struct BuiltinFnEntry { @@ -1441,6 +1488,7 @@ enum PanicMsgId { PanicMsgIdBadUnionField, PanicMsgIdBadEnumValue, PanicMsgIdFloatToInt, + PanicMsgIdPtrCastNull, PanicMsgIdCount, }; @@ -1455,11 +1503,12 @@ struct TypeId { struct { ZigType *child_type; PtrLen ptr_len; - bool is_const; - bool is_volatile; uint32_t alignment; uint32_t bit_offset_in_host; uint32_t host_int_bytes; + bool is_const; + bool is_volatile; + bool allow_zero; } pointer; struct { ZigType *child_type; @@ -1473,6 +1522,10 @@ struct TypeId { ZigType *err_set_type; ZigType *payload_type; } error_union; + struct { + ZigType *elem_type; + uint32_t len; + } vector; } data; }; @@ -1487,8 +1540,12 @@ enum ZigLLVMFnId { ZigLLVMFnIdFloor, ZigLLVMFnIdCeil, ZigLLVMFnIdSqrt, + ZigLLVMFnIdBswap, + ZigLLVMFnIdBitReverse, }; +// There are a bunch of places in code that rely on these values being in +// exactly this order. enum AddSubMul { AddSubMulAdd = 0, AddSubMulSub = 1, @@ -1514,8 +1571,15 @@ struct ZigLLVMFnKey { struct { AddSubMul add_sub_mul; uint32_t bit_count; + uint32_t vector_len; // 0 means not a vector bool is_signed; } overflow_arithmetic; + struct { + uint32_t bit_count; + } bswap; + struct { + uint32_t bit_count; + } bit_reverse; } data; }; @@ -1600,7 +1664,7 @@ 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; @@ -1694,6 +1758,7 @@ struct CodeGen { ZigFn *cur_fn; ZigFn *main_fn; ZigFn *panic_fn; + TldFn *panic_tld_fn; AstNode *root_export_decl; CacheHash cache_hash; @@ -1746,12 +1811,12 @@ struct CodeGen { BuildMode build_mode; OutType out_type; ZigTarget zig_target; + TargetSubsystem subsystem; bool is_static; bool strip_debug_symbols; bool is_test_build; + bool is_single_threaded; bool is_native_target; - bool windows_subsystem_windows; - bool windows_subsystem_console; bool linker_rdynamic; bool no_rosegment_workaround; bool each_lib_rpath; @@ -1793,10 +1858,9 @@ enum VarLinkage { struct ZigVar { Buf name; - ConstExprValue *value; + ConstExprValue *const_value; + ZigType *var_type; LLVMValueRef value_ref; - bool src_is_const; - bool gen_is_const; IrInstruction *is_comptime; // which node is the declaration of the variable AstNode *decl_node; @@ -1806,17 +1870,22 @@ struct ZigVar { Scope *parent_scope; Scope *child_scope; LLVMValueRef param_value_ref; - bool shadowable; size_t mem_slot_index; IrExecutable *owner_exec; size_t ref_count; - VarLinkage linkage; - uint32_t align_bytes; // 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. ZigVar *next_var; + + uint32_t align_bytes; + VarLinkage linkage; + + bool shadowable; + bool src_is_const; + bool gen_is_const; + bool is_thread_local; }; struct ErrorTableEntry { @@ -1882,10 +1951,11 @@ struct ScopeBlock { ZigList *incoming_values; ZigList *incoming_blocks; - bool safety_off; AstNode *safety_set_node; - bool fast_math_on; AstNode *fast_math_set_node; + + bool safety_off; + bool fast_math_on; }; // This scope is created from every defer expression. @@ -2021,8 +2091,19 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +// These instructions are in transition to having "pass 1" instructions +// and "pass 2" instructions. The pass 1 instructions are suffixed with Src +// and pass 2 are suffixed with Gen. +// Once all instructions are separated in this way, they'll have different +// base types for better type safety. +// Src instructions are generated by ir_gen_* functions in ir.cpp from AST. +// ir_analyze_* functions consume Src instructions and produce Gen instructions. +// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. +// Src instructions do not have type information; Gen instructions do. enum IrInstructionId { IrInstructionIdInvalid, + IrInstructionIdDeclVarSrc, + IrInstructionIdDeclVarGen, IrInstructionIdBr, IrInstructionIdCondBr, IrInstructionIdSwitchBr, @@ -2031,7 +2112,6 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, - IrInstructionIdDeclVar, IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, @@ -2060,7 +2140,7 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalUnwrapPtr, IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, @@ -2076,7 +2156,8 @@ enum IrInstructionId { IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, - IrInstructionIdCmpxchg, + IrInstructionIdCmpxchgSrc, + IrInstructionIdCmpxchgGen, IrInstructionIdFence, IrInstructionIdTruncate, IrInstructionIdIntCast, @@ -2085,6 +2166,7 @@ enum IrInstructionId { IrInstructionIdFloatToInt, IrInstructionIdBoolToInt, IrInstructionIdIntType, + IrInstructionIdVectorType, IrInstructionIdBoolNot, IrInstructionIdMemset, IrInstructionIdMemcpy, @@ -2105,7 +2187,8 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdPtrCast, + IrInstructionIdPtrCastSrc, + IrInstructionIdPtrCastGen, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, @@ -2158,10 +2241,15 @@ enum IrInstructionId { IrInstructionIdMergeErrRetTraces, IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, + IrInstructionIdBswap, + IrInstructionIdBitReverse, IrInstructionIdErrSetCast, IrInstructionIdToBytes, IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, + IrInstructionIdVectorToArray, + IrInstructionIdArrayToVector, + IrInstructionIdAssertZero, }; struct IrInstruction { @@ -2183,6 +2271,22 @@ struct IrInstruction { bool is_gen; }; +struct IrInstructionDeclVarSrc { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_type; + IrInstruction *align_value; + IrInstruction *init_value; +}; + +struct IrInstructionDeclVarGen { + IrInstruction base; + + ZigVar *var; + IrInstruction *init_value; +}; + struct IrInstructionCondBr { IrInstruction base; @@ -2291,20 +2395,11 @@ struct IrInstructionBinOp { IrInstruction base; IrInstruction *op1; - IrBinOp op_id; IrInstruction *op2; + IrBinOp op_id; bool safety_check_on; }; -struct IrInstructionDeclVar { - IrInstruction base; - - ZigVar *var; - IrInstruction *var_type; - IrInstruction *align_value; - IrInstruction *init_value; -}; - struct IrInstructionLoadPtr { IrInstruction base; @@ -2324,7 +2419,6 @@ struct IrInstructionFieldPtr { IrInstruction *container_ptr; Buf *field_name_buffer; IrInstruction *field_name_expr; - bool is_const; }; struct IrInstructionStructFieldPtr { @@ -2367,13 +2461,13 @@ struct IrInstructionCall { ZigFn *fn_entry; size_t arg_count; IrInstruction **args; - bool is_comptime; LLVMValueRef tmp_ptr; - FnInline fn_inline; - bool is_async; IrInstruction *async_allocator; IrInstruction *new_stack; + FnInline fn_inline; + bool is_async; + bool is_comptime; }; struct IrInstructionConst { @@ -2504,6 +2598,7 @@ struct IrInstructionPtrType { PtrLen ptr_len; bool is_const; bool is_volatile; + bool allow_zero; }; struct IrInstructionPromiseType { @@ -2516,9 +2611,10 @@ struct IrInstructionSliceType { IrInstruction base; IrInstruction *align_value; + IrInstruction *child_type; bool is_const; bool is_volatile; - IrInstruction *child_type; + bool allow_zero; }; struct IrInstructionAsm { @@ -2546,10 +2642,12 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapOptional { +// Takes a pointer to an optional value, returns a pointer +// to the payload. +struct IrInstructionOptionalUnwrapPtr { IrInstruction base; - IrInstruction *value; + IrInstruction *base_ptr; bool safety_check_on; }; @@ -2640,7 +2738,7 @@ struct IrInstructionEmbedFile { IrInstruction *name; }; -struct IrInstructionCmpxchg { +struct IrInstructionCmpxchgSrc { IrInstruction base; IrInstruction *type_value; @@ -2650,14 +2748,19 @@ struct IrInstructionCmpxchg { IrInstruction *success_order_value; IrInstruction *failure_order_value; - // if this instruction gets to runtime then we know these values: - ZigType *type; + bool is_weak; +}; + +struct IrInstructionCmpxchgGen { + IrInstruction base; + + IrInstruction *ptr; + IrInstruction *cmp_value; + IrInstruction *new_value; + LLVMValueRef tmp_ptr; AtomicOrder success_order; AtomicOrder failure_order; - bool is_weak; - - LLVMValueRef tmp_ptr; }; struct IrInstructionFence { @@ -2737,6 +2840,13 @@ struct IrInstructionIntType { IrInstruction *bit_count; }; +struct IrInstructionVectorType { + IrInstruction base; + + IrInstruction *len; + IrInstruction *elem_type; +}; + struct IrInstructionBoolNot { IrInstruction base; @@ -2840,7 +2950,7 @@ struct IrInstructionTestErr { struct IrInstructionUnwrapErrCode { IrInstruction base; - IrInstruction *value; + IrInstruction *err_union; }; struct IrInstructionUnwrapErrPayload { @@ -2888,11 +2998,19 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionPtrCast { +struct IrInstructionPtrCastSrc { IrInstruction base; IrInstruction *dest_type; IrInstruction *ptr; + bool safety_check_on; +}; + +struct IrInstructionPtrCastGen { + IrInstruction base; + + IrInstruction *ptr; + bool safety_check_on; }; struct IrInstructionBitCast { @@ -3251,6 +3369,39 @@ struct IrInstructionCheckRuntimeScope { IrInstruction *is_comptime; }; +struct IrInstructionBswap { + IrInstruction base; + + IrInstruction *type; + IrInstruction *op; +}; + +struct IrInstructionBitReverse { + IrInstruction base; + + IrInstruction *type; + IrInstruction *op; +}; + +struct IrInstructionArrayToVector { + IrInstruction base; + + IrInstruction *array; +}; + +struct IrInstructionVectorToArray { + IrInstruction base; + + IrInstruction *vector; + LLVMValueRef tmp_ptr; +}; + +struct IrInstructionAssertZero { + IrInstruction base; + + IrInstruction *target; +}; + 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 2f4b173c5f..9941104bc4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); -ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { - if (node->owner->c_import_node != nullptr) { - // if this happens, then translate_c generated code that - // failed semantic analysis, which isn't supposed to happen - ErrorMsg *err = add_node_error(g, node->owner->c_import_node, - buf_sprintf("compiler bug: @cImport generated invalid zig code")); - - add_error_note(g, err, node, msg); - - g->errors.append(err); - return err; - } - - ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, - node->owner->source_code, node->owner->line_offsets, msg); - - g->errors.append(err); - return err; -} - -ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { - if (node->owner->c_import_node != nullptr) { +static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token, + Buf *msg) +{ + if (owner->c_import_node != nullptr) { // if this happens, then translate_c generated code that // failed semantic analysis, which isn't supposed to happen @@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m return note; } - ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, - node->owner->source_code, node->owner->line_offsets, msg); + ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, + owner->source_code, owner->line_offsets, msg); err_msg_add_note(parent_msg, err); return err; } +ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) { + if (owner->c_import_node != nullptr) { + // if this happens, then translate_c generated code that + // failed semantic analysis, which isn't supposed to happen + ErrorMsg *err = add_node_error(g, owner->c_import_node, + buf_sprintf("compiler bug: @cImport generated invalid zig code")); + + add_error_note_token(g, err, owner, token, msg); + + g->errors.append(err); + return err; + } + ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, + owner->source_code, owner->line_offsets, msg); + + g->errors.append(err); + return err; +} + +ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + return add_token_error(g, node->owner, &fake_token, msg); +} + +ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg); +} + ZigType *new_type_table_entry(ZigTypeId id) { ZigType *entry = allocate(1); entry->id = id; @@ -250,6 +265,7 @@ AstNode *type_decl_node(ZigType *type_entry) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: + case ZigTypeIdVector: return nullptr; } zig_unreachable(); @@ -311,6 +327,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: + case ZigTypeIdVector: return true; } zig_unreachable(); @@ -339,6 +356,28 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) { } } + return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); +} + +uint64_t type_size_store(CodeGen *g, ZigType *type_entry) { + assert(type_is_complete(type_entry)); + + if (!type_has_bits(type_entry)) + return 0; + + if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { + uint64_t size_in_bits = type_size_bits(g, type_entry); + return (size_in_bits + 7) / 8; + } else if (type_entry->id == ZigTypeIdArray) { + ZigType *child_type = type_entry->data.array.child_type; + if (child_type->id == ZigTypeIdStruct && + child_type->data.structure.layout == ContainerLayoutPacked) + { + uint64_t size_in_bits = type_size_bits(g, type_entry); + return (size_in_bits + 7) / 8; + } + } + return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); } @@ -400,9 +439,25 @@ ZigType *get_promise_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_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 is_volatile, PtrLen ptr_len, uint32_t byte_alignment, + uint32_t bit_offset_in_host, uint32_t host_int_bytes) { + // TODO when implementing https://github.com/ziglang/zig/issues/1953 + // move this to a parameter + bool allow_zero = (ptr_len == PtrLenC); assert(!type_is_invalid(child_type)); assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque); @@ -422,7 +477,7 @@ 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) { + if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) { type_id.id = ZigTypeIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; @@ -431,6 +486,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons type_id.data.pointer.bit_offset_in_host = bit_offset_in_host; 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; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) @@ -448,21 +504,31 @@ 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 == PtrLenSingle ? "*" : "[*]"; + 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", star_str, const_str, volatile_str, buf_ptr(&child_type->name)); + 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", star_str, byte_alignment, - const_str, volatile_str, buf_ptr(&child_type->name)); + 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", star_str, - bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name)); + 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", star_str, byte_alignment, - bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name)); + 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)); } assert(child_type->id != ZigTypeIdInvalid); @@ -470,7 +536,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons entry->zero_bits = !type_has_bits(child_type); if (!entry->zero_bits) { - if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || bit_offset_in_host != 0) { + if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || + bit_offset_in_host != 0 || allow_zero) + { ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false, PtrLenSingle, 0, 0, host_int_bytes); entry->type_ref = peer_type->type_ref; @@ -504,6 +572,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons entry->data.pointer.explicit_alignment = byte_alignment; 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; if (parent_pointer) { *parent_pointer = entry; @@ -569,13 +638,16 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; - } else if (type_is_codegen_pointer(child_type)) { + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers // function types are technically pointers entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; + if (entry->di_type == g->builtin_types.entry_global_error_set->di_type) { + g->error_di_types.append(&entry->di_type); + } } else { assert(child_type->di_type); // create a struct with a boolean whether this is the null value @@ -817,7 +889,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { 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.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); @@ -840,7 +912,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { 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.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero) { 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, @@ -1054,11 +1126,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { } if (g->zig_target.arch.arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); - if (abi_class == X64CABIClass_MEMORY) { - return true; - } - zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481", - buf_ptr(&fn_type_id->return_type->name)); + return abi_class == X64CABIClass_MEMORY; } else if (target_is_arm(&g->zig_target)) { return type_size(g, fn_type_id->return_type) > 16; } @@ -1219,7 +1287,10 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); - fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); + fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); + fn_type->di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type, + LLVMStoreSizeOfType(g->target_data_ref, fn_type->type_ref), + LLVMABIAlignmentOfType(g->target_data_ref, fn_type->type_ref), ""); } g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); @@ -1274,20 +1345,22 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind return entry; } -static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) { +static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, + Buf *type_name) +{ size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, - nullptr, nullptr, node, type_name, nullptr); + nullptr, nullptr, node, type_name, nullptr, nullptr); } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { - IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); + if (type_is_invalid(result->type)) return g->builtin_types.entry_invalid; - assert(result->value.special != ConstValSpecialRuntime); - return result->value.data.x_type; + assert(result->special != ConstValSpecialRuntime); + return result->data.x_type; } ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { @@ -1338,11 +1411,11 @@ 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) { - IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); - if (type_is_invalid(align_result->value.type)) + ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); + if (type_is_invalid(align_result->type)) return false; - uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); + uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint); if (align_bytes == 0) { add_node_error(g, node, buf_sprintf("alignment must be >= 1")); return false; @@ -1360,12 +1433,12 @@ 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); ZigType *str_type = get_slice_type(g, ptr_type); - IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); - if (type_is_invalid(instr->value.type)) + ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr); + if (type_is_invalid(result_val->type)) return false; - ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index]; + ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *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; @@ -1418,6 +1491,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdFn: + case ZigTypeIdVector: return true; case ZigTypeIdStruct: return type_entry->data.structure.layout == ContainerLayoutPacked; @@ -1434,7 +1508,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { zig_unreachable(); } -static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { +bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -1466,6 +1540,8 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { default: return false; } + case ZigTypeIdVector: + return type_allowed_in_extern(g, type_entry->data.vector.elem_type); case ZigTypeIdFloat: return true; case ZigTypeIdArray: @@ -1619,6 +1695,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: switch (type_requires_comptime(g, type_entry)) { case ReqCompTimeNo: break; @@ -1714,6 +1791,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: switch (type_requires_comptime(g, fn_type_id.return_type)) { case ReqCompTimeInvalid: return g->builtin_types.entry_invalid; @@ -2500,20 +2578,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { enum_type->data.enumeration.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt || - result_inst->value.type->id == ZigTypeIdComptimeInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || + result->type->id == ZigTypeIdComptimeInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -2623,6 +2701,13 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { buf_sprintf("enums, not structs, support field assignment")); } + if (field_type->id == ZigTypeIdOpaque) { + add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + continue; + } + switch (type_requires_comptime(g, field_type)) { case ReqCompTimeYes: struct_type->data.structure.requires_comptime = true; @@ -2680,39 +2765,50 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { assert(decl_node->type == NodeTypeContainerDecl); assert(struct_type->di_type); + size_t field_count = struct_type->data.structure.src_field_count; if (struct_type->data.structure.layout == ContainerLayoutPacked) { struct_type->data.structure.abi_alignment = 1; - } - - size_t field_count = struct_type->data.structure.src_field_count; - for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; - - // If this assertion trips, look up the call stack. Probably something is - // calling type_resolve with ResolveStatusAlignmentKnown when it should only - // be resolving ResolveStatusZeroBitsKnown - assert(field->type_entry != nullptr); - - if (type_is_invalid(field->type_entry)) { - struct_type->data.structure.resolve_status = ResolveStatusInvalid; - break; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + if (field->type_entry != nullptr && type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } } + } else for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + uint32_t this_field_align; - if (!type_has_bits(field->type_entry)) - continue; + // TODO If we have no type_entry for the field, we've already failed to + // compile the program correctly. This stage1 compiler needs a deeper + // reworking to make this correct, or we can ignore the problem + // and make sure it is fixed in stage2. This workaround is for when + // there is a false positive of a dependency loop, of alignment depending + // on itself. When this false positive happens we assume a pointer-aligned + // field, which is usually fine but could be incorrectly over-aligned or + // even under-aligned. See https://github.com/ziglang/zig/issues/1512 + if (field->type_entry == nullptr) { + this_field_align = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); + } else { + if (type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } + + if (!type_has_bits(field->type_entry)) + continue; - // alignment of structs is the alignment of the most-aligned field - if (struct_type->data.structure.layout != ContainerLayoutPacked) { if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } - uint32_t this_field_align = get_abi_alignment(g, field->type_entry); + this_field_align = get_abi_alignment(g, field->type_entry); assert(this_field_align != 0); - if (this_field_align > struct_type->data.structure.abi_alignment) { - struct_type->data.structure.abi_alignment = this_field_align; - } + } + // alignment of structs is the alignment of the most-aligned field + if (this_field_align > struct_type->data.structure.abi_alignment) { + struct_type->data.structure.abi_alignment = this_field_align; } } @@ -2790,7 +2886,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum || enum_type_node != nullptr; bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); + bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease); ZigType *tag_type; bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); bool *covered_enum_fields; @@ -2896,6 +2992,13 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { } union_field->type_entry = field_type; + if (field_type->id == ZigTypeIdOpaque) { + add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in unions")); + union_type->data.unionation.is_invalid = true; + continue; + } + switch (type_requires_comptime(g, field_type)) { case ReqCompTimeInvalid: union_type->data.unionation.is_invalid = true; @@ -2929,19 +3032,19 @@ 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; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { union_type->data.unionation.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -3144,36 +3247,19 @@ static bool scope_is_root_decls(Scope *scope) { zig_unreachable(); } -static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, ZigType *fn_type) { - add_node_error(g, proto_node, - buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'", - buf_ptr(&fn_type->name))); -} +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; -static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) { - AstNode *proto_node = panic_fn->proto_node; - assert(proto_node->type == NodeTypeFnProto); - ZigType *fn_type = panic_fn->type_entry; - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - if (fn_type_id->param_count != 2) { - return wrong_panic_prototype(g, proto_node, fn_type); - } - ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, 0, 0, 0); - ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr); - if (fn_type_id->param_info[0].type != const_u8_slice) { - return wrong_panic_prototype(g, proto_node, fn_type); - } + AstNode *fake_decl = allocate(1); + *fake_decl = *panic_fn->proto_node; + fake_decl->type = NodeTypeSymbol; + fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name; - ZigType *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g)); - if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { - return wrong_panic_prototype(g, proto_node, fn_type); - } - - ZigType *actual_return_type = fn_type_id->return_type; - if (actual_return_type != g->builtin_types.entry_unreachable) { - return wrong_panic_prototype(g, proto_node, fn_type); - } + // 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); } ZigType *get_test_fn_type(CodeGen *g) { @@ -3191,14 +3277,12 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLi if (ccc) { if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) { g->have_c_main = true; - g->windows_subsystem_windows = false; - g->windows_subsystem_console = true; + g->subsystem = TargetSubsystemConsole; } else if (buf_eql_str(symbol_name, "WinMain") && g->zig_target.os == OsWindows) { g->have_winmain = true; - g->windows_subsystem_windows = true; - g->windows_subsystem_console = false; + g->subsystem = TargetSubsystemWindows; } else if (buf_eql_str(symbol_name, "WinMainCRTStartup") && g->zig_target.os == OsWindows) { @@ -3209,6 +3293,7 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLi g->have_dllmain_crt_startup = true; } } + FnExport *fn_export = fn_table_entry->export_list.add_one(); memset(fn_export, 0, sizeof(FnExport)); buf_init_from_buf(&fn_export->name, symbol_name); @@ -3269,18 +3354,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (!fn_table_entry->type_entry->data.fn.is_generic) { if (fn_def_node) g->fn_defs.append(fn_table_entry); + } - if (scope_is_root_decls(tld_fn->base.parent_scope) && - (import == g->root_import || import->package == g->panic_package)) + if (scope_is_root_decls(tld_fn->base.parent_scope) && + (import == g->root_import || import->package == g->panic_package)) + { + if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { + g->main_fn = fn_table_entry; + } else if ((import->package == g->panic_package || g->have_pub_panic) && + buf_eql_str(&fn_table_entry->symbol_name, "panic")) { - if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { - g->main_fn = fn_table_entry; - } else if ((import->package == g->panic_package || g->have_pub_panic) && - buf_eql_str(&fn_table_entry->symbol_name, "panic")) - { - g->panic_fn = fn_table_entry; - typecheck_panic_fn(g, fn_table_entry); - } + g->panic_fn = fn_table_entry; + g->panic_tld_fn = tld_fn; } } } else if (source_node->type == NodeTypeTestDecl) { @@ -3405,7 +3490,8 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { resolve_top_level_decl(g, tld, false, tld->source_node); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - tld_var->var->value = value; + tld_var->var->const_value = value; + tld_var->var->var_type = value->type; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } @@ -3499,7 +3585,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeIfErrorExpr: - case NodeTypeTestExpr: + case NodeTypeIfOptional: case NodeTypeErrorSetDecl: case NodeTypeCancel: case NodeTypeResume: @@ -3560,6 +3646,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: return type_entry; } zig_unreachable(); @@ -3568,13 +3655,15 @@ 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 *value, Tld *src_tld) + bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type) { Error err; - assert(value); + assert(const_value != nullptr); + assert(var_type != nullptr); ZigVar *variable_entry = allocate(1); - variable_entry->value = value; + variable_entry->const_value = const_value; + variable_entry->var_type = var_type; variable_entry->parent_scope = parent_scope; variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; @@ -3583,23 +3672,23 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf assert(name); buf_init_from_buf(&variable_entry->name, name); - if ((err = type_resolve(g, value->type, ResolveStatusAlignmentKnown))) { - variable_entry->value->type = g->builtin_types.entry_invalid; + if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) { + variable_entry->var_type = g->builtin_types.entry_invalid; } else { - variable_entry->align_bytes = get_abi_alignment(g, value->type); + variable_entry->align_bytes = get_abi_alignment(g, var_type); ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(g, source_node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { Scope *search_scope = nullptr; if (src_tld == nullptr) { @@ -3613,7 +3702,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } } } @@ -3645,6 +3734,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { bool is_const = var_decl->is_const; bool is_extern = var_decl->is_extern; bool is_export = var_decl->is_export; + bool is_thread_local = var_decl->threadlocal_tok != nullptr; ZigType *explicit_type = nullptr; if (var_decl->type) { @@ -3663,7 +3753,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { linkage = VarLinkageInternal; } - IrInstruction *init_value = nullptr; + ConstExprValue *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; @@ -3672,7 +3762,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol); assert(init_value); - implicit_type = init_value->value.type; + implicit_type = init_value->type; if (implicit_type->id == ZigTypeIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); @@ -3690,7 +3780,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } - assert(implicit_type->id == ZigTypeIdInvalid || init_value->value.special != ConstValSpecialRuntime); + assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime); } else if (linkage != VarLinkageExternal) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; @@ -3699,19 +3789,20 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { ZigType *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); + ConstExprValue *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); + is_const, init_val, &tld_var->base, type); tld_var->var->linkage = linkage; + tld_var->var->is_thread_local = is_thread_local; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } if (var_decl->align_expr != nullptr) { if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } } @@ -3724,6 +3815,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } } + if (is_thread_local && is_const) { + add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant")); + } + g->global_vars.append(tld_var); } @@ -3924,6 +4019,7 @@ static bool is_container(ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: + case ZigTypeIdVector: return false; } zig_unreachable(); @@ -3983,6 +4079,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: + case ZigTypeIdVector: zig_unreachable(); } } @@ -3992,7 +4089,9 @@ ZigType *get_src_ptr_type(ZigType *type) { if (type->id == ZigTypeIdFn) return type; if (type->id == ZigTypeIdPromise) return type; if (type->id == ZigTypeIdOptional) { - if (type->data.maybe.child_type->id == ZigTypeIdPointer) return type->data.maybe.child_type; + if (type->data.maybe.child_type->id == ZigTypeIdPointer) { + return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type; + } if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == ZigTypeIdPromise) return type->data.maybe.child_type; } @@ -4006,6 +4105,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) { return ty; } +bool type_is_nonnull_ptr(ZigType *type) { + return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type); +} + bool type_is_codegen_pointer(ZigType *type) { return get_codegen_ptr_type(type) == type; } @@ -4076,7 +4179,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, - param_name, true, create_const_runtime(param_type), nullptr); + param_name, true, create_const_runtime(param_type), nullptr, param_type); var->src_arg_index = i; fn_table_entry->child_scope = var->child_scope; var->shadowable = var->shadowable || is_var_args; @@ -4214,18 +4317,17 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * preview_use_decl(g, src_use_node); } - IrInstruction *use_target_value = src_use_node->data.use.value; - if (use_target_value->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *use_target_value = src_use_node->data.use.value; + if (type_is_invalid(use_target_value->type)) { dst_use_node->owner->any_imports_failed = true; return; } dst_use_node->data.use.resolution = TldResolutionOk; - ConstExprValue *const_val = &use_target_value->value; - assert(const_val->special != ConstValSpecialRuntime); + assert(use_target_value->special != ConstValSpecialRuntime); - ImportTableEntry *target_import = const_val->data.x_import; + ImportTableEntry *target_import = use_target_value->data.x_import; assert(target_import); if (target_import->any_imports_failed) { @@ -4288,10 +4390,10 @@ void preview_use_decl(CodeGen *g, AstNode *node) { } node->data.use.resolution = TldResolutionResolving; - IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base, + ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + if (type_is_invalid(result->type)) node->owner->any_imports_failed = true; node->data.use.value = result; @@ -4364,8 +4466,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *r if (is_pub && ok_cc) { if (buf_eql_str(proto_name, "main")) { g->have_pub_main = true; - g->windows_subsystem_windows = false; - g->windows_subsystem_console = true; + g->subsystem = TargetSubsystemConsole; } else if (buf_eql_str(proto_name, "panic")) { g->have_pub_panic = true; } @@ -4434,6 +4535,43 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { return new_entry; } +bool is_valid_vector_elem_type(ZigType *elem_type) { + return elem_type->id == ZigTypeIdInt || + elem_type->id == ZigTypeIdFloat || + get_codegen_ptr_type(elem_type) != nullptr; +} + +ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) { + assert(is_valid_vector_elem_type(elem_type)); + + TypeId type_id = {}; + type_id.id = ZigTypeIdVector; + type_id.data.vector.len = len; + type_id.data.vector.elem_type = elem_type; + + { + auto entry = g->type_table.maybe_get(type_id); + if (entry) + return entry->value; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdVector); + entry->zero_bits = (len == 0) || !type_has_bits(elem_type); + entry->type_ref = entry->zero_bits ? LLVMVoidType() : LLVMVectorType(elem_type->type_ref, len); + entry->data.vector.len = len; + entry->data.vector.elem_type = elem_type; + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name)); + + entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, + len * type_size_bits(g, elem_type), + LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type, len); + + g->type_table.put(type_id, entry); + return entry; +} + ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { return &g->builtin_types.entry_c_int[c_int_type]; } @@ -4465,6 +4603,7 @@ bool handle_is_ptr(ZigType *type_entry) { case ZigTypeIdFn: case ZigTypeIdEnum: case ZigTypeIdPromise: + case ZigTypeIdVector: return false; case ZigTypeIdArray: case ZigTypeIdStruct: @@ -4473,7 +4612,8 @@ bool handle_is_ptr(ZigType *type_entry) { return type_has_bits(type_entry->data.error_union.payload_type); case ZigTypeIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && - !type_is_codegen_pointer(type_entry->data.maybe.child_type); + !type_is_codegen_pointer(type_entry->data.maybe.child_type) && + type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; case ZigTypeIdUnion: assert(type_entry->data.unionation.zero_bits_known); if (type_entry->data.unionation.gen_field_count == 0) @@ -4580,8 +4720,7 @@ static Buf *get_posix_libc_include_path(void) { void find_libc_include_path(CodeGen *g) { if (g->libc_include_dir == nullptr) { if (!g->is_native_target) { - fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); - exit(1); + return; } if (g->zig_target.os == OsWindows) { @@ -4720,6 +4859,11 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_error_set(ConstExprValue *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) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { @@ -4751,6 +4895,18 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); return hash_val; + case ConstPtrSpecialBaseErrorUnionCode: + hash_val += (uint32_t)2994743799; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); + return hash_val; + case ConstPtrSpecialBaseErrorUnionPayload: + hash_val += (uint32_t)3456080131; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); + return hash_val; + case ConstPtrSpecialBaseOptionalPayload: + hash_val += (uint32_t)3163140517; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -4762,6 +4918,9 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)2590901619; hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return hash_val; + case ConstPtrSpecialNull: + hash_val += (uint32_t)1486246455; + return hash_val; } zig_unreachable(); } @@ -4860,7 +5019,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 2709806591; case ZigTypeIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { - return hash_const_val(const_val) * 1992916303; + return hash_const_val_ptr(const_val) * 1992916303; + } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return hash_const_val_error_set(const_val) * 3147031929; } else { if (const_val->data.x_optional) { return hash_const_val(const_val->data.x_optional) * 1992916303; @@ -4872,10 +5033,12 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 3415065496; case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return const_val->data.x_err_set->value ^ 2630160122; + return hash_const_val_error_set(const_val); case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); + case ZigTypeIdVector: + // TODO better hashing algorithm + return 3647867726; case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -4928,6 +5091,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case ZigTypeIdBool: case ZigTypeIdUnreachable: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -4975,7 +5139,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return can_mutate_comptime_var_state(value->data.x_optional); case ZigTypeIdErrorUnion: - if (value->data.x_err_union.err != nullptr) + if (value->data.x_err_union.error_set->data.x_err_set != nullptr) return false; assert(value->data.x_err_union.payload != nullptr); return can_mutate_comptime_var_state(value->data.x_err_union.payload); @@ -5011,6 +5175,7 @@ static bool return_type_is_cacheable(ZigType *return_type) { case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdPointer: + case ZigTypeIdVector: return true; case ZigTypeIdArray: @@ -5036,9 +5201,9 @@ bool fn_eval_cacheable(Scope *scope, ZigType *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - if (type_is_invalid(var_scope->var->value->type)) + if (type_is_invalid(var_scope->var->var_type)) return false; - if (can_mutate_comptime_var_state(var_scope->var->value)) + if (can_mutate_comptime_var_state(var_scope->var->const_value)) return false; } else if (scope->id == ScopeIdFnDef) { return true; @@ -5056,7 +5221,7 @@ uint32_t fn_eval_hash(Scope* scope) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - result += hash_const_val(var_scope->var->value); + result += hash_const_val(var_scope->var->const_value); } else if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; result += hash_ptr(fn_scope->fn_entry); @@ -5080,10 +5245,16 @@ bool fn_eval_eql(Scope *a, Scope *b) { if (a->id == ScopeIdVarDecl) { ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a; ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b; - if (a_var_scope->var->value->type != b_var_scope->var->value->type) - return false; - if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value)) + if (a_var_scope->var->var_type != b_var_scope->var->var_type) return false; + if (a_var_scope->var->var_type == a_var_scope->var->const_value->type && + b_var_scope->var->var_type == b_var_scope->var->const_value->type) + { + if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value)) + return false; + } else { + zig_panic("TODO comptime ptr reinterpret for fn_eval_eql"); + } } else if (a->id == ScopeIdFnDef) { ScopeFnDef *a_fn_scope = (ScopeFnDef *)a; ScopeFnDef *b_fn_scope = (ScopeFnDef *)b; @@ -5101,6 +5272,7 @@ bool fn_eval_eql(Scope *a, Scope *b) { return false; } +// Whether the type has bits at runtime. bool type_has_bits(ZigType *type_entry) { assert(type_entry); assert(!type_is_invalid(type_entry)); @@ -5108,6 +5280,66 @@ bool type_has_bits(ZigType *type_entry) { return !type_entry->zero_bits; } +// 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); + Error err; + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return OnePossibleValueInvalid; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdOpaque: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdMetaType: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdOptional: + case ZigTypeIdFn: + case ZigTypeIdBool: + case ZigTypeIdFloat: + case ZigTypeIdPromise: + case ZigTypeIdErrorUnion: + return OnePossibleValueNo; + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + return OnePossibleValueYes; + case ZigTypeIdArray: + if (type_entry->data.array.len == 0) + return OnePossibleValueYes; + 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]; + switch (type_has_one_possible_value(g, field->type_entry)) { + case OnePossibleValueInvalid: + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + continue; + } + } + return OnePossibleValueYes; + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdVector: + return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + case ZigTypeIdPointer: + return type_has_one_possible_value(g, type_entry->data.pointer.child_type); + case ZigTypeIdUnion: + if (type_entry->data.unionation.src_field_count > 1) + return OnePossibleValueNo; + return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry); + } + zig_unreachable(); +} + ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) @@ -5147,6 +5379,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdBool: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdVoid: case ZigTypeIdUnreachable: @@ -5562,6 +5795,33 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) return false; 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) + { + 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) + { + 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) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5570,10 +5830,34 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return true; case ConstPtrSpecialFunction: return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + case ConstPtrSpecialNull: + return true; } zig_unreachable(); } +static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *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 && + b->data.x_array.special == ConstArraySpecialBuf) + { + return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf); + } + 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; + + for (size_t i = 0; i < len; i += 1) { + if (!const_values_equal(g, &a_elems[i], &b_elems[i])) + return false; + } + + return true; +} + bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); @@ -5628,28 +5912,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { case ZigTypeIdPointer: case ZigTypeIdFn: return const_values_equal_ptr(a, b); + case ZigTypeIdVector: + assert(a->type->data.vector.len == b->type->data.vector.len); + return const_values_equal_array(g, a, b, a->type->data.vector.len); case ZigTypeIdArray: { assert(a->type->data.array.len == b->type->data.array.len); - assert(a->data.x_array.special != ConstArraySpecialUndef); - assert(b->data.x_array.special != ConstArraySpecialUndef); - if (a->data.x_array.special == ConstArraySpecialBuf && - b->data.x_array.special == ConstArraySpecialBuf) - { - return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf); - } - expand_undef_array(g, a); - expand_undef_array(g, b); - - size_t len = a->type->data.array.len; - ConstExprValue *a_elems = a->data.x_array.data.s_none.elements; - ConstExprValue *b_elems = b->data.x_array.data.s_none.elements; - - for (size_t i = 0; i < len; ++i) { - if (!const_values_equal(g, &a_elems[i], &b_elems[i])) - return false; - } - - return true; + return const_values_equal_array(g, a, b, a->type->data.array.len); } case ZigTypeIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { @@ -5738,14 +6006,25 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v } } -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, ConstExprValue *const_val, ZigType *type_entry) { + assert(type_entry->id == ZigTypeIdPointer); + + if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) { + buf_append_buf(buf, &type_entry->name); + return; + } + switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: buf_appendf(buf, "*"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); + // 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 ConstPtrSpecialBaseArray: if (const_val->data.x_ptr.data.base_array.is_cstr) { @@ -5753,7 +6032,8 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy return; } else { buf_appendf(buf, "*"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); + // 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: @@ -5769,6 +6049,56 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); return; } + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; + } + zig_unreachable(); +} + +static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { + if (const_val->data.x_err_set == nullptr) { + buf_append_str(buf, "null"); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); + } +} + +static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstExprValue *const_val, uint64_t start, uint64_t len) { + ConstArrayValue *array = &const_val->data.x_array; + switch (array->special) { + case ConstArraySpecialUndef: + buf_append_str(buf, "undefined"); + return; + case ConstArraySpecialBuf: { + Buf *array_buf = array->data.s_buf; + const char *base = &buf_ptr(array_buf)[start]; + assert(start + len <= buf_len(array_buf)); + + buf_append_char(buf, '"'); + for (size_t i = 0; i < len; i += 1) { + uint8_t c = base[i]; + if (c == '"') { + buf_append_str(buf, "\\\""); + } else { + buf_append_char(buf, c); + } + } + buf_append_char(buf, '"'); + return; + } + case ConstArraySpecialNone: { + ConstExprValue *base = &array->data.s_none.elements[start]; + assert(start + len <= const_val->type->data.array.len); + + buf_appendf(buf, "%s{", buf_ptr(type_name)); + for (uint64_t i = 0; i < len; i += 1) { + if (i != 0) buf_appendf(buf, ","); + render_const_value(g, buf, &base[i]); + } + buf_appendf(buf, "}"); + return; + } } zig_unreachable(); } @@ -5853,39 +6183,16 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdPointer: return render_const_val_ptr(g, buf, const_val, type_entry); - case ZigTypeIdArray: - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - buf_append_str(buf, "undefined"); - return; - case ConstArraySpecialBuf: { - Buf *array_buf = const_val->data.x_array.data.s_buf; - buf_append_char(buf, '"'); - for (size_t i = 0; i < buf_len(array_buf); i += 1) { - uint8_t c = buf_ptr(array_buf)[i]; - if (c == '"') { - buf_append_str(buf, "\\\""); - } else { - buf_append_char(buf, c); - } - } - buf_append_char(buf, '"'); - return; - } - case ConstArraySpecialNone: { - buf_appendf(buf, "%s{", buf_ptr(&type_entry->name)); - uint64_t len = type_entry->data.array.len; - for (uint64_t i = 0; i < len; i += 1) { - if (i != 0) - buf_appendf(buf, ","); - ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i]; - render_const_value(g, buf, child_value); - } - buf_appendf(buf, "}"); - return; - } - } - zig_unreachable(); + case ZigTypeIdArray: { + uint64_t len = type_entry->data.array.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } case ZigTypeIdNull: { buf_appendf(buf, "null"); @@ -5900,6 +6207,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet) + return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type); if (const_val->data.x_optional) { render_const_value(g, buf, const_val->data.x_optional); } else { @@ -5925,7 +6234,24 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdStruct: { - buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); + if (is_slice(type_entry)) { + ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index]; + size_t len = bigint_as_unsigned(&len_val->data.x_bigint); + + ConstExprValue *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; + 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); + } else { + buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); + } return; } case ZigTypeIdEnum: @@ -5937,11 +6263,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdErrorUnion: { buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); - if (const_val->data.x_err_union.err == nullptr) { + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set == nullptr) { render_const_value(g, buf, const_val->data.x_err_union.payload); } else { buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), - buf_ptr(&const_val->data.x_err_union.err->name)); + buf_ptr(&err_set->name)); } buf_appendf(buf, ")"); return; @@ -5956,10 +6283,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case ZigTypeIdErrorSet: - { - buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); - return; - } + return render_const_val_err_set(g, buf, const_val, type_entry); case ZigTypeIdArgTuple: { buf_appendf(buf, "(args value)"); @@ -6035,6 +6359,7 @@ uint32_t type_id_hash(TypeId x) { ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) + (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); @@ -6044,6 +6369,8 @@ uint32_t type_id_hash(TypeId x) { case ZigTypeIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); + case ZigTypeIdVector: + return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681); } zig_unreachable(); } @@ -6083,6 +6410,7 @@ bool type_id_eql(TypeId a, TypeId b) { a.data.pointer.ptr_len == b.data.pointer.ptr_len && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && + 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; @@ -6092,6 +6420,9 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; + case ZigTypeIdVector: + return a.data.vector.elem_type == b.data.vector.elem_type && + a.data.vector.len == b.data.vector.len; } zig_unreachable(); } @@ -6110,10 +6441,15 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; case ZigLLVMFnIdSqrt: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; + case ZigLLVMFnIdBswap: + return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335; + case ZigLLVMFnIdBitReverse: + return (uint32_t)(x.data.bit_reverse.bit_count) * (uint32_t)2621398431; case ZigLLVMFnIdOverflowArithmetic: return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) + - ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820); + ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) + + x.data.overflow_arithmetic.vector_len * 1435156945; } zig_unreachable(); } @@ -6128,6 +6464,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { return a.data.clz.bit_count == b.data.clz.bit_count; case ZigLLVMFnIdPopCount: return a.data.pop_count.bit_count == b.data.pop_count.bit_count; + case ZigLLVMFnIdBswap: + return a.data.bswap.bit_count == b.data.bswap.bit_count; + case ZigLLVMFnIdBitReverse: + return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: case ZigLLVMFnIdSqrt: @@ -6135,31 +6475,42 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { case ZigLLVMFnIdOverflowArithmetic: return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && - (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed); + (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) && + (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len); } zig_unreachable(); } // Canonicalize the array value as ConstArraySpecialNone void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { - assert(const_val->type->id == ZigTypeIdArray); + size_t elem_count; + ZigType *elem_type; + if (const_val->type->id == ZigTypeIdArray) { + elem_count = const_val->type->data.array.len; + elem_type = const_val->type->data.array.child_type; + } else if (const_val->type->id == ZigTypeIdVector) { + elem_count = const_val->type->data.vector.len; + elem_type = const_val->type->data.vector.elem_type; + } else { + zig_unreachable(); + } + if (const_val->special == ConstValSpecialUndef) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } switch (const_val->data.x_array.special) { case ConstArraySpecialNone: return; case ConstArraySpecialUndef: { const_val->data.x_array.special = ConstArraySpecialNone; - size_t elem_count = const_val->type->data.array.len; 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]; - element_val->type = const_val->type->data.array.child_type; + element_val->type = elem_type; init_const_undefined(g, element_val); - ConstParent *parent = get_const_val_parent(g, element_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = const_val; - parent->data.p_array.elem_index = i; - } + element_val->parent.id = ConstParentIdArray; + element_val->parent.data.p_array.array_val = const_val; + element_val->parent.data.p_array.elem_index = i; } return; } @@ -6170,7 +6521,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { g->string_literals_table.maybe_remove(buf); const_val->data.x_array.special = ConstArraySpecialNone; - size_t elem_count = const_val->type->data.array.len; 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) { @@ -6178,6 +6528,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { 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]); + this_char->parent.id = ConstParentIdArray; + this_char->parent.data.p_array.array_val = const_val; + this_char->parent.data.p_array.elem_index = i; } return; } @@ -6185,18 +6538,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { zig_unreachable(); } +// Deprecated. Reference the parent field directly. ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { - assert(value->type); - ZigType *type_entry = value->type; - if (type_entry->id == ZigTypeIdArray) { - expand_undef_array(g, value); - return &value->data.x_array.data.s_none.parent; - } else if (type_entry->id == ZigTypeIdStruct) { - return &value->data.x_struct.parent; - } else if (type_entry->id == ZigTypeIdUnion) { - return &value->data.x_union.parent; - } - return nullptr; + return &value->parent; } static const ZigTypeId all_type_ids[] = { @@ -6224,6 +6568,7 @@ static const ZigTypeId all_type_ids[] = { ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdPromise, + ZigTypeIdVector, }; ZigTypeId type_id_at_index(size_t index) { @@ -6289,6 +6634,8 @@ size_t type_id_index(ZigType *entry) { return 22; case ZigTypeIdPromise: return 23; + case ZigTypeIdVector: + return 24; } zig_unreachable(); } @@ -6345,6 +6692,8 @@ const char *type_id_name(ZigTypeId id) { return "Opaque"; case ZigTypeIdPromise: return "Promise"; + case ZigTypeIdVector: + return "Vector"; } zig_unreachable(); } @@ -6361,7 +6710,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; - if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && g->zig_target.os != OsIOS) { + if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && g->zig_target.os != OsIOS && g->zig_target.os != OsFreeBSD) { fprintf(stderr, "TODO linking against libc is currently incompatible with `--cache on`.\n" "Zig is not yet capable of determining whether the libc installation has changed on subsequent builds.\n"); exit(1); @@ -6424,7 +6773,7 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { resolve_top_level_decl(codegen, tld, false, nullptr); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; + ConstExprValue *var_value = tld_var->var->const_value; assert(var_value != nullptr); return var_value; } @@ -6500,6 +6849,7 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { case ZigTypeIdBool: return X64CABIClass_INTEGER; case ZigTypeIdFloat: + case ZigTypeIdVector: return X64CABIClass_SSE; case ZigTypeIdStruct: { // "If the size of an object is larger than four eightbytes, or it contains unaligned @@ -6579,3 +6929,36 @@ uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *f LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(struct_type->type_ref, field->gen_index); return LLVMStoreSizeOfType(g->target_data_ref, field_type); } + +Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, + ConstExprValue *const_val, ZigType *wanted_type) +{ + ConstExprValue ptr_val = {}; + ptr_val.special = ConstValSpecialStatic; + ptr_val.type = get_pointer_to_type(codegen, wanted_type, true); + ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst; + ptr_val.data.x_ptr.special = ConstPtrSpecialRef; + ptr_val.data.x_ptr.data.ref.pointee = const_val; + if (const_ptr_pointee(ira, codegen, &ptr_val, source_node) == nullptr) + return ErrorSemanticAnalyzeFail; + + return ErrorNone; +} + +const char *container_string(ContainerKind kind) { + switch (kind) { + case ContainerKindEnum: return "enum"; + case ContainerKindStruct: return "struct"; + case ContainerKindUnion: return "union"; + } + zig_unreachable(); +} + +bool ptr_allows_addr_zero(ZigType *ptr_type) { + if (ptr_type->id == ZigTypeIdPointer) { + return ptr_type->data.pointer.allow_zero; + } else if (ptr_type->id == ZigTypeIdOptional) { + return true; + } + return false; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index b506b533ca..956ef47309 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -12,14 +12,17 @@ void semantic_analyze(CodeGen *g); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); +ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg); ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); ZigType *new_type_table_entry(ZigTypeId id); 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, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, ZigType *type_entry); +uint64_t type_size_store(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); +ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type); 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); @@ -42,7 +45,9 @@ void find_libc_include_path(CodeGen *g); void find_libc_lib_path(CodeGen *g); bool type_has_bits(ZigType *type_entry); - +bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry); +bool ptr_allows_addr_zero(ZigType *ptr_type); +bool type_is_nonnull_ptr(ZigType *type); ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code); @@ -73,6 +78,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag); bool is_ref(ZigType *type_entry); bool is_array_ref(ZigType *type_entry); bool is_container_ref(ZigType *type_entry); +bool is_valid_vector_elem_type(ZigType *elem_type); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); void preview_use_decl(CodeGen *g, AstNode *node); @@ -81,7 +87,7 @@ ZigFn *scope_fn_entry(Scope *scope); ImportTableEntry *get_scope_import(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); + bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); ZigFn *create_fn(CodeGen *g, AstNode *proto_node); ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value); @@ -212,6 +218,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); bool type_is_c_abi_int(CodeGen *g, ZigType *ty); bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id); +const char *container_string(ContainerKind kind); uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field); @@ -222,4 +229,15 @@ enum ReqCompTime { }; ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); +enum OnePossibleValue { + OnePossibleValueInvalid, + OnePossibleValueNo, + OnePossibleValueYes, +}; +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); + +void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn); #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f7eb646e16..7b57841205 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -132,13 +132,23 @@ static const char *const_or_var_string(bool is_const) { return is_const ? "const" : "var"; } -const char *container_string(ContainerKind kind) { - switch (kind) { - case ContainerKindEnum: return "enum"; - case ContainerKindStruct: return "struct"; - case ContainerKindUnion: return "union"; +static const char *thread_local_string(Token *tok) { + return (tok == nullptr) ? "" : "threadlocal "; +} + +static const char *token_to_ptr_len_str(Token *tok) { + assert(tok != nullptr); + switch (tok->id) { + case TokenIdStar: + case TokenIdStarStar: + return "*"; + case TokenIdBracketStarBracket: + return "[*]"; + case TokenIdBracketStarCBracket: + return "[*c]"; + default: + zig_unreachable(); } - zig_unreachable(); } static const char *node_type_str(NodeType node_type) { @@ -233,8 +243,8 @@ static const char *node_type_str(NodeType node_type) { return "ErrorType"; case NodeTypeIfErrorExpr: return "IfErrorExpr"; - case NodeTypeTestExpr: - return "TestExpr"; + case NodeTypeIfOptional: + return "IfOptional"; case NodeTypeErrorSetDecl: return "ErrorSetDecl"; case NodeTypeCancel: @@ -387,7 +397,7 @@ static bool statement_terminates_without_semicolon(AstNode *node) { if (node->data.if_err_expr.else_node) return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); return node->data.if_err_expr.then_node->type == NodeTypeBlock; - case NodeTypeTestExpr: + case NodeTypeIfOptional: if (node->data.test_expr.else_node) return statement_terminates_without_semicolon(node->data.test_expr.else_node); return node->data.test_expr.then_node->type == NodeTypeBlock; @@ -554,8 +564,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); const char *extern_str = extern_string(node->data.variable_declaration.is_extern); + const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok); const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); - fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var); + fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var); print_symbol(ar, node->data.variable_declaration.symbol); if (node->data.variable_declaration.type) { @@ -639,13 +650,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - const char *star = "[*]"; - if (node->data.pointer_type.star_token != nullptr && - (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar)) - { - star = "*"; - } - fprintf(ar->f, "%s", star); + const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token); + fprintf(ar->f, "%s", ptr_len_str); if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); render_node_grouped(ar, node->data.pointer_type.align_expr); @@ -974,7 +980,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } - case NodeTypeTestExpr: + case NodeTypeIfOptional: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.test_expr.target_node); diff --git a/src/ast_render.hpp b/src/ast_render.hpp index d37002d8c7..1652156eee 100644 --- a/src/ast_render.hpp +++ b/src/ast_render.hpp @@ -17,7 +17,4 @@ void ast_print(FILE *f, AstNode *node, int indent); void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size); -const char *container_string(ContainerKind kind); - #endif - diff --git a/src/bigint.cpp b/src/bigint.cpp index 8a8d028e82..7299f2379c 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1722,3 +1722,4 @@ void bigint_incr(BigInt *x) { bigint_add(x, ©, &one); } + diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp index 5e6c3b9a9d..4526a83c27 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -222,14 +222,9 @@ static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents assert(chf->path != nullptr); OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file))) + if ((err = os_file_open_r(chf->path, &this_file, &chf->mtime))) return err; - if ((err = os_file_mtime(this_file, &chf->mtime))) { - os_file_close(this_file); - return err; - } - if ((err = hash_file(chf->bin_digest, this_file, contents))) { os_file_close(this_file); return err; @@ -351,17 +346,12 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) { // if the mtime matches we can trust the digest OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file))) { + OsTimeStamp actual_mtime; + if ((err = os_file_open_r(chf->path, &this_file, &actual_mtime))) { fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err)); os_file_close(ch->manifest_file); return ErrorCacheUnavailable; } - OsTimeStamp actual_mtime; - if ((err = os_file_mtime(this_file, &actual_mtime))) { - os_file_close(this_file); - os_file_close(ch->manifest_file); - return err; - } if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) { os_file_close(this_file); } else { diff --git a/src/codegen.cpp b/src/codegen.cpp index 1033ed8120..d2b2836b0c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = { }; CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir) + Buf *zig_lib_dir, Buf *override_std_dir) { CodeGen *g = allocate(1); @@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->zig_lib_dir = zig_lib_dir; - g->zig_std_dir = buf_alloc(); - os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + if (override_std_dir == nullptr) { + g->zig_std_dir = buf_alloc(); + os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + } else { + g->zig_std_dir = override_std_dir; + } g->zig_c_headers_dir = buf_alloc(); os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); @@ -118,6 +122,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->string_literals_table.init(16); g->type_info_cache.init(32); g->is_test_build = false; + g->is_single_threaded = false; buf_resize(&g->global_asm, 0); for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { @@ -178,7 +183,8 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out // On Darwin/MacOS/iOS, we always link libSystem which contains libc. if (g->zig_target.os == OsMacOSX || - g->zig_target.os == OsIOS) + g->zig_target.os == OsIOS || + g->zig_target.os == OsFreeBSD) { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); @@ -291,11 +297,6 @@ void codegen_add_framework(CodeGen *g, const char *framework) { g->darwin_frameworks.append(buf_create_from_str(framework)); } -void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole) { - g->windows_subsystem_windows = mwindows; - g->windows_subsystem_console = mconsole; -} - void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min) { g->mmacosx_version_min = mmacosx_version_min; } @@ -317,6 +318,8 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char * 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 generate_error_name_table(CodeGen *g); +static bool value_is_all_undef(ConstExprValue *const_val); +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -465,6 +468,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag } } +static bool cc_want_sret_attr(CallingConvention cc) { + switch (cc) { + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionC: + case CallingConventionCold: + case CallingConventionStdcall: + return true; + case CallingConventionAsync: + case CallingConventionUnspecified: + return false; + } + zig_unreachable(); +} + static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -599,12 +617,13 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { unsigned init_gen_i = 0; if (!type_has_bits(return_type)) { // nothing to do - } else if (type_is_codegen_pointer(return_type)) { + } else if (type_is_nonnull_ptr(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { - addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + // Sret pointers must not be address 0 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); - if (cc == CallingConventionC) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + if (cc_want_sret_attr(cc)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); } init_gen_i = 1; @@ -619,6 +638,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); if (err_ret_trace_arg_index != UINT32_MAX) { + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); } @@ -653,7 +674,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, fn_di_scope, buf_ptr(&fn_table_entry->symbol_name), "", import->di_file, line_number, - fn_table_entry->type_entry->di_type, is_internal_linkage, + fn_table_entry->type_entry->data.fn.raw_di_type, is_internal_linkage, is_definition, scope_line, flags, is_optimized, nullptr); scope->di_scope = ZigLLVMSubprogramToScope(subprogram); @@ -697,38 +718,59 @@ static void clear_debug_source_node(CodeGen *g) { ZigLLVMClearCurrentDebugLocation(g->builder); } -static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry, +static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type, const char *signed_name, const char *unsigned_name) { + ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; char fn_name[64]; - assert(type_entry->id == ZigTypeIdInt); - const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name; - sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, type_entry->data.integral.bit_count); + assert(int_type->id == ZigTypeIdInt); + const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name; - LLVMTypeRef return_elem_types[] = { - type_entry->type_ref, - LLVMInt1Type(), - }; LLVMTypeRef param_types[] = { - type_entry->type_ref, - type_entry->type_ref, + operand_type->type_ref, + operand_type->type_ref, }; - LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); - LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); - assert(LLVMGetIntrinsicID(fn_val)); - return fn_val; + + if (operand_type->id == ZigTypeIdVector) { + sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu32 "i%" PRIu32, signed_str, + operand_type->data.vector.len, int_type->data.integral.bit_count); + + LLVMTypeRef return_elem_types[] = { + operand_type->type_ref, + LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len), + }; + LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + return fn_val; + } else { + sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count); + + LLVMTypeRef return_elem_types[] = { + operand_type->type_ref, + LLVMInt1Type(), + }; + LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + return fn_val; + } } -static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) { - assert(type_entry->id == ZigTypeIdInt); +static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) { + ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + assert(int_type->id == ZigTypeIdInt); ZigLLVMFnKey key = {}; key.id = ZigLLVMFnIdOverflowArithmetic; - key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed; + key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed; key.data.overflow_arithmetic.add_sub_mul = add_sub_mul; - key.data.overflow_arithmetic.bit_count = (uint32_t)type_entry->data.integral.bit_count; + key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count; + key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.len : 0; auto existing_entry = g->llvm_fn_table.maybe_get(key); if (existing_entry) @@ -737,13 +779,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM LLVMValueRef fn_val; switch (add_sub_mul) { case AddSubMulAdd: - fn_val = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd"); + fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd"); break; case AddSubMulSub: - fn_val = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub"); + fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub"); break; case AddSubMulMul: - fn_val = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul"); + fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul"); break; } @@ -911,6 +953,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("invalid enum value"); case PanicMsgIdFloatToInt: return buf_create_from_str("integer part of floating point value out of bounds"); + case PanicMsgIdPtrCastNull: + return buf_create_from_str("cast causes pointer to be null"); } zig_unreachable(); } @@ -1205,6 +1249,8 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { LLVMSetFunctionCallConv(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 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); if (g->build_mode == BuildModeDebug) { ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); @@ -1279,9 +1325,13 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { LLVMSetFunctionCallConv(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 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); addLLVMArgAttr(fn_val, (unsigned)0, "writeonly"); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)1, "nonnull"); addLLVMArgAttr(fn_val, (unsigned)1, "noalias"); addLLVMArgAttr(fn_val, (unsigned)1, "readonly"); @@ -1409,6 +1459,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { LLVMSetFunctionCallConv(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 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); if (g->build_mode == BuildModeDebug) { ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); @@ -1633,10 +1685,25 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMPositionBuilderAtEnd(g->builder, ok_block); } +static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { + LLVMValueRef zero = LLVMConstNull(int_type->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdCastTruncatedData); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type, ZigType *wanted_type, LLVMValueRef expr_val) { assert(actual_type->id == wanted_type->id); + assert(expr_val != nullptr); uint64_t actual_bits; uint64_t wanted_bits; @@ -1689,17 +1756,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z if (!want_runtime_safety) return nullptr; - LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdCastTruncatedData); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return nullptr; + return gen_assert_zero(g, expr_val, actual_type); } LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); if (!want_runtime_safety) { @@ -1729,17 +1786,49 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z } } -static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *type_entry, AddSubMul op, +typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); +// These are lookup table using the AddSubMul enum as the lookup. +// If AddSubMul ever changes, then these tables will be out of +// date. +static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; +static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; +static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; +static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; + +static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op, LLVMValueRef val1, LLVMValueRef val2) { - LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op); - LLVMValueRef params[] = { - val1, - val2, - }; - 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, ""); + LLVMValueRef overflow_bit; + LLVMValueRef result; + + if (operand_type->id == ZigTypeIdVector) { + ZigType *int_type = operand_type->data.vector.elem_type; + assert(int_type->id == ZigTypeIdInt); + LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1); + LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len); + const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt; + LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, ""); + LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, ""); + LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, ""); + result = LLVMBuildTrunc(g->builder, extended_result, operand_type->type_ref, ""); + + LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, ""); + LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, ""); + LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len); + LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, ""); + LLVMValueRef zero = LLVMConstNull(bitcast_int_type); + overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, ""); + } else { + LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op); + LLVMValueRef params[] = { + val1, + val2, + }; + LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); + overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); + } + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); @@ -1907,9 +1996,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { } static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); + LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment); return result; } @@ -1967,14 +2055,14 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ break; } - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector || ty->id == ZigTypeIdInt // TODO investigate if we need to change this ) { switch (fn_walk->id) { case FnWalkIdAttrs: { ZigType *ptr_type = get_codegen_ptr_type(ty); if (ptr_type != nullptr) { - if (ty->id != ZigTypeIdOptional) { + if (type_is_nonnull_ptr(ty)) { addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); } if (ptr_type->data.pointer.is_const) { @@ -2018,6 +2106,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ 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; @@ -2057,6 +2146,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdAttrs: addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + // Byvalue parameters must not have address 0 addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); fn_walk->data.attrs.gen_i += 1; break; @@ -2189,7 +2279,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); } - if (param_type->id == ZigTypeIdPointer) { + if (type_is_nonnull_ptr(param_type)) { addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); } break; @@ -2204,10 +2294,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { assert(variable); assert(variable->value_ref); - if (!handle_is_ptr(variable->value->type)) { + if (!handle_is_ptr(variable->var_type)) { clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, - variable->align_bytes, false); + gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), + variable->value_ref, variable->align_bytes, false); } if (variable->decl_node) { @@ -2582,9 +2672,10 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, (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 == PtrLenUnknown) + op1->value.type->data.pointer.ptr_len != PtrLenSingle) ); - ZigType *type_entry = 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 && ir_want_runtime_safety(g, &bin_op_instruction->base); @@ -2610,46 +2701,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: - if (type_entry->id == ZigTypeIdFloat) { + if (scalar_type->id == ZigTypeIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == ZigTypeIdInt) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); + } else if (scalar_type->id == ZigTypeIdInt) { + LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == ZigTypeIdEnum || - type_entry->id == ZigTypeIdErrorSet || - type_entry->id == ZigTypeIdBool || - get_codegen_ptr_type(type_entry) != nullptr) + } else if (scalar_type->id == ZigTypeIdEnum || + scalar_type->id == ZigTypeIdErrorSet || + scalar_type->id == ZigTypeIdBool || + get_codegen_ptr_type(scalar_type) != nullptr) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else { zig_unreachable(); } + case IrBinOpMult: + case IrBinOpMultWrap: case IrBinOpAdd: case IrBinOpAddWrap: - if (type_entry->id == ZigTypeIdPointer) { - assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); + case IrBinOpSub: + case IrBinOpSubWrap: { + bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); + AddSubMul add_sub_mul = + op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : + op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : + 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."); + + switch (add_sub_mul) { + case AddSubMulAdd: + subscript_value = op2_value; + break; + case AddSubMulSub: + subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); + break; + case AddSubMulMul: + zig_unreachable(); + } + // TODO runtime safety - return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); - } else if (type_entry->id == ZigTypeIdFloat) { + return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); + } else if (scalar_type->id == ZigTypeIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); - return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); - } else if (type_entry->id == ZigTypeIdInt) { - bool is_wrapping = (op_id == IrBinOpAddWrap); + return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); + } else if (scalar_type->id == ZigTypeIdInt) { if (is_wrapping) { - return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); + return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); } else if (want_runtime_safety) { - return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value); - } else if (type_entry->data.integral.is_signed) { - return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); + return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); + } else if (scalar_type->data.integral.is_signed) { + return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); } else { - return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); + return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); } } else { zig_unreachable(); } + } case IrBinOpBinOr: return LLVMBuildOr(g->builder, op1_value, op2_value, ""); case IrBinOpBinXor: @@ -2659,15 +2775,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpBitShiftLeftLossy: case IrBinOpBitShiftLeftExact: { - assert(type_entry->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, - type_entry, op2_value); + assert(scalar_type->id == ZigTypeIdInt); + 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, ""); } else if (want_runtime_safety) { - return gen_overflow_shl_op(g, type_entry, op1_value, op2_casted); - } else if (type_entry->data.integral.is_signed) { + return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); + } else if (scalar_type->data.integral.is_signed) { return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); } else { return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); @@ -2676,85 +2791,41 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpBitShiftRightLossy: case IrBinOpBitShiftRightExact: { - assert(type_entry->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, - type_entry, op2_value); + assert(scalar_type->id == ZigTypeIdInt); + 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 (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); } else { return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); } } else if (want_runtime_safety) { - return gen_overflow_shr_op(g, type_entry, op1_value, op2_casted); - } else if (type_entry->data.integral.is_signed) { + return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); + } else if (scalar_type->data.integral.is_signed) { return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); } else { return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); } } - case IrBinOpSub: - case IrBinOpSubWrap: - if (type_entry->id == ZigTypeIdPointer) { - assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); - // TODO runtime safety - LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); - return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); - } else if (type_entry->id == ZigTypeIdFloat) { - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); - return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); - } else if (type_entry->id == ZigTypeIdInt) { - bool is_wrapping = (op_id == IrBinOpSubWrap); - if (is_wrapping) { - return LLVMBuildSub(g->builder, op1_value, op2_value, ""); - } else if (want_runtime_safety) { - return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value); - } else if (type_entry->data.integral.is_signed) { - return LLVMBuildNSWSub(g->builder, op1_value, op2_value, ""); - } else { - return LLVMBuildNUWSub(g->builder, op1_value, op2_value, ""); - } - } else { - zig_unreachable(); - } - case IrBinOpMult: - case IrBinOpMultWrap: - if (type_entry->id == ZigTypeIdFloat) { - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); - return LLVMBuildFMul(g->builder, op1_value, op2_value, ""); - } else if (type_entry->id == ZigTypeIdInt) { - bool is_wrapping = (op_id == IrBinOpMultWrap); - if (is_wrapping) { - return LLVMBuildMul(g->builder, op1_value, op2_value, ""); - } else if (want_runtime_safety) { - return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value); - } else if (type_entry->data.integral.is_signed) { - return LLVMBuildNSWMul(g->builder, op1_value, op2_value, ""); - } else { - return LLVMBuildNUWMul(g->builder, op1_value, op2_value, ""); - } - } else { - zig_unreachable(); - } case IrBinOpDivUnspecified: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, DivKindFloat); + op1_value, op2_value, scalar_type, DivKindFloat); case IrBinOpDivExact: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, DivKindExact); + op1_value, op2_value, scalar_type, DivKindExact); case IrBinOpDivTrunc: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, DivKindTrunc); + op1_value, op2_value, scalar_type, DivKindTrunc); case IrBinOpDivFloor: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, DivKindFloor); + op1_value, op2_value, scalar_type, DivKindFloor); case IrBinOpRemRem: return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, RemKindRem); + op1_value, op2_value, scalar_type, RemKindRem); case IrBinOpRemMod: return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, type_entry, RemKindMod); + op1_value, op2_value, scalar_type, RemKindMod); } zig_unreachable(); } @@ -2882,32 +2953,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - return cast_instruction->tmp_ptr; - } - case CastOpBytesToSlice: - { - assert(cast_instruction->tmp_ptr); - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdArray); - - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_ptr_index, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_len_index, ""); - LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - actual_type->data.array.len / type_size(g, wanted_child_type), false); - gen_store_untyped(g, len_val, len_ptr, 0, false); - return cast_instruction->tmp_ptr; } case CastOpIntToFloat: @@ -2991,14 +3036,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, - IrInstructionPtrCast *instruction) + IrInstructionPtrCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; if (!type_has_bits(wanted_type)) { return nullptr; } LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); + LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); + bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + if (!want_safety_check || ptr_allows_addr_zero(wanted_type)) + return result_ptr; + + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr)); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, ""); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdPtrCastNull); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return result_ptr; } static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, @@ -3179,11 +3239,11 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVar *decl_var_instruction) + IrInstructionDeclVarGen *decl_var_instruction) { ZigVar *var = decl_var_instruction->var; - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) return nullptr; if (var->ref_count == 0 && g->build_mode != BuildModeDebug) @@ -3191,34 +3251,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstruction *init_value = decl_var_instruction->init_value; - bool have_init_expr = false; - - ConstExprValue *const_val = &init_value->value; - if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic) - have_init_expr = true; + bool have_init_expr = !value_is_all_undef(&init_value->value); if (have_init_expr) { - assert(var->value->type == init_value->value.type); - ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, + ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false, PtrLenSingle, var->align_bytes, 0, 0); LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); - } else { - bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); - if (want_safe) { - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - assert(size_bytes > 0); - - assert(var->align_bytes > 0); - - // memset uninitialized memory to 0xa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); - LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false); - } + } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { + uint32_t align_bytes = (var->align_bytes == 0) ? get_abi_alignment(g, var->var_type) : var->align_bytes; + gen_undef_init(g, align_bytes, var->var_type, var->value_ref); } gen_var_debug_decl(g, var); @@ -3255,21 +3297,81 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); } +static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < len; i += 1) { + if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); +} + +static bool value_is_all_undef(ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + 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(&const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else if (const_val->type->id == ZigTypeIdArray) { + return value_is_all_undef_array(const_val, const_val->type->data.array.len); + } else if (const_val->type->id == ZigTypeIdVector) { + return value_is_all_undef_array(const_val, const_val->type->data.vector.len); + } else { + return false; + } + } + zig_unreachable(); +} + +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + assert(type_has_bits(value_type)); + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); +} + static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); - - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + if (!type_has_bits(ptr_type)) + return nullptr; - gen_assign_raw(g, ptr, ptr_type, value); - + bool have_init_expr = !value_is_all_undef(&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, + ir_llvm_value(g, instruction->ptr)); + } return nullptr; } static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) { ZigVar *var = instruction->var; - if (type_has_bits(var->value->type)) { + if (type_has_bits(var->var_type)) { assert(var->value_ref); return var->value_ref; } else { @@ -3583,7 +3685,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMPositionBuilderAtEnd(g->builder, ok_block); } - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, ""); + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_union_index, ""); LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); return bitcasted_union_field_ptr; } @@ -3745,8 +3848,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR if (child_type->zero_bits) { return maybe_handle; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); } else { LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); @@ -3761,17 +3864,17 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); } -static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapOptional *instruction) +static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapPtr *instruction) { - ZigType *ptr_type = instruction->value->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); ZigType *child_type = maybe_type->data.maybe.child_type; - LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); + LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); @@ -3785,8 +3888,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (child_type->zero_bits) { return nullptr; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return maybe_ptr; } else { LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); @@ -3814,6 +3917,16 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnI n_args = 1; key.id = ZigLLVMFnIdPopCount; key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdBswap) { + fn_name = "bswap"; + n_args = 1; + key.id = ZigLLVMFnIdBswap; + key.data.bswap.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdBitReverse) { + fn_name = "bitreverse"; + n_args = 1; + key.id = ZigLLVMFnIdBitReverse; + key.data.bit_reverse.bit_count = (uint32_t)int_type->data.integral.bit_count; } else { zig_unreachable(); } @@ -4194,7 +4307,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) zig_unreachable(); } -static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) { +static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) { LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); @@ -4209,18 +4322,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - if (type_is_codegen_pointer(child_type)) { + if (!handle_is_ptr(maybe_type)) { LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); } assert(instruction->tmp_ptr != nullptr); - assert(type_has_bits(instruction->type)); + assert(type_has_bits(child_type)); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); @@ -4371,6 +4484,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst assert(array_type->data.structure.is_slice); 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; assert(ptr_index != SIZE_MAX); @@ -4560,12 +4674,14 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); } -static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { - ZigType *ptr_type = instruction->value->value.type; +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrCode *instruction) +{ + ZigType *ptr_type = instruction->err_union->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; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); if (type_has_bits(payload_type)) { @@ -4576,7 +4692,13 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab } } -static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { +static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrPayload *instruction) +{ + bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && + g->errors_by_index.length > 1; + if (!want_safety && !type_has_bits(instruction->base.value.type)) + return nullptr; ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; @@ -4588,7 +4710,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu return err_union_handle; } - if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { + if (want_safety) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -4627,7 +4749,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I } LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - if (type_is_codegen_pointer(child_type)) { + if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return payload_val; } @@ -5098,6 +5220,74 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } +static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { + LLVMValueRef op = ir_llvm_value(g, instruction->op); + ZigType *int_type = instruction->base.value.type; + assert(int_type->id == ZigTypeIdInt); + if (int_type->data.integral.bit_count % 16 == 0) { + LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value.type, BuiltinFnIdBswap); + return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); + } + // Not an even number of bytes, so we zext 1 byte, then bswap, shift right 1 byte, truncate + ZigType *extended_type = get_int_type(g, int_type->data.integral.is_signed, + int_type->data.integral.bit_count + 8); + // aabbcc + LLVMValueRef extended = LLVMBuildZExt(g->builder, op, extended_type->type_ref, ""); + // 00aabbcc + LLVMValueRef fn_val = get_int_builtin_fn(g, extended_type, BuiltinFnIdBswap); + LLVMValueRef swapped = LLVMBuildCall(g->builder, fn_val, &extended, 1, ""); + // ccbbaa00 + LLVMValueRef shifted = ZigLLVMBuildLShrExact(g->builder, swapped, + LLVMConstInt(extended_type->type_ref, 8, false), ""); + // 00ccbbaa + return LLVMBuildTrunc(g->builder, shifted, int_type->type_ref, ""); +} + +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; + assert(int_type->id == ZigTypeIdInt); + 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; + assert(array_type->id == ZigTypeIdArray); + assert(handle_is_ptr(array_type)); + assert(instruction->tmp_ptr); + LLVMValueRef vector = ir_llvm_value(g, instruction->vector); + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, + LLVMPointerType(instruction->vector->value.type->type_ref, 0), ""); + gen_store_untyped(g, vector, casted_ptr, 0, false); + return instruction->tmp_ptr; +} + +static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable, + IrInstructionArrayToVector *instruction) +{ + 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); + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, + LLVMPointerType(vector_type->type_ref, 0), ""); + return gen_load_untyped(g, casted_ptr, 0, false, ""); +} + +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; + if (ir_want_runtime_safety(g, &instruction->base)) { + return gen_assert_zero(g, target, int_type); + } + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5137,6 +5327,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdCUndef: case IrInstructionIdEmbedFile: case IrInstructionIdIntType: + case IrInstructionIdVectorType: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: @@ -5173,12 +5364,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdCmpxchgSrc: zig_unreachable(); + case IrInstructionIdDeclVarGen: + return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); - case IrInstructionIdDeclVar: - return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction); case IrInstructionIdBinOp: return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCast: @@ -5209,8 +5403,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -5225,8 +5419,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_ref(g, executable, (IrInstructionRef *)instruction); case IrInstructionIdErrName: return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction); - case IrInstructionIdCmpxchg: - return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgGen: + return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction); case IrInstructionIdFence: return ir_render_fence(g, executable, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -5267,8 +5461,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdUnionInit: return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); - case IrInstructionIdPtrCast: - return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastGen: + return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); case IrInstructionIdBitCast: return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); case IrInstructionIdWidenOrShorten: @@ -5335,6 +5529,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); + case IrInstructionIdBswap: + return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction); + case IrInstructionIdBitReverse: + return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction); + case IrInstructionIdArrayToVector: + return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); + case IrInstructionIdVectorToArray: + return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); + case IrInstructionIdAssertZero: + return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); } zig_unreachable(); } @@ -5362,6 +5566,9 @@ 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_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5372,6 +5579,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent case ConstParentIdStruct: return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, parent->data.p_struct.field_index); + case ConstParentIdErrUnionCode: + return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); + case ConstParentIdErrUnionPayload: + return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); + case ConstParentIdOptionalPayload: + return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5387,7 +5600,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) { expand_undef_array(g, array_const_val); - ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent; + ConstParent *parent = &array_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); @@ -5412,7 +5625,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) { - ConstParent *parent = &struct_const_val->data.x_struct.parent; + ConstParent *parent = &struct_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5423,8 +5636,44 @@ 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) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_err_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) { + ConstParent *parent = &optional_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, maybe_child_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { - ConstParent *parent = &union_const_val->data.x_union.parent; + ConstParent *parent = &union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5494,6 +5743,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } case ZigTypeIdArray: zig_panic("TODO bit pack an array"); + case ZigTypeIdVector: + zig_panic("TODO bit pack a vector"); case ZigTypeIdUnion: zig_panic("TODO bit pack a union"); case ZigTypeIdStruct: @@ -5541,76 +5792,127 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con zig_unreachable(); case ConstPtrSpecialRef: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; render_const_val(g, pointee, ""); render_const_val_global(g, pointee, ""); - ConstExprValue *other_val = pointee; - const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); - render_const_val_global(g, const_val, ""); + const_val->global_refs->llvm_value = LLVMConstBitCast(pointee->global_refs->llvm_global, const_val->type->type_ref); return const_val->global_refs->llvm_value; } case ConstPtrSpecialBaseArray: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; assert(array_const_val->type->id == ZigTypeIdArray); - if (array_const_val->type->zero_bits) { + 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->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, - elem_index); + 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, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialBaseStruct: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; assert(struct_const_val->type->id == ZigTypeIdStruct); - if (struct_const_val->type->zero_bits) { + 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->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->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, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_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; + 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->type_ref), + const_val->type->type_ref); + return const_val->global_refs->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, const_val->type->type_ref); + const_val->global_refs->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; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + return const_val->global_refs->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, const_val->type->type_ref); + const_val->global_refs->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; + assert(optional_const_val->type->id == ZigTypeIdOptional); + if (optional_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; return ptr_val; } case ConstPtrSpecialHardCodedAddr: { - render_const_val_global(g, const_val, name); + 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(LLVMConstInt(usize->type_ref, addr_value, false), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); + const_val->global_refs->llvm_value = LLVMConstIntToPtr( + LLVMConstInt(usize->type_ref, addr_value, false), const_val->type->type_ref); return const_val->global_refs->llvm_value; } case ConstPtrSpecialFunction: return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + case ConstPtrSpecialNull: + return LLVMConstNull(const_val->type->type_ref); } zig_unreachable(); } +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *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(g->builtin_types.entry_global_error_set->type_ref, value, false); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { + Error err; + ZigType *type_entry = const_val->type; assert(!type_entry->zero_bits); @@ -5627,9 +5929,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdInt: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, - const_val->data.x_err_set->value, false); + return gen_const_val_err_set(g, const_val, name); case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: @@ -5663,6 +5963,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); + } else if (child_type->id == ZigTypeIdErrorSet) { + return gen_const_val_err_set(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; @@ -5754,6 +6056,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c } ConstExprValue *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))) + { + zig_unreachable(); + } + 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(field_val->type, val); @@ -5793,7 +6101,33 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true); } } + zig_unreachable(); } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return LLVMGetUndef(type_entry->type_ref); + 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]; + values[i] = gen_const_val(g, elem_value, ""); + } + return LLVMConstVector(values, len); + } + case ConstArraySpecialBuf: { + Buf *buf = const_val->data.x_array.data.s_buf; + assert(buf_len(buf) == len); + LLVMValueRef *values = allocate(len); + for (uint64_t i = 0; i < len; i += 1) { + values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false); + } + return LLVMConstVector(values, len); + } + } + zig_unreachable(); + } case ZigTypeIdUnion: { LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; @@ -5891,7 +6225,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c ZigType *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { assert(type_has_bits(err_set_type)); - uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + uint64_t value = (err_set == nullptr) ? 0 : err_set->value; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else if (!type_has_bits(err_set_type)) { assert(type_has_bits(payload_type)); @@ -5900,8 +6235,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; bool make_unnamed_struct; - if (const_val->data.x_err_union.err) { - err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set != nullptr) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false); err_payload_value = LLVMConstNull(payload_type->type_ref); make_unnamed_struct = false; } else { @@ -6075,6 +6411,12 @@ static void validate_inline_fns(CodeGen *g) { report_errors_and_maybe_exit(g); } +static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) { + if (var->is_thread_local && !g->is_single_threaded) { + LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); + } +} + static void do_code_gen(CodeGen *g) { assert(!g->errors.length); @@ -6107,10 +6449,13 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); ZigVar *var = tld_var->var; - if (var->value->type->id == ZigTypeIdComptimeFloat) { + if (var->var_type->id == ZigTypeIdComptimeFloat) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } ZigType *var_type = g->builtin_types.entry_f128; ConstExprValue coerced_value; coerced_value.special = ConstValSpecialStatic; @@ -6121,10 +6466,13 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == ZigTypeIdComptimeInt) { + if (var->var_type->id == ZigTypeIdComptimeInt) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); if (bits_needed < 8) { bits_needed = 8; @@ -6135,7 +6483,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) continue; assert(var->decl_node); @@ -6144,22 +6492,23 @@ static void do_code_gen(CodeGen *g) { if (var->linkage == VarLinkageExternal) { LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name)); if (existing_llvm_var) { - global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0)); + global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0)); } else { - global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name)); + global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name)); // TODO debug info for the extern variable LLVMSetLinkage(global_value, LLVMExternalLinkage); maybe_import_dll(g, global_value, GlobalLinkageIdStrong); LLVMSetAlignment(global_value, var->align_bytes); LLVMSetGlobalConstant(global_value, var->gen_is_const); + set_global_tls(g, var, global_value); } } else { bool exported = (var->linkage == VarLinkageExport); const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); - render_const_val(g, var->value, mangled_name); - render_const_val_global(g, var->value, mangled_name); - global_value = var->value->global_refs->llvm_global; + render_const_val(g, var->const_value, mangled_name); + render_const_val_global(g, var->const_value, mangled_name); + global_value = var->const_value->global_refs->llvm_global; if (exported) { LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6171,11 +6520,14 @@ static void do_code_gen(CodeGen *g) { LLVMSetAlignment(global_value, var->align_bytes); // TODO debug info for function pointers - if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type); + // 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); } LLVMSetGlobalConstant(global_value, var->gen_is_const); + set_global_tls(g, var, global_value); } var->value_ref = global_value; @@ -6226,6 +6578,7 @@ static void do_code_gen(CodeGen *g) { IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); LLVMValueRef *slot; ZigType *slot_type = instruction->value.type; + uint32_t alignment_bytes = 0; if (instruction->id == IrInstructionIdCast) { IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction; slot = &cast_instruction->tmp_ptr; @@ -6258,13 +6611,17 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCmpxchg) { - IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + } else if (instruction->id == IrInstructionIdCmpxchgGen) { + IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdVectorToArray) { + IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; + alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); + slot = &vector_to_array_instruction->tmp_ptr; } else { zig_unreachable(); } - *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); + *slot = build_alloca(g, slot_type, "", alignment_bytes); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); @@ -6281,12 +6638,12 @@ static void do_code_gen(CodeGen *g) { for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); - if (!type_has_bits(var->value->type)) { + if (!type_has_bits(var->var_type)) { continue; } if (ir_get_var_is_comptime(var)) continue; - switch (type_requires_comptime(g, var->value->type)) { + switch (type_requires_comptime(g, var->var_type)) { case ReqCompTimeInvalid: zig_unreachable(); case ReqCompTimeYes: @@ -6296,11 +6653,11 @@ static void do_code_gen(CodeGen *g) { } if (var->src_arg_index == SIZE_MAX) { - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), - var->value->type->di_type, !g->strip_debug_symbols, 0); + var->var_type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { fn_walk_var.data.vars.var = var; @@ -6310,16 +6667,16 @@ static void do_code_gen(CodeGen *g) { ZigType *gen_type; FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; - if (handle_is_ptr(var->value->type)) { + if (handle_is_ptr(var->var_type)) { if (gen_info->is_byval) { - gen_type = var->value->type; + gen_type = var->var_type; } else { gen_type = gen_info->type; } var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index); } else { - gen_type = var->value->type; - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + gen_type = var->var_type; + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); } if (var->decl_node) { var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), @@ -6719,6 +7076,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int + create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); @@ -6757,6 +7115,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); create_builtin_fn(g, BuiltinFnIdThis, "This", 0); + create_builtin_fn(g, BuiltinFnIdBswap, "bswap", 2); + create_builtin_fn(g, BuiltinFnIdBitReverse, "bitreverse", 2); } static const char *bool_to_str(bool b) { @@ -6784,6 +7144,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " instruction_addresses: []usize,\n" "};\n\n"); + buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n"); + const char *cur_os = nullptr; { buf_appendf(contents, "pub const Os = enum {\n"); @@ -6942,6 +7304,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " ArgTuple: void,\n" " Opaque: void,\n" " Promise: Promise,\n" + " Vector: Vector,\n" "\n\n" " pub const Int = struct {\n" " is_signed: bool,\n" @@ -6963,6 +7326,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " One,\n" " Many,\n" " Slice,\n" + " C,\n" " };\n" " };\n" "\n" @@ -7060,6 +7424,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " child: ?type,\n" " };\n" "\n" + " pub const Vector = struct {\n" + " len: u32,\n" + " child: type,\n" + " };\n" + "\n" " pub const Definition = struct {\n" " name: []const u8,\n" " is_pub: bool,\n" @@ -7128,6 +7497,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const endian = %s;\n", endian_str); } 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); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ); @@ -7162,6 +7532,7 @@ 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->is_test_build); + cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target.arch.arch); cache_int(&cache_hash, g->zig_target.arch.sub_arch); cache_int(&cache_hash, g->zig_target.vendor); @@ -7210,6 +7581,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); 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); g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); scan_import(g, g->compile_var_import); @@ -7231,8 +7603,7 @@ static void init(CodeGen *g) { } if (g->is_test_build) { - g->windows_subsystem_windows = false; - g->windows_subsystem_console = true; + g->subsystem = TargetSubsystemConsole; } assert(g->root_out_name); @@ -7251,7 +7622,13 @@ static void init(CodeGen *g) { LLVMTargetRef target_ref; char *err_msg = nullptr; if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) { - zig_panic("unable to create target based on: %s", buf_ptr(&g->triple_str)); + fprintf(stderr, + "Zig is expecting LLVM to understand this target: '%s'\n" + "However LLVM responded with: \"%s\"\n" + "Zig is unable to continue. This is a bug in Zig:\n" + "https://github.com/ziglang/zig/issues/438\n" + , buf_ptr(&g->triple_str), err_msg); + exit(1); } bool is_optimized = g->build_mode != BuildModeDebug; @@ -7268,7 +7645,7 @@ static void init(CodeGen *g) { // 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. - if (g->zig_target.os == OsWindows) { + if (g->zig_target.os == OsWindows || g->zig_target.os == OsUefi) { target_specific_cpu_args = ""; target_specific_features = ""; } else { @@ -7434,9 +7811,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; - this_val->data.x_struct.parent.id = ConstParentIdArray; - this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array; - this_val->data.x_struct.parent.data.p_array.elem_index = i; + 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); ConstExprValue *name_field = &this_val->data.x_struct.fields[0]; @@ -7514,6 +7891,7 @@ static void gen_root_source(CodeGen *g) { report_errors_and_maybe_exit(g); if (!g->is_test_build && g->zig_target.os != OsFreestanding && + g->zig_target.os != OsUefi && !g->have_c_main && !g->have_winmain && !g->have_winmain_crt_startup && ((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe)) { @@ -7537,6 +7915,8 @@ static void gen_root_source(CodeGen *g) { } } + typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn); + report_errors_and_maybe_exit(g); } @@ -7631,6 +8011,9 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdArray: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); return; + case ZigTypeIdVector: + prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type); + return; case ZigTypeIdOptional: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); return; @@ -7762,6 +8145,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu buf_appendf(out_buf, "%s", buf_ptr(child_buf)); return; } + case ZigTypeIdVector: + zig_panic("TODO implement get_c_type for vector types"); case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdFn: @@ -7927,6 +8312,7 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: zig_unreachable(); case ZigTypeIdEnum: if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { @@ -8033,8 +8419,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { if (!entry) break; - cache_buf(ch, entry->key); - add_cache_pkg(g, ch, entry->value); + // TODO: I think we need a more sophisticated detection of + // packages we have already seen + if (entry->value != pkg) { + cache_buf(ch, entry->key); + add_cache_pkg(g, ch, entry->value); + } } } @@ -8070,12 +8460,12 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->zig_target.os); cache_int(ch, g->zig_target.env_type); cache_int(ch, g->zig_target.oformat); + cache_int(ch, g->subsystem); cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); + cache_bool(ch, g->is_single_threaded); cache_bool(ch, g->is_native_target); - cache_bool(ch, g->windows_subsystem_windows); - cache_bool(ch, g->windows_subsystem_console); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->no_rosegment_workaround); cache_bool(ch, g->each_lib_rpath); diff --git a/src/codegen.hpp b/src/codegen.hpp index 1d39e4bf5e..4bd8f2dcca 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -15,7 +15,7 @@ #include CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir); + Buf *zig_lib_dir, Buf *override_std_dir); 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); @@ -33,7 +33,6 @@ void codegen_set_libc_include_dir(CodeGen *codegen, Buf *libc_include_dir); void codegen_set_msvc_lib_dir(CodeGen *g, Buf *msvc_lib_dir); void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir); void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker); -void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); diff --git a/src/ir.cpp b/src/ir.cpp index 68a0b7f6f6..0fcbb60fe8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -61,7 +61,7 @@ enum ConstCastResultId { ConstCastResultIdType, ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, - ConstCastResultIdNullWrapPtr + ConstCastResultIdBadAllowsZero, }; struct ConstCastOnly; @@ -83,6 +83,7 @@ struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; struct ConstCastTypeMismatch; +struct ConstCastBadAllowsZero; struct ConstCastOnly { ConstCastResultId id; @@ -99,6 +100,7 @@ struct ConstCastOnly { ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; + ConstCastBadAllowsZero *bad_allows_zero; } data; }; @@ -141,6 +143,12 @@ struct ConstCastErrSetMismatch { ZigList missing_errors; }; +struct ConstCastBadAllowsZero { + ZigType *wanted_type; + ZigType *actual_type; +}; + + enum UndefAllowed { UndefOk, UndefBad, @@ -159,13 +167,20 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); 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 void buf_read_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, ConstExprValue *val); static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); -static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, +static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val); static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, - ZigType *dest_type, IrInstruction *dest_type_src); + 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 Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); +static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry); +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 ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -177,15 +192,28 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialRef: result = const_val->data.x_ptr.data.ref.pointee; break; - case ConstPtrSpecialBaseArray: - expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); - result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[ - const_val->data.x_ptr.data.base_array.elem_index]; + 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]; break; + } case ConstPtrSpecialBaseStruct: result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; break; + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; + case ConstPtrSpecialNull: + result = const_val; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -197,6 +225,11 @@ 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 types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; @@ -207,15 +240,10 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; - return false; -} + if (is_opt_err_set(a) && is_opt_err_set(b)) + return true; -ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); - if (const_val->type->id == ZigTypeIdPointer) { - assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); - } - return result; + return false; } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -304,6 +332,14 @@ static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { return new_bb; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) { + return IrInstructionIdDeclVarSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) { + return IrInstructionIdDeclVarGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } @@ -336,10 +372,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { - return IrInstructionIdDeclVar; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -448,8 +480,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { - return IrInstructionIdUnwrapOptional; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) { + return IrInstructionIdOptionalUnwrapPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -516,8 +548,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { - return IrInstructionIdCmpxchg; +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) { + return IrInstructionIdCmpxchgSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) { + return IrInstructionIdCmpxchgGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { @@ -564,6 +600,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) { + return IrInstructionIdVectorType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { return IrInstructionIdBoolNot; } @@ -648,8 +688,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { - return IrInstructionIdPtrCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) { + return IrInstructionIdPtrCastSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { @@ -856,10 +900,30 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { return IrInstructionIdSqrt; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBswap *) { + return IrInstructionIdBswap; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionBitReverse *) { + return IrInstructionIdBitReverse; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) { + return IrInstructionIdVectorToArray; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) { + return IrInstructionIdArrayToVector; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) { + return IrInstructionIdAssertZero; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -906,7 +970,7 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &cond_br_instruction->base; } @@ -922,16 +986,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - assert(type_entry); - IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - return &const_instruction->base; -} - 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; @@ -1179,14 +1233,11 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + 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 (async_allocator) - ir_ref_instruction(async_allocator, irb->current_basic_block); - if (new_stack != nullptr) - ir_ref_instruction(new_stack, irb->current_basic_block); + if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } @@ -1271,7 +1322,7 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; - ir_ref_instruction(container_type, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i], irb->current_basic_block); } @@ -1346,10 +1397,10 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { - IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + 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->var = var; @@ -1357,13 +1408,28 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *s decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; - if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); + if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } +static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigVar *var, IrInstruction *init_value) +{ + 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->var = var; + decl_var_instruction->init_value = init_value; + + ir_ref_instruction(init_value, ira->new_irb.current_basic_block); + + return &decl_var_instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -1533,14 +1599,14 @@ static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool safety_check_on) +static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } @@ -1756,13 +1822,12 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, - IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, +static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) + bool is_weak) { - IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionCmpxchgSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; @@ -1770,16 +1835,33 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; instruction->is_weak = is_weak; - instruction->type = type; - instruction->success_order = success_order; - instruction->failure_order = failure_order; - if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order = success_order; + instruction->failure_order = failure_order; + instruction->is_weak = is_weak; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block); + ir_ref_instruction(new_value, ira->new_irb.current_basic_block); return &instruction->base; } @@ -1900,6 +1982,19 @@ static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len, + IrInstruction *elem_type) +{ + IrInstructionVectorType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->len = len; + instruction->elem_type = elem_type; + + ir_ref_instruction(len, irb->current_basic_block); + ir_ref_instruction(elem_type, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -2051,12 +2146,12 @@ static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *s } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value) + IrInstruction *err_union) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->err_union = err_union; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(err_union, irb->current_basic_block); return &instruction->base; } @@ -2106,20 +2201,35 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *dest_type, IrInstruction *ptr) +static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on) { - IrInstructionPtrCast *instruction = ir_build_instruction( + IrInstructionPtrCastSrc *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on) +{ + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ptr_type; + instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -2362,7 +2472,7 @@ static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode * ir_ref_instruction(type_value, irb->current_basic_block); - return &instruction->base; + return &instruction->base; } static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -2705,6 +2815,28 @@ static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *sourc return &instruction->base; } +static IrInstruction *ir_build_bswap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { + IrInstructionBswap *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_bit_reverse(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { + IrInstructionBitReverse *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) { IrInstructionCheckRuntimeScope *instruction = ir_build_instruction(irb, scope, source_node); instruction->scope_is_comptime = scope_is_comptime; @@ -2716,6 +2848,47 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *vector, ZigType *result_type) +{ + IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->vector = vector; + + ir_ref_instruction(vector, ira->new_irb.current_basic_block); + + ir_add_alloca(ira, &instruction->base, result_type); + + return &instruction->base; +} + +static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *array, ZigType *result_type) +{ + IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->array = array; + + ir_ref_instruction(array, ira->new_irb.current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *target) +{ + 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->target = target; + + ir_ref_instruction(target, 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; @@ -2776,10 +2949,13 @@ 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 { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } } } @@ -3034,7 +3210,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; - variable_entry->value = create_const_vals(1); + variable_entry->const_value = create_const_vals(1); if (is_comptime != nullptr) { is_comptime->ref_count += 1; @@ -3049,20 +3225,20 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } @@ -3125,7 +3301,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; @@ -3143,9 +3320,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); - } else if (statement_value->id == IrInstructionIdDeclVar) { + } else if (statement_value->id == IrInstructionIdDeclVarSrc) { // variable declarations start a new scope - IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; + IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void @@ -3300,7 +3477,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; @@ -3334,7 +3511,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3452,7 +3629,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: - return ir_gen_maybe_ok_or(irb, scope, node); + return ir_gen_orelse(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } @@ -3511,6 +3688,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } + assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { @@ -3630,7 +3808,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); - if (!entry) { + if (!entry) { // new built in not found add_node_error(irb->codegen, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return irb->codegen->invalid_instruction; @@ -3873,9 +4051,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: @@ -4138,6 +4315,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_type, lval); } + case BuiltinFnIdVectorType: + { + 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; + + IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, vector_type, lval); + } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4315,7 +4507,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true); return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: @@ -4689,6 +4881,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdBswap: + { + 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; + + IrInstruction *result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdBitReverse: + { + 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; + + IrInstruction *result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } } zig_unreachable(); } @@ -4723,7 +4945,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -4732,7 +4955,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4755,7 +4978,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) - return then_expr_result; + return irb->codegen->invalid_instruction; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4765,7 +4988,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) - return else_expr_result; + return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); } @@ -4810,10 +5033,23 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return ir_build_ref(irb, scope, value->source_node, value, false, false); } +static PtrLen star_token_to_ptr_len(TokenId token_id) { + switch (token_id) { + case TokenIdStar: + case TokenIdStarStar: + return PtrLenSingle; + case TokenIdBracketStarBracket: + return PtrLenUnknown; + case TokenIdBracketStarCBracket: + return PtrLenC; + default: + zig_unreachable(); + } +} + static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); - PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || - node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; + 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; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -4866,7 +5102,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode ptr_len, align_value, bit_offset_start, host_int_bytes); } -static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); @@ -4992,11 +5228,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { - var->value->type = irb->codegen->builtin_types.entry_invalid; + var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } @@ -5012,6 +5248,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod add_node_error(irb->codegen, variable_declaration->section_expr, buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } + if (variable_declaration->threadlocal_tok != nullptr) { + add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok, + buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol))); + } // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. @@ -5023,7 +5263,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); + return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5079,7 +5319,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -5119,7 +5359,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) @@ -5159,10 +5399,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5319,7 +5559,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; @@ -5337,7 +5577,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); @@ -5561,8 +5801,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeTestExpr); +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -5600,9 +5840,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5677,7 +5917,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5702,7 +5942,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; @@ -5757,7 +5997,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } @@ -6167,7 +6407,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); } -static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6181,7 +6421,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, LValNone); } @@ -6215,7 +6455,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } @@ -6251,7 +6491,8 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); - render_const_value(codegen, name, var_scope->var->value); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); return true; } @@ -6501,14 +6742,15 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst, + false); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); // set the is_canceled bit - IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); @@ -6579,14 +6821,15 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst, + false); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); // clear the is_suspended bit - IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr, AtomicRmwOp_and, AtomicOrderSeqCst); @@ -6695,14 +6938,14 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false); - IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); @@ -6745,7 +6988,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); - IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); @@ -6777,7 +7020,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); - IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); @@ -6989,7 +7232,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true); if (lval == LValPtr) return unwrapped_ptr; @@ -7013,8 +7256,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); - case NodeTypeTestExpr: - return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); + case NodeTypeIfOptional: + return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeCompTime: @@ -7032,7 +7275,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7091,6 +7334,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -7111,27 +7355,28 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); - IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, + coro_promise_ptr, false); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); @@ -7147,7 +7392,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr, + false); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); @@ -7225,9 +7471,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, - irb->exec->coro_result_field_ptr); + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, + result_ptr, false); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, + u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst); @@ -7242,7 +7489,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); @@ -7277,7 +7524,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, + coro_mem_ptr_maybe, false); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -7330,24 +7578,51 @@ static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *ms return exec_add_error_node(ira->codegen, ira->new_irb.exec, source_node, msg); } +static ErrorMsg *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) { + if (ira != nullptr) + return exec_add_error_node(codegen, ira->new_irb.exec, source_node, msg); + else + return add_node_error(codegen, source_node, msg); +} + static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) { return ir_add_error_node(ira, source_instruction->source_node, msg); } -static ConstExprValue *ir_const_ptr_pointee(IrAnalyze *ira, ConstExprValue *const_val, AstNode *source_node) { - ConstExprValue *val = const_ptr_pointee_unchecked(ira->codegen, const_val); +// 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) +{ + Error err; + assert(ptr_val->type->id == ZigTypeIdPointer); + ConstExprValue 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); + return ErrorNone; +} + +ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, + AstNode *source_node) +{ + Error err; + ConstExprValue *val = const_ptr_pointee_unchecked(codegen, const_val); assert(val != 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)) { - ir_add_error_node(ira, source_node, - buf_sprintf("TODO handle comptime reinterpreted pointer. See https://github.com/ziglang/zig/issues/955")); - return nullptr; + if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) + return nullptr; + return const_ptr_pointee_unchecked(codegen, const_val); } return val; } -static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ConstExprValue *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); @@ -7357,16 +7632,16 @@ static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } - return value; + return &value->value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } } - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { @@ -8086,6 +8361,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); + assert(const_val_is_int || const_val_is_float); if (other_type->id == ZigTypeIdFloat) { if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { @@ -8376,7 +8652,6 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp return err_set_type; } - static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type, ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable) { @@ -8389,53 +8664,63 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (wanted_type == actual_type) return result; - // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively - // but not if we want a mutable pointer - // and not if the actual pointer has zero bits - if (!wanted_is_mutable && wanted_type->id == ZigTypeIdOptional && - wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && - actual_type->id == ZigTypeIdPointer && type_has_bits(actual_type)) - { - ConstCastOnly child = types_match_const_cast_only(ira, - wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdNullWrapPtr; - result.data.null_wrap_ptr_child = allocate_nonzero(1); - *result.data.null_wrap_ptr_child = child; - } - return result; - } - - // pointer const - if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) { - ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); + // If pointers have the same representation in memory, they can be "const-casted". + // `const` attribute can be gained + // `volatile` attribute can be gained + // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) + // but only if !wanted_is_mutable + // alignment can be decreased + // bit offset attributes must match exactly + // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one + 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); + bool actual_allows_zero = ptr_allows_addr_zero(actual_type); + bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC; + bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC; + bool wanted_opt_or_ptr = wanted_ptr_type != nullptr && + (wanted_type->id == ZigTypeIdPointer || wanted_type->id == ZigTypeIdOptional); + 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) { + 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) return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; result.data.pointer_mismatch = allocate_nonzero(1); result.data.pointer_mismatch->child = child; - result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; - result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; + result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; + result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; return result; } - if ((err = type_resolve(g, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + bool ok_allows_zero = (wanted_allows_zero && + (actual_allows_zero || !wanted_is_mutable)) || + (!wanted_allows_zero && !actual_allows_zero); + if (!ok_allows_zero) { + result.id = ConstCastResultIdBadAllowsZero; + result.data.bad_allows_zero = allocate_nonzero(1); + result.data.bad_allows_zero->wanted_type = wanted_type; + result.data.bad_allows_zero->actual_type = actual_type; + return result; + } + if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } - if ((err = type_resolve(g, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { result.id = ConstCastResultIdInvalid; return result; } - if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset_in_host == wanted_type->data.pointer.bit_offset_in_host && - actual_type->data.pointer.host_int_bytes == wanted_type->data.pointer.host_int_bytes && - get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type)) + 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) && + 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)) { return result; } @@ -8669,7 +8954,9 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t * *errors = reallocate(*errors, old_errors_count, *errors_count); } -static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, IrInstruction **instructions, size_t instruction_count) { +static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, + IrInstruction **instructions, size_t instruction_count) +{ Error err; assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; @@ -8680,13 +8967,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT 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)) { + return ira->codegen->builtin_types.entry_invalid; + } 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; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { @@ -8845,6 +9132,9 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } + if (!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)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; @@ -8852,9 +9142,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); @@ -8986,6 +9273,37 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT continue; } + if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC && + (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt)) + { + continue; + } + + if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC && + (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt)) + { + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) { + if (prev_type->data.pointer.ptr_len == PtrLenC && + types_match_const_cast_only(ira, prev_type->data.pointer.child_type, + cur_type->data.pointer.child_type, source_node, + !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + continue; + } + if (cur_type->data.pointer.ptr_len == PtrLenC && + types_match_const_cast_only(ira, cur_type->data.pointer.child_type, + prev_type->data.pointer.child_type, source_node, + !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; + continue; + } + } + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } @@ -9292,7 +9610,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ const_val->type = new_type; break; case CastOpResizeSlice: - case CastOpBytesToSlice: // can't do it zig_unreachable(); case CastOpIntToFloat: @@ -9345,14 +9662,23 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } return true; } + +static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { + 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; + return new_instruction; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && - cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) + cast_op != CastOpResizeSlice) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, 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)) { @@ -9385,13 +9711,11 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { - ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); + ConstExprValue *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_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + 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; @@ -9421,7 +9745,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { - ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); + ConstExprValue *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) { @@ -9430,8 +9754,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + 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; @@ -9566,15 +9889,6 @@ static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) return instruction; } -static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { - 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; - return new_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; @@ -9625,20 +9939,20 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un if (undef_allowed == UndefOk) { return &value->value; } else { - ir_add_error(ira, value, buf_sprintf("use of undefined value")); + ir_add_error(ira, value, buf_sprintf("use of undefined value here causes undefined behavior")); return nullptr; } } zig_unreachable(); } -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *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) + IrExecutable *parent_exec, AstNode *expected_type_source_node) { if (expected_type != nullptr && type_is_invalid(expected_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; @@ -9651,13 +9965,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); @@ -9671,13 +9985,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; - ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); + 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; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } @@ -9702,6 +10016,34 @@ static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { return const_val->data.x_type; } +static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) { + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + 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))); + 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); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + ZigType *result_type = const_val->data.x_type; + if (result_type->id != ZigTypeIdErrorSet) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_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; + } + return result_type; +} + static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; @@ -9723,7 +10065,9 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { @@ -9739,7 +10083,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (get_codegen_ptr_type(wanted_type) != nullptr) { + 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); } else { const_instruction->base.value.data.x_optional = val; @@ -9770,11 +10114,16 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = wanted_type->data.error_union.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.err = nullptr; + 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; } @@ -9839,11 +10188,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *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.err = val->data.x_err_set; + 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; } @@ -9865,8 +10219,9 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(wanted_type)) { + const_instruction->base.value.data.x_err_set = nullptr; } else { const_instruction->base.value.data.x_optional = nullptr; } @@ -9899,7 +10254,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { + if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); @@ -9925,20 +10280,17 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); - if (instr_is_comptime(array)) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + 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_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); - IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + 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); @@ -9977,8 +10329,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -9988,8 +10339,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); return result; @@ -10012,8 +10362,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + 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); @@ -10024,8 +10373,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + 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; @@ -10042,8 +10390,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } @@ -10075,8 +10422,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + 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); @@ -10131,8 +10477,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); @@ -10142,6 +10487,18 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return result; } + // If the destination integer type has no bits, then we can emit a comptime + // zero. However, we still want to emit a runtime safety check to make sure + // the target is zero. + if (!type_has_bits(wanted_type)) { + assert(wanted_type->id == ZigTypeIdInt); + 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; + 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; @@ -10186,8 +10543,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -10205,8 +10561,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { @@ -10229,8 +10584,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; @@ -10294,12 +10648,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.err; + err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { @@ -10334,15 +10687,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); 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_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + 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); return result; @@ -10380,7 +10729,7 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou return ira->codegen->invalid_instruction; assert(val->type->id == ZigTypeIdPointer); - ConstExprValue *pointee = ir_const_ptr_pointee(ira, val, source_instr->source_node); + ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { @@ -10389,8 +10738,8 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; - array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar; - array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -10496,21 +10845,100 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); break; } + case ConstCastResultIdBadAllowsZero: { + ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type; + ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type; + bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); + bool actual_allows_zero = ptr_allows_addr_zero(actual_type); + if (actual_allows_zero && !wanted_allows_zero) { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("'%s' could have null values which are illegal in type '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->name))); + } else { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + } + break; + } + case ConstCastResultIdFnIsGeneric: + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("only one of the functions is generic")); + break; case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO - case ConstCastResultIdFnIsGeneric: // TODO case ConstCastResultIdFnReturnType: // TODO case ConstCastResultIdFnArgCount: // TODO case ConstCastResultIdFnGenericArgCount: // TODO case ConstCastResultIdFnArgNoAlias: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO - case ConstCastResultIdNullWrapPtr: // TODO break; } } +static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *array, ZigType *vector_type) +{ + if (instr_is_comptime(array)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, vector_type); + copy_const_val(&result->value, &array->value, false); + result->value.type = vector_type; + return result; + } + return ir_build_array_to_vector(ira, source_instr, array, vector_type); +} + +static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *vector, ZigType *array_type) +{ + if (instr_is_comptime(vector)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, array_type); + copy_const_val(&result->value, &vector->value, false); + result->value.type = array_type; + return result; + } + return ir_build_vector_to_array(ira, source_instr, vector, array_type); +} + +static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *integer, ZigType *dest_type) +{ + IrInstruction *unsigned_integer; + if (instr_is_comptime(integer)) { + unsigned_integer = integer; + } else { + assert(integer->value.type->id == ZigTypeIdInt); + + 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(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + 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); + unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); + if (type_is_invalid(unsigned_integer->value.type)) + return ira->codegen->invalid_instruction; + } else { + unsigned_integer = integer; + } + } + + return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type); +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, ZigType *wanted_type, IrInstruction *value) { @@ -10538,12 +10966,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_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } 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_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } @@ -10567,7 +10995,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type); } } } @@ -10620,6 +11048,11 @@ 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) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); + 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); @@ -10673,6 +11106,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // 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); @@ -10685,6 +11119,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // cast from [N]T to ?[]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) @@ -10779,7 +11214,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from error set to error union type + // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { @@ -10875,7 +11310,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes && get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type)) { - return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr); + return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true); } } @@ -10892,6 +11327,40 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // cast from @Vector(N, T) to [N]T + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && + wanted_type->data.array.len == actual_type->data.vector.len && + 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); + } + + // cast from [N]T to @Vector(N, T) + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && + actual_type->data.array.len == wanted_type->data.vector.len && + types_match_const_cast_only(ira, actual_type->data.array.child_type, + wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); + } + + // casting between C pointers and normal pointers + if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer && + (wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true); + } + + // cast from integer to C pointer + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && + (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt)) + { + return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); + } // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { @@ -10931,8 +11400,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ZigType *child_type = type_entry->data.pointer.child_type; // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } @@ -10946,10 +11414,9 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); - if ((err = ir_read_const_ptr(ira, source_instruction->source_node, &result->value, + if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value, &ptr->value))) { return ira->codegen->invalid_instruction; @@ -10959,7 +11426,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + // 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; + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; @@ -11184,10 +11655,13 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio return ir_unreach_error(ira); IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); - if (type_is_invalid(casted_value->value.type) && ira->explicit_return_type_source_node != nullptr) { - ErrorMsg *msg = ira->codegen->errors.last(); - add_error_note(ira->codegen, msg, ira->explicit_return_type_source_node, - buf_sprintf("return type declared here")); + if (type_is_invalid(casted_value->value.type)) { + AstNode *source_node = ira->explicit_return_type_source_node; + if (source_node != nullptr) { + ErrorMsg *msg = ira->codegen->errors.last(); + add_error_note(ira->codegen, msg, source_node, + buf_sprintf("return type declared here")); + } return ir_unreach_error(ira); } @@ -11206,8 +11680,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - // TODO determine if we need to use copy_const_val here - result->value = instruction->base.value; + copy_const_val(&result->value, &instruction->base.value, true); return result; } @@ -11260,28 +11733,36 @@ static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp } static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { - if (op_id == IrBinOpCmpEq) { - return cmp == CmpEQ; - } else if (op_id == IrBinOpCmpNotEq) { - return cmp != CmpEQ; - } else if (op_id == IrBinOpCmpLessThan) { - return cmp == CmpLT; - } else if (op_id == IrBinOpCmpGreaterThan) { - return cmp == CmpGT; - } else if (op_id == IrBinOpCmpLessOrEq) { - return cmp != CmpGT; - } else if (op_id == IrBinOpCmpGreaterOrEq) { - return cmp != CmpLT; - } else { - zig_unreachable(); + switch (op_id) { + case IrBinOpCmpEq: + return cmp == CmpEQ; + case IrBinOpCmpNotEq: + return cmp != CmpEQ; + case IrBinOpCmpLessThan: + return cmp == CmpLT; + case IrBinOpCmpGreaterThan: + return cmp == CmpGT; + case IrBinOpCmpLessOrEq: + return cmp != CmpGT; + case IrBinOpCmpGreaterOrEq: + return cmp != CmpLT; + default: + zig_unreachable(); } } static bool optional_value_is_null(ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); if (get_codegen_ptr_type(val->type) != nullptr) { - return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - val->data.x_ptr.data.hard_coded_addr.addr == 0; + if (val->data.x_ptr.special == ConstPtrSpecialNull) { + return true; + } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + return val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else { + return false; + } + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; } else { return val->data.x_optional == nullptr; } @@ -11436,13 +11917,13 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdComptimeInt: case ZigTypeIdInt: case ZigTypeIdFloat: + case ZigTypeIdVector: operator_allowed = true; break; case ZigTypeIdBool: case ZigTypeIdMetaType: case ZigTypeIdVoid: - case ZigTypeIdPointer: case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdOpaque: @@ -11454,6 +11935,10 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * operator_allowed = is_equality_cmp; break; + case ZigTypeIdPointer: + operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC); + break; + case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: @@ -11481,19 +11966,18 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - bool requires_comptime; - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeYes: - requires_comptime = true; - break; - case ReqCompTimeNo: - requires_comptime = false; - break; - case ReqCompTimeInvalid: + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + one_possible_value = true; + break; + case OnePossibleValueNo: + one_possible_value = false; + break; } - bool one_possible_value = !requires_comptime && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) @@ -11502,15 +11986,38 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (op2_val == nullptr) return ira->codegen->invalid_instruction; - bool answer; if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); - answer = resolve_cmp_op_id(op_id, cmp_result); + 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); - answer = resolve_cmp_op_id(op_id, cmp_result); + 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 ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op1_val->data.x_ptr.special == ConstPtrSpecialNull) && + (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op2_val->data.x_ptr.special == ConstPtrSpecialNull)) + { + uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? + 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; + uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? + 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; + Cmp cmp_result; + if (op1_addr > op2_addr) { + cmp_result = CmpGT; + } else if (op1_addr < op2_addr) { + cmp_result = CmpLT; + } else { + cmp_result = CmpEQ; + } + bool answer = resolve_cmp_op_id(op_id, cmp_result); + return ir_const_bool(ira, &bin_op_instruction->base, answer); + } } else { bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); + bool answer; if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { @@ -11518,9 +12025,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * } else { zig_unreachable(); } + return ir_const_bool(ira, &bin_op_instruction->base, answer); } - - return ir_const_bool(ira, &bin_op_instruction->base, answer); } // some comparisons with unsigned numbers can be evaluated @@ -11567,8 +12073,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * return result; } -static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, - IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) +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) { bool is_int; bool is_float; @@ -11590,10 +12096,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { - return ErrorDivByZero; + return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); } if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { - return ErrorNegativeDenominator; + return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); } switch (op_id) { @@ -11639,7 +12145,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt orig_bigint; bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { - return ErrorShiftedOutOneBits; + return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); } break; } @@ -11707,14 +12213,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt remainder; bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } else { float_div_trunc(out_val, op1_val, op2_val); ConstExprValue remainder; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } break; @@ -11738,13 +12244,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed)) { - return ErrorOverflow; + return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; - return 0; + return nullptr; +} + +// 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) +{ + IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry); + ConstExprValue *out_val = &result_instruction->value; + if (type_entry->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, op1_val); + expand_undef_array(ira->codegen, op2_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + 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]; + assert(scalar_op1_val->type == scalar_type); + assert(scalar_op2_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, + 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 = type_entry; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { + return ira->codegen->invalid_instruction; + } + } + return ir_implicit_cast(ira, result_instruction, type_entry); } static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { @@ -11816,24 +12360,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type); - - int err; - if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorOverflow) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorShiftedOutOneBits) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); - return result_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) { 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")); @@ -11852,7 +12379,71 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b return result; } +static bool ok_float_op(IrBinOp op) { + switch (op) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpAdd: + case IrBinOpSub: + case IrBinOpMult: + case IrBinOpDivUnspecified: + case IrBinOpDivTrunc: + case IrBinOpDivFloor: + case IrBinOpDivExact: + case IrBinOpRemRem: + case IrBinOpRemMod: + return true; + + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + case IrBinOpAddWrap: + case IrBinOpSubWrap: + case IrBinOpMultWrap: + 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: + break; + default: + return false; + } + switch (lhs_type->data.pointer.ptr_len) { + case PtrLenSingle: + return false; + case PtrLenUnknown: + case PtrLenC: + break; + } + return true; +} + 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)) return ira->codegen->invalid_instruction; @@ -11864,13 +12455,44 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = instruction->op_id; // look for pointer math - if (op1->value.type->id == ZigTypeIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown && - (op_id == IrBinOpAdd || op_id == IrBinOpSub)) - { + 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 (casted_op2 == ira->codegen->invalid_instruction) + if (type_is_invalid(casted_op2->value.type)) return ira->codegen->invalid_instruction; + if (op1->value.special == ConstValSpecialUndef || casted_op2->value.special == ConstValSpecialUndef) { + IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type); + result->value.special = ConstValSpecialUndef; + return result; + } + if (casted_op2->value.special == ConstValSpecialStatic && op1->value.special == ConstValSpecialStatic && + (op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op1->value.data.x_ptr.special == ConstPtrSpecialNull)) + { + uint64_t start_addr = (op1->value.data.x_ptr.special == ConstPtrSpecialNull) ? + 0 : op1->value.data.x_ptr.data.hard_coded_addr.addr; + uint64_t elem_offset; + if (!ir_resolve_usize(ira, casted_op2, &elem_offset)) + return ira->codegen->invalid_instruction; + ZigType *elem_type = op1->value.type->data.pointer.child_type; + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset; + uint64_t new_addr; + if (op_id == IrBinOpAdd) { + new_addr = start_addr + byte_offset; + } else if (op_id == IrBinOpSub) { + new_addr = start_addr - byte_offset; + } else { + zig_unreachable(); + } + IrInstruction *result = ir_const(ira, &instruction->base, op1->value.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; @@ -11989,21 +12611,20 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp op_id = IrBinOpRemRem; } + bool ok = false; if (is_int) { - // int - } else if (is_float && - (op_id == IrBinOpAdd || - op_id == IrBinOpSub || - op_id == IrBinOpMult || - op_id == IrBinOpDivUnspecified || - op_id == IrBinOpDivTrunc || - op_id == IrBinOpDivFloor || - op_id == IrBinOpDivExact || - op_id == IrBinOpRemRem || - op_id == IrBinOpRemMod)) - { - // float - } else { + ok = true; + } else if (is_float && ok_float_op(op_id)) { + ok = true; + } else if (resolved_type->id == ZigTypeIdVector) { + ZigType *elem_type = resolved_type->data.vector.elem_type; + if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) { + ok = true; + } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) { + ok = true; + } + } + if (!ok) { AstNode *source_node = instruction->base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", @@ -12038,30 +12659,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type); - - int err; - if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorDivByZero) { - ir_add_error(ira, &instruction->base, buf_sprintf("division by zero")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorOverflow) { - ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorExactDivRemainder) { - ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorNegativeDenominator) { - ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false); - return result_instruction; + return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val); } IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope, @@ -12115,7 +12713,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i 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]; - op1_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op1_array_end = op1_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); @@ -12125,13 +12723,9 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; + bool op2_type_valid; if (op2_type->id == ZigTypeIdArray) { - if (op2_type->data.array.child_type != child_type) { - 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))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = op2_type->data.array.child_type == child_type; op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; @@ -12140,35 +12734,30 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray && op2_val->data.x_ptr.data.base_array.is_cstr) { - if (child_type != ira->codegen->builtin_types.entry_u8) { - 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))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8; 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; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; - if (ptr_type->data.pointer.child_type != child_type) { - 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))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = ptr_type->data.pointer.child_type == child_type; ConstExprValue *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; - op2_array_end = op2_array_val->type->data.array.len; ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index]; - op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op2_array_end = op2_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, 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))); + return ira->codegen->invalid_instruction; + } // The type of result is populated in the following if blocks IrInstruction *result = ir_const(ira, &instruction->base, nullptr); @@ -12301,26 +12890,14 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { - ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child); + 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; - if (op1_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op1, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op1_type->name))); - return ira->codegen->invalid_instruction; - } - - ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child); + ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child); if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; - if (op2_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op2, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op2_type->name))); - return ira->codegen->invalid_instruction; - } - if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { @@ -12394,13 +12971,15 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { +static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, + IrInstructionDeclVarSrc *decl_var_instruction) +{ Error err; ZigVar *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->child; if (type_is_invalid(init_value->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12411,7 +12990,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } } @@ -12436,7 +13015,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct case ReqCompTimeInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; - case ReqCompTimeYes: { + case ReqCompTimeYes: var_class_requires_const = true; if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, @@ -12445,10 +13024,10 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct result_type = ira->codegen->builtin_types.entry_invalid; } break; - } case ReqCompTimeNo: if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && + casted_init_value->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr && casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) { var_class_requires_const = true; @@ -12463,7 +13042,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct break; } - if (var->value->type != nullptr && !is_comptime_var) { + if (var->var_type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can @@ -12485,8 +13064,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct // This must be done after possibly creating a new variable above var->ref_count = 0; - var->value->type = result_type; - assert(var->value->type); + var->var_type = result_type; + assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -12494,13 +13073,13 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base); } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; } } @@ -12517,7 +13096,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12525,11 +13104,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, - var, var_type, nullptr, casted_init_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value); } static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { @@ -12651,6 +13226,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdBool: + case ZigTypeIdVector: break; case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -12685,6 +13261,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: + case ZigTypeIdVector: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case ZigTypeIdNamespace: case ZigTypeIdBoundFn: @@ -12859,7 +13436,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr); + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; @@ -12917,7 +13494,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr); + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; @@ -12971,9 +13548,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -12982,14 +13557,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - if (var->value->type == nullptr || type_is_invalid(var->value->type)) + if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; - if (var->value->special == ConstValSpecialStatic) { - mem_slot = var->value; + if (var->const_value->special == ConstValSpecialStatic) { + mem_slot = var->const_value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context @@ -13018,7 +13593,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, ptr_mut, is_const, is_volatile, var->align_bytes); } } @@ -13029,7 +13604,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -13038,6 +13613,96 @@ no_mem_slot: return var_ptr_instruction; } +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *uncasted_value) +{ + if (ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + 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; + + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (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) { + if (instr_is_comptime(value)) { + ConstExprValue *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 (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + break; + case OnePossibleValueYes: + break; + } + } + return ir_const_void(ira, source_instr); + } + } + 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); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_instruction; + } + } + + 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)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); + case ReqCompTimeNo: + break; + } + + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, + ptr, value); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13182,7 +13847,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - IrInstruction *result = nullptr; + ConstExprValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -13194,22 +13859,23 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call 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); + nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node); if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; - if (result->value.type->id == ZigTypeIdErrorUnion) { - if (result->value.data.x_err_union.err != nullptr) { + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); - inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + inferred_err_set_type->data.error_set.errors[0] = err; } - ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->value.type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } @@ -13217,13 +13883,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->type)) return ira->codegen->invalid_instruction; } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); - // TODO should we use copy_const_val? - new_instruction->value = result->value; + 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; return ir_finish_anal(ira, new_instruction); } @@ -13382,18 +14047,21 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call 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); + 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) { - IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ConstExprValue *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, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr); + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = *align_result; uint32_t align_bytes = 0; - ir_resolve_align(ira, align_result, &align_bytes); + ir_resolve_align(ira, &const_instruction->base, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } @@ -13471,12 +14139,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->fn_defs.append(impl_fn); } - ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } - size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; + size_t impl_param_count = impl_fn_type_id->param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); @@ -13489,9 +14157,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, call_instruction->is_async, nullptr, casted_new_stack); - new_call_instruction->value.type = return_type; + new_call_instruction->value.type = impl_fn_type_id->return_type; - ir_add_alloca(ira, new_call_instruction, return_type); + ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -13653,39 +14321,87 @@ 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, AstNode *source_node, +static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val) { Error err; assert(out_val->type != nullptr); - ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr_val); + ConstExprValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); - if ((err = type_resolve(ira->codegen, pointee->type, ResolveStatusSizeKnown))) + if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - if ((err = type_resolve(ira->codegen, out_val->type, ResolveStatusSizeKnown))) + if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - size_t src_size = type_size(ira->codegen, pointee->type); - size_t dst_size = type_size(ira->codegen, out_val->type); + // We don't need to read the padding bytes, so we look at type_size_store bytes + size_t src_size = type_size_store(codegen, pointee->type); + size_t dst_size = type_size_store(codegen, out_val->type); - 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 == ConstPtrMutComptimeConst); + if (dst_size <= src_size) { + if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { + copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); + return ErrorNone; + } + Buf buf = BUF_INIT; + buf_resize(&buf, src_size); + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; return ErrorNone; } - if (dst_size > src_size) { - ir_add_error_node(ira, source_node, - buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", - dst_size, buf_ptr(&pointee->type->name), src_size)); - return ErrorSemanticAnalyzeFail; + switch (ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; + case ConstPtrSpecialRef: { + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", + dst_size, buf_ptr(&pointee->type->name), src_size)); + return ErrorSemanticAnalyzeFail; + } + case ConstPtrSpecialBaseArray: { + ConstExprValue *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"); + size_t elem_size = src_size; + size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; + src_size = elem_size * (array_val->type->data.array.len - elem_index); + if (dst_size > src_size) { + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from %s at index %" ZIG_PRI_usize " which is %zu bytes", + dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); + return ErrorSemanticAnalyzeFail; + } + size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); + 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]; + 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))) + return err; + return ErrorNone; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + case ConstPtrSpecialFunction: + zig_panic("TODO"); } - - Buf buf = BUF_INIT; - buf_resize(&buf, src_size); - buf_write_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), pointee); - buf_read_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), out_val); - return ErrorNone; + zig_unreachable(); } static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { @@ -13704,6 +14420,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: @@ -13875,9 +14592,14 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); - if (is_comptime || instr_is_comptime(condition)) { + 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)) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; - if (!ir_resolve_bool(ira, condition, &cond_is_true)) + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? @@ -13896,11 +14618,6 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi return ir_finish_anal(ira, result); } - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (casted_condition == ira->codegen->invalid_instruction) - return ir_unreach_error(ira); - assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base); if (new_then_block == nullptr) @@ -13939,8 +14656,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (value->value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - // TODO use copy_const_val? - result->value = value->value; + copy_const_val(&result->value, &value->value, true); return result; } else { return value; @@ -13989,14 +14705,24 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (type_is_invalid(resolved_type)) return ira->codegen->invalid_instruction; - if (resolved_type->id == ZigTypeIdComptimeFloat || - resolved_type->id == ZigTypeIdComptimeInt || - resolved_type->id == ZigTypeIdNull || - resolved_type->id == ZigTypeIdUndefined) - { - ir_add_error_node(ira, phi_instruction->base.source_node, - buf_sprintf("unable to infer expression type")); + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const(ira, &phi_instruction->base, resolved_type); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + ir_add_error_node(ira, phi_instruction->base.source_node, + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); + return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); @@ -14112,7 +14838,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct array_type = array_type->data.pointer.child_type; ptr_type = ptr_type->data.pointer.child_type; if (orig_array_ptr_val->special != ConstValSpecialRuntime) { - orig_array_ptr_val = ir_const_ptr_pointee(ira, orig_array_ptr_val, + orig_array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, elem_ptr_instruction->base.source_node); if (orig_array_ptr_val == nullptr) return ira->codegen->invalid_instruction; @@ -14156,7 +14882,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (!ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *args_val = ir_const_ptr_pointee(ira, ptr_val, elem_ptr_instruction->base.source_node); + ConstExprValue *args_val = const_ptr_pointee(ira, ira->codegen, ptr_val, elem_ptr_instruction->base.source_node); if (args_val == nullptr) return ira->codegen->invalid_instruction; size_t start = args_val->data.x_arg_tuple.start_index; @@ -14238,7 +14964,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) { - ConstExprValue *array_ptr_val = ir_const_ptr_pointee(ira, orig_array_ptr_val, + ConstExprValue *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; @@ -14286,10 +15012,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -14339,10 +15073,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray) { @@ -14457,13 +15199,12 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *struct_val = ir_const_ptr_pointee(ira, ptr_val, source_instr->source_node); + ConstExprValue *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; - ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type); @@ -14500,7 +15241,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ConstExprValue *union_val = ir_const_ptr_pointee(ira, ptr_val, source_instr->source_node); + ConstExprValue *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)) @@ -14697,7 +15438,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); + ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->invalid_instruction; @@ -14723,7 +15464,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; assert(container_ptr->value.type->id == ZigTypeIdPointer); - ConstExprValue *child_val = ir_const_ptr_pointee(ira, container_ptr_val, source_node); + ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->invalid_instruction; ZigType *child_type = child_val->data.x_type; @@ -14998,7 +15739,7 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (!container_ptr_val) return ira->codegen->invalid_instruction; - ConstExprValue *namespace_val = ir_const_ptr_pointee(ira, container_ptr_val, + ConstExprValue *namespace_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, field_ptr_instruction->base.source_node); if (namespace_val == nullptr) return ira->codegen->invalid_instruction; @@ -15030,74 +15771,23 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &load_ptr_instruction->base, ptr); -} - -static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->child; +static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *value = store_ptr_instruction->value->child; + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} + +static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - } - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, &store_ptr_instruction->base); - } - - if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = ir_const_ptr_pointee(ira, &ptr->value, store_ptr_instruction->base.source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_instruction; - if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; - } - return ir_const_void(ira, &store_ptr_instruction->base); - } - } - ir_add_error(ira, &store_ptr_instruction->base, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_instruction; - } - } - - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, - store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, - ptr, casted_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_get_deref(ira, &instruction->base, ptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -15105,42 +15795,14 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio ZigType *type_entry = expr_value->value.type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - switch (type_entry->id) { - case ZigTypeIdInvalid: - zig_unreachable(); // handled above - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdNamespace: - case ZigTypeIdBoundFn: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - case ZigTypeIdPromise: - return ir_const_type(ira, &typeof_instruction->base, type_entry); - } - - zig_unreachable(); + return ir_const_type(ira, &typeof_instruction->base, type_entry); } static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, IrInstructionToPtrType *to_ptr_type_instruction) { + Error err; + IrInstruction *value = to_ptr_type_instruction->value->child; ZigType *type_entry = value->value.type; if (type_is_invalid(type_entry)) @@ -15156,7 +15818,17 @@ static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const); } else if (is_slice(type_entry)) { - ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle); + ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry; + ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle); + // If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type. + if (slice_ptr_type->data.pointer.explicit_alignment != 0) { + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type); + uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment); + ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align); + } } else if (type_entry->id == ZigTypeIdArgTuple) { ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad); if (!arg_tuple_val) @@ -15374,6 +16046,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; @@ -15494,6 +16167,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->invalid_instruction; @@ -15560,6 +16234,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes); @@ -15568,11 +16243,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { @@ -15581,60 +16252,66 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns if (!maybe_val) return ira->codegen->invalid_instruction; - return ir_const_bool(ira, &instruction->base, !optional_value_is_null(maybe_val)); + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); + source_inst->scope, source_inst->source_node, value); result->value.type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, &instruction->base, false); + return ir_const_bool(ira, source_inst, false); } else { - return ir_const_bool(ira, &instruction->base, true); + return ir_const_bool(ira, source_inst, true); } } -static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapOptional *unwrap_maybe_instruction) -{ - IrInstruction *value = unwrap_maybe_instruction->value->child; +static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + return ir_analyze_test_non_null(ira, &instruction->base, value); +} + +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) +{ + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id != ZigTypeIdOptional) { - ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + 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); - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *maybe_val = ir_const_ptr_pointee(ira, val, unwrap_maybe_instruction->base.source_node); + ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { - ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &unwrap_maybe_instruction->base, result_type); + IrInstruction *result = ir_const(ira, source_instr, result_type); ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; - if (type_is_codegen_pointer(child_type)) { + if (types_have_same_zig_comptime_repr(type_entry, child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; @@ -15643,13 +16320,22 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, - unwrap_maybe_instruction->base.scope, unwrap_maybe_instruction->base.source_node, - value, unwrap_maybe_instruction->safety_check_on); + IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } +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)) + return ira->codegen->invalid_instruction; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -15918,10 +16604,10 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, 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 = ir_const_ptr_pointee(ira, &target_value_ptr->value, target_value_ptr->source_node); + 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; - + if (pointee_val->special == ConstValSpecialRuntime) pointee_val = nullptr; } @@ -15950,9 +16636,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); result->value.type = target_type; return result; } @@ -15982,8 +16666,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16007,8 +16690,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); enum_value->value.type = target_type; return enum_value; } @@ -16022,6 +16704,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdVector: ir_add_error(ira, &switch_target_instruction->base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; @@ -16053,7 +16736,7 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru if (!target_value_ptr) return ira->codegen->invalid_instruction; - ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, target_val_ptr, instruction->base.source_node); + ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.source_node); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; @@ -16165,7 +16848,7 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI Error err; assert(container_type->id == ZigTypeIdUnion); - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (instr_field_count != 1) { @@ -16197,7 +16880,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || !type_has_bits(casted_field_value->value.type)) { @@ -16209,12 +16893,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI ConstExprValue *out_val = &result->value; out_val->data.x_union.payload = field_val; out_val->data.x_union.tag = type_field->enum_field->value; - - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdUnion; - parent->data.p_union.union_val = out_val; - } + out_val->parent.id = ConstParentIdUnion; + out_val->parent.data.p_union.union_val = out_val; return result; } @@ -16241,7 +16921,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -16252,7 +16932,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstructionStructInitField *new_fields = allocate(actual_field_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; @@ -16320,9 +17001,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (const_val.special == ConstValSpecialStatic) { IrInstruction *result = ir_const(ira, instruction, nullptr); ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = container_type; + copy_const_val(out_val, &const_val, true); + out_val->type = container_type; for (size_t i = 0; i < instr_field_count; i += 1) { ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; @@ -16354,127 +17034,119 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - if (type_is_invalid(container_type_value->value.type)) + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; size_t elem_count = instruction->item_count; - if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr); - } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { - // array is same as slice init but we make a compile error if the length is wrong - ZigType *child_type; - if (container_type->id == ZigTypeIdArray) { - 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); + if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, + 0, nullptr); + } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { + // array is same as slice init but we make a compile error if the length is wrong + ZigType *child_type; + if (container_type->id == ZigTypeIdArray) { + 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; - } - } else { - ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; - assert(pointer_type->id == ZigTypeIdPointer); - child_type = pointer_type->data.pointer.child_type; + 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 { + ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; + assert(pointer_type->id == ZigTypeIdPointer); + child_type = pointer_type->data.pointer.child_type; + } - ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); + ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = fixed_size_array_type; - const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); + ConstExprValue const_val = {}; + const_val.special = ConstValSpecialStatic; + const_val.type = fixed_size_array_type; + const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); - IrInstruction **new_items = allocate(elem_count); + IrInstruction **new_items = allocate(elem_count); - IrInstruction *first_non_const_instruction = nullptr; + IrInstruction *first_non_const_instruction = nullptr; - for (size_t i = 0; i < elem_count; i += 1) { - IrInstruction *arg_value = instruction->items[i]->child; - if (type_is_invalid(arg_value->value.type)) - return ira->codegen->invalid_instruction; + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *arg_value = instruction->items[i]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); + if (casted_arg == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; - new_items[i] = casted_arg; - - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { - ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!elem_val) - return ira->codegen->invalid_instruction; - - copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); - } else { - first_non_const_instruction = casted_arg; - const_val.special = ConstValSpecialRuntime; - } - } - } + new_items[i] = casted_arg; if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = fixed_size_array_type; - for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = out_val; - parent->data.p_array.elem_index = i; - } + if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { + ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); + if (!elem_val) + return ira->codegen->invalid_instruction; + + copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); + } else { + first_non_const_instruction = casted_arg; + const_val.special = ConstValSpecialRuntime; } - 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; + if (const_val.special == ConstValSpecialStatic) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + ConstExprValue *out_val = &result->value; + copy_const_val(out_val, &const_val, true); + result->value.type = fixed_size_array_type; + for (size_t i = 0; i < elem_count; i += 1) { + ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; + ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); + if (parent != nullptr) { + parent->id = ConstParentIdArray; + parent->data.p_array.array_val = out_val; + parent->data.p_array.elem_index = i; + } } + return result; + } - IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - container_type_value, elem_count, new_items); - new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); - return new_instruction; - } else if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); - } else { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); + 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; } + + IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, + nullptr, elem_count, new_items); + new_instruction->value.type = fixed_size_array_type; + ir_add_alloca(ira, new_instruction, fixed_size_array_type); + return new_instruction; + } else if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("void expression expects no arguments")); + return ira->codegen->invalid_instruction; + } + return ir_const_void(ira, &instruction->base); } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); + 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; } } -static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { +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)) @@ -16534,7 +17206,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value.type)) return ira->codegen->invalid_instruction; @@ -16685,7 +17357,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, static TypeStructField *validate_byte_offset(IrAnalyze *ira, IrInstruction *type_value, IrInstruction *field_name_value, - size_t *byte_offset) + size_t *byte_offset) { ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) @@ -16768,16 +17440,12 @@ static void ensure_field_index(ZigType *type, const char *field_name, size_t ind static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { Error err; - static ConstExprValue *type_info_var = nullptr; // TODO oops this global variable made it past code review - static ZigType *type_info_type = nullptr; // TODO oops this global variable made it past code review - if (type_info_var == nullptr) { - type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_var->type->id == ZigTypeIdMetaType); + ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_var->type->id == ZigTypeIdMetaType); + assertNoError(ensure_complete_type(ira->codegen, type_info_var->data.x_type)); - assertNoError(ensure_complete_type(ira->codegen, type_info_var->data.x_type)); - type_info_type = type_info_var->data.x_type; - assert(type_info_type->id == ZigTypeIdUnion); - } + ZigType *type_info_type = type_info_var->data.x_type; + assert(type_info_type->id == ZigTypeIdUnion); if (type_name == nullptr && root == nullptr) return type_info_type; @@ -16799,10 +17467,11 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig ZigVar *var = tld->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ira->codegen->builtin_types.entry_invalid; - assert(var->value->type->id == ZigTypeIdMetaType); - return var->value->data.x_type; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + return var->const_value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { @@ -16857,13 +17526,12 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; - definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); // Loop through the definitions and generate info. decl_it = decls_scope->decl_table.entry_iterator(); - curr_entry = nullptr; + curr_entry = nullptr; int definition_index = 0; while ((curr_entry = decl_it.next()) != nullptr) { // Skip comptime blocks and test functions. @@ -16888,33 +17556,30 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; - inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; - inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; - inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + inner_fields[2].parent.id = ConstParentIdStruct; + inner_fields[2].parent.data.p_struct.struct_val = definition_val; + inner_fields[2].parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ErrorSemanticAnalyzeFail; - if (var->value->type->id == ZigTypeIdMetaType) - { + if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->value; - } - else - { + 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); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->value->type; + payload->data.x_type = var->const_value->type; inner_fields[2].data.x_union.payload = payload; } @@ -16934,8 +17599,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; - fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; - fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + fn_def_val->parent.id = ConstParentIdUnion; + fn_def_val->parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; @@ -16990,12 +17655,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; - if (fn_entry->src_implicit_return_type != nullptr) - fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; - else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) - fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type; - else - fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; // arg_names: [][] const u8 ensure_field_index(fn_def_val->type, "arg_names", 8); size_t fn_arg_count = fn_entry->variable_list.length; @@ -17004,20 +17664,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], 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++) - { + 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, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); - fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + 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_def_val; @@ -17052,6 +17710,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco return ErrorNone; } +static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) { + switch (ptr_len) { + case PtrLenSingle: + return 0; + case PtrLenUnknown: + return 1; + case PtrLenC: + return 3; + } + zig_unreachable(); +} + static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) { Error err; ZigType *attrs_type; @@ -17061,7 +17731,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty size_enum_index = 2; } else if (ptr_type_entry->id == ZigTypeIdPointer) { attrs_type = ptr_type_entry; - size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1; + size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len); } else { zig_unreachable(); } @@ -17129,21 +17799,16 @@ static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, enum_field_val->data.x_struct.fields = inner_fields; } -static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstExprValue **out) { +static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigType *type_entry, ConstExprValue **out) { Error err; assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); - if ((err = ensure_complete_type(ira->codegen, type_entry))) + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) return err; - if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { - zig_panic("TODO implement @typeInfo for global error set"); - } - ConstExprValue *result = nullptr; - switch (type_entry->id) - { + switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdMetaType: @@ -17238,6 +17903,27 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE break; } + case ZigTypeIdVector: { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Vector", nullptr); + + ConstExprValue *fields = create_const_vals(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_u32; + 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; + + break; + } case ZigTypeIdOptional: { result = create_const_vals(1); @@ -17310,7 +17996,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; 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); @@ -17320,9 +18005,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->data.x_struct.parent.id = ConstParentIdArray; - enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; - enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17344,12 +18029,20 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE ensure_field_index(result->type, "errors", 0); ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); + if (!resolve_inferred_error_set(ira->codegen, type_entry, source_node)) { + return ErrorSemanticAnalyzeFail; + } + if (type_is_global_error_set(type_entry)) { + ir_add_error_node(ira, source_node, + buf_sprintf("TODO: compiler bug: implement @typeInfo support for anyerror. https://github.com/ziglang/zig/issues/1936")); + return ErrorSemanticAnalyzeFail; + } + uint32_t error_count = type_entry->data.error_set.err_count; ConstExprValue *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->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); @@ -17373,9 +18066,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; - error_val->data.x_struct.parent.id = ConstParentIdArray; - error_val->data.x_struct.parent.data.p_array.array_val = error_array; - error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; } break; @@ -17444,7 +18137,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; 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); @@ -17477,9 +18169,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_struct.parent.id = ConstParentIdArray; - union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; - union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17519,7 +18211,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; 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); @@ -17553,9 +18244,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_struct.parent.id = ConstParentIdArray; - struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; - struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); @@ -17625,7 +18316,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE 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->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); @@ -17662,9 +18352,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE } fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; @@ -17673,7 +18363,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE { ZigType *fn_type = type_entry->data.bound_fn.fn_type; assert(fn_type->id == ZigTypeIdFn); - if ((err = ir_make_type_info_value(ira, fn_type, &result))) + if ((err = ir_make_type_info_value(ira, source_node, fn_type, &result))) return err; break; @@ -17698,7 +18388,7 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); ConstExprValue *payload; - if ((err = ir_make_type_info_value(ira, type_entry, &payload))) + if ((err = ir_make_type_info_value(ira, instruction->base.source_node, type_entry, &payload))) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, &instruction->base, result_type); @@ -17708,8 +18398,8 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; } return result; @@ -17735,12 +18425,6 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, IrInstructionSetEvalBranchQuota *instruction) { - if (ira->new_irb.exec->parent_exec != nullptr && !ira->new_irb.exec->is_generic_instantiation) { - ir_add_error(ira, &instruction->base, - buf_sprintf("@setEvalBranchQuota must be called from the top of the comptime stack")); - return ira->codegen->invalid_instruction; - } - uint64_t new_quota; if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota)) return ira->codegen->invalid_instruction; @@ -17781,10 +18465,10 @@ 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; - IrInstruction *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ConstExprValue *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); - if (type_is_invalid(cimport_result->value.type)) + &cimport_scope->buf, block_node, nullptr, nullptr, nullptr); + if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; find_libc_include_path(ira->codegen); @@ -17934,7 +18618,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru return result; } -static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { +static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; @@ -18006,9 +18690,9 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi zig_panic("TODO compile-time execution of cmpxchg"); } - IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order); + IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base, + casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, instruction->is_weak); result->value.type = get_optional_type(ira->codegen, operand_type); ir_add_alloca(ira, result, result->value.type); return result; @@ -18054,6 +18738,27 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } + if (dest_type->id == ZigTypeIdComptimeInt) { + return ir_implicit_cast(ira, target, dest_type); + } + + if (instr_is_comptime(target)) { + ConstExprValue *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, + 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); + return result; + } + if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); @@ -18064,13 +18769,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } - if (target->value.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint, - dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); - return result; - } - 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; @@ -18174,18 +18872,6 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); } -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { - Error err; - - if (ty->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } - - *result_align = get_ptr_align(ira->codegen, ty); - return ErrorNone; -} - static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; @@ -18304,6 +18990,20 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); } +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + ZigType *ptr_type = get_src_ptr_type(ty); + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) @@ -18410,6 +19110,27 @@ static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruct return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count)); } +static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) { + uint64_t len; + if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) + return ira->codegen->invalid_instruction; + + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + if (!is_valid_vector_elem_type(elem_type)) { + ir_add_error(ira, instruction->elem_type, + buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid", + buf_ptr(&elem_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); + + return ir_const_type(ira, &instruction->base, vector_type); +} + static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) @@ -18508,10 +19229,18 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); @@ -18623,10 +19352,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { @@ -18659,10 +19396,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { @@ -18690,9 +19435,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_type->data.pointer.child_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)) @@ -18721,10 +19466,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction 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, - ptr_type->data.pointer.is_const || is_comptime_const, - ptr_type->data.pointer.is_volatile, + ptr_ptr_type->data.pointer.is_const || is_comptime_const, + ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -18771,18 +19516,18 @@ 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 = ir_const_ptr_pointee(ira, &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 = ir_const_ptr_pointee(ira, parent_ptr, instruction->base.source_node); + 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 = ir_const_ptr_pointee(ira, &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; @@ -18791,7 +19536,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 = ir_const_ptr_pointee(ira, &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; @@ -18822,6 +19567,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18829,9 +19580,11 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { - ConstExprValue *slice_ptr = ir_const_ptr_pointee(ira, &ptr_ptr->value, instruction->base.source_node); + ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); if (slice_ptr == nullptr) return ira->codegen->invalid_instruction; @@ -18859,6 +19612,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18866,6 +19625,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); } } else { zig_unreachable(); @@ -18931,6 +19692,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -18939,6 +19706,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; @@ -19165,6 +19934,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: + case ZigTypeIdVector: { uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); return ir_const_unsigned(ira, &instruction->base, align_in_bytes); @@ -19240,7 +20010,7 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr { BigInt *op1_bigint = &casted_op1->value.data.x_bigint; BigInt *op2_bigint = &casted_op2->value.data.x_bigint; - ConstExprValue *pointee_val = ir_const_ptr_pointee(ira, &casted_result_ptr->value, casted_result_ptr->source_node); + ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, &casted_result_ptr->value, casted_result_ptr->source_node); if (pointee_val == nullptr) return ira->codegen->invalid_instruction; BigInt *dest_bigint = &pointee_val->data.x_bigint; @@ -19294,7 +20064,8 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - return ir_const_bool(ira, &instruction->base, (err_union_val->data.x_err_union.err != nullptr)); + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base, (err != nullptr)); } } @@ -19320,48 +20091,47 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) -{ - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) +static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { + IrInstruction *base_ptr = instruction->err_union->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->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); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - assert(err); - IrInstruction *result = ir_const(ira, &instruction->base, - type_entry->data.error_union.err_set_type); - result->value.data.x_err_set = err; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); - result->value.type = type_entry->data.error_union.err_set_type; - return result; - } else { - ir_add_error(ira, value, + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + assert(err); + + IrInstruction *result = ir_const(ira, &instruction->base, + type_entry->data.error_union.err_set_type); + result->value.data.x_err_set = err; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, base_ptr); + result->value.type = type_entry->data.error_union.err_set_type; + return result; } static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, @@ -19377,48 +20147,48 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { - return ira->codegen->invalid_instruction; - } - 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); - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = ir_const_ptr_pointee(ira, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *result = ir_const(ira, &instruction->base, 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; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - return result; - } else { + if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + + 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); + if (instr_is_comptime(value)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_const(ira, &instruction->base, 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; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); + result->value.type = result_type; + return result; } static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { @@ -19696,6 +20466,39 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, return ira->codegen->invalid_instruction; } } + } else if (switch_type->id == ZigTypeIdBool) { + int seenTrue = 0; + int seenFalse = 0; + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstruction *value = range->start->child; + + IrInstruction *casted_value = ir_implicit_cast(ira, value, switch_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); + if (!const_expr_val) + return ira->codegen->invalid_instruction; + + assert(const_expr_val->type->id == ZigTypeIdBool); + + if (const_expr_val->data.x_bool == true) { + seenTrue += 1; + } else { + seenFalse += 1; + } + + if ((seenTrue > 1) || (seenFalse > 1)) { + ir_add_error(ira, value, buf_sprintf("duplicate switch value")); + return ira->codegen->invalid_instruction; + } + } + if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) { + ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities")); + return ira->codegen->invalid_instruction; + } } else if (!instruction->have_else_prong) { ir_add_error(ira, &instruction->base, buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); @@ -19802,7 +20605,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); + IrInstruction *result = ir_const(ira, target, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; return result; @@ -19819,7 +20622,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 } static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, - ZigType *dest_type, IrInstruction *dest_type_src) + ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on) { Error err; @@ -19829,12 +20632,14 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ // We have a check for zero bits later so we use get_src_ptr_type to // validate src_type and dest_type. - if (get_src_ptr_type(src_type) == nullptr) { + ZigType *src_ptr_type = get_src_ptr_type(src_type); + if (src_ptr_type == nullptr) { ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } - if (get_src_ptr_type(dest_type) == nullptr) { + ZigType *dest_ptr_type = get_src_ptr_type(dest_type); + if (dest_ptr_type == nullptr) { ir_add_error(ira, dest_type_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; @@ -19846,12 +20651,24 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ } if (instr_is_comptime(ptr)) { - ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk); + 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); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, - dest_type); + if (val->special == ConstValSpecialStatic) { + bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull || + (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr == 0); + if (is_addr_zero && !dest_allows_addr_zero) { + ir_add_error(ira, source_instr, + buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + } + + IrInstruction *result = ir_const(ira, source_instr, dest_type); copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -19874,9 +20691,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return ira->codegen->invalid_instruction; } - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; + IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -19902,7 +20717,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return result; } -static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -19913,10 +20728,24 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; - return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value); + return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value, + instruction->safety_check_on); +} + +static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *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]; + 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) { + if (val->special == ConstValSpecialUndef) + val->special = ConstValSpecialStatic; assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: @@ -19959,17 +20788,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } case ZigTypeIdArray: - { - 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]; - buf_write_value_bytes(codegen, &buf[buf_i], elem); - buf_i += type_size(codegen, elem->type); - } - } - return; + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); + case ZigTypeIdVector: + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); case ZigTypeIdOptional: @@ -19986,7 +20807,33 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } -static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { +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) +{ + Error err; + uint64_t elem_size = type_size(codegen, elem_type); + + switch (val->data.x_array.special) { + 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]; + elem->special = ConstValSpecialStatic; + elem->type = elem_type; + if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) + return err; + } + return ErrorNone; + case ConstArraySpecialUndef: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); + case ConstArraySpecialBuf: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); + } + zig_unreachable(); +} + +static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) { + Error err; assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: @@ -20003,17 +20850,17 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdPromise: zig_unreachable(); case ZigTypeIdVoid: - return; + return ErrorNone; case ZigTypeIdBool: val->data.x_bool = (buf[0] != 0); - return; + return ErrorNone; case ZigTypeIdInt: bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, codegen->is_big_endian, val->type->data.integral.is_signed); - return; + return ErrorNone; case ZigTypeIdFloat: float_read_ieee597(val, buf, codegen->is_big_endian); - return; + return ErrorNone; case ZigTypeIdPointer: { val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; @@ -20021,20 +20868,67 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian, false); val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); - return; + return ErrorNone; } case ZigTypeIdArray: - zig_panic("TODO buf_read_value_bytes array type"); + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, + val->type->data.array.len); + case ZigTypeIdVector: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, + val->type->data.vector.len); + case ZigTypeIdEnum: + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + assert(tag_int_type->id == ZigTypeIdInt); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return ErrorNone; + } + } + zig_unreachable(); case ZigTypeIdStruct: - zig_panic("TODO buf_read_value_bytes struct type"); + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: { + ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted", + buf_ptr(&val->type->name))); + add_error_note(codegen, msg, val->type->data.structure.decl_node, + buf_sprintf("declared here")); + return ErrorSemanticAnalyzeFail; + } + 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); + for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { + ConstExprValue *field_val = &val->data.x_struct.fields[field_i]; + field_val->special = ConstValSpecialStatic; + TypeStructField *type_field = &val->type->data.structure.fields[field_i]; + field_val->type = type_field->type_entry; + if (type_field->gen_index == SIZE_MAX) + continue; + size_t offset = LLVMOffsetOfElement(codegen->target_data_ref, val->type->type_ref, + type_field->gen_index); + uint8_t *new_buf = buf + offset; + if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val))) + return err; + } + return ErrorNone; + } + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes packed struct"); + } + zig_unreachable(); case ZigTypeIdOptional: zig_panic("TODO buf_read_value_bytes maybe type"); case ZigTypeIdErrorUnion: zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdEnum: - zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: @@ -20043,8 +20937,86 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { +static bool type_can_bit_cast(ZigType *t) { + switch (t->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdNamespace: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdPointer: + return false; + default: + // TODO list these types out explicitly, there are probably some other invalid ones here + return true; + } +} + +static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *dest_type) +{ Error err; + + ZigType *src_type = value->value.type; + assert(get_codegen_ptr_type(src_type) == nullptr); + assert(type_can_bit_cast(src_type)); + assert(get_codegen_ptr_type(dest_type) == nullptr); + assert(type_can_bit_cast(dest_type)); + + 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) { + ir_add_error(ira, source_instr, + buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, + buf_ptr(&dest_type->name), dest_size_bytes, + buf_ptr(&src_type->name), src_size_bytes)); + return ira->codegen->invalid_instruction; + } + + uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); + uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); + if (dest_size_bits != src_size_bits) { + ir_add_error(ira, source_instr, + buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", + buf_ptr(&dest_type->name), dest_size_bits, + buf_ptr(&src_type->name), src_size_bits)); + return ira->codegen->invalid_instruction; + } + + if (instr_is_comptime(value)) { + ConstExprValue *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))) + return ira->codegen->invalid_instruction; + return result; + } + + IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, value); + result->value.type = dest_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -20055,35 +21027,16 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; - if ((err = ensure_complete_type(ira->codegen, dest_type))) - return ira->codegen->invalid_instruction; - - if ((err = ensure_complete_type(ira->codegen, src_type))) - return ira->codegen->invalid_instruction; - if (get_codegen_ptr_type(src_type) != nullptr) { ir_add_error(ira, value, buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } - switch (src_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - default: - break; + if (!type_can_bit_cast(src_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_instruction; } if (get_codegen_ptr_type(dest_type) != nullptr) { @@ -20092,50 +21045,40 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } - switch (dest_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } - - 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) { - ir_add_error(ira, &instruction->base, - buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, - buf_ptr(&dest_type->name), dest_size_bytes, - buf_ptr(&src_type->name), src_size_bytes)); + if (!type_can_bit_cast(dest_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type); +} + +static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + ZigType *ptr_type) +{ + assert(get_src_ptr_type(ptr_type) != nullptr); + assert(type_has_bits(ptr_type)); + + IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); + 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); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - uint8_t *buf = allocate_nonzero(src_size_bytes); - buf_write_value_bytes(ira->codegen, buf, val); - buf_read_value_bytes(ira->codegen, buf, &result->value); + 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 = bigint_as_unsigned(&val->data.x_bigint); return result; } - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, value); - result->value.type = dest_type; + 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; return result; } @@ -20160,29 +21103,12 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; } + IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); - 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); - if (!val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); - result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); - return result; - } - - IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, casted_int); - result->value.type = dest_type; - return result; + return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type); } static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, @@ -20199,8 +21125,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, case TldIdContainer: case TldIdCompTime: zig_unreachable(); - case TldIdVar: - { + case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; @@ -20218,8 +21143,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ir_get_deref(ira, &instruction->base, var_ptr); } } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20265,8 +21189,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, usize); + 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; return result; @@ -20291,6 +21214,15 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct } else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) { ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); return ira->codegen->invalid_instruction; + } else if (instruction->ptr_len == PtrLenC) { + if (!type_allowed_in_extern(ira->codegen, child_type)) { + ir_add_error(ira, &instruction->base, + buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + } else if (child_type->id == ZigTypeIdOpaque) { + ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types")); + return ira->codegen->invalid_instruction; + } } uint32_t align_bytes; @@ -20299,6 +21231,9 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; + if (!type_has_bits(child_type)) { + align_bytes = 0; + } } else { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; @@ -20898,6 +21833,126 @@ static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionS return result; } +static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *op = instruction->op->child; + if (type_is_invalid(op->value.type)) + return ira->codegen->invalid_instruction; + + if (int_type->id != ZigTypeIdInt) { + ir_add_error(ira, instruction->type, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name))); + return ira->codegen->invalid_instruction; + } + + if (int_type->data.integral.bit_count % 8 != 0) { + ir_add_error(ira, instruction->type, + buf_sprintf("@bswap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8", + buf_ptr(&int_type->name), int_type->data.integral.bit_count)); + return ira->codegen->invalid_instruction; + } + + IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type); + if (type_is_invalid(casted_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); + return result; + } + + if (int_type->data.integral.bit_count == 8) { + return casted_op; + } + + if (instr_is_comptime(casted_op)) { + ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_const(ira, &instruction->base, int_type); + size_t buf_size = int_type->data.integral.bit_count / 8; + uint8_t *buf = allocate_nonzero(buf_size); + 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, + int_type->data.integral.is_signed); + return result; + } + + IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_op); + result->value.type = int_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) { + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *op = instruction->op->child; + if (type_is_invalid(op->value.type)) + return ira->codegen->invalid_instruction; + + if (int_type->id != ZigTypeIdInt) { + ir_add_error(ira, instruction->type, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type); + if (type_is_invalid(casted_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); + return result; + } + + if (instr_is_comptime(casted_op)) { + ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_const(ira, &instruction->base, int_type); + size_t num_bits = int_type->data.integral.bit_count; + size_t buf_size = (num_bits + 7) / 8; + uint8_t *comptime_buf = allocate_nonzero(buf_size); + uint8_t *result_buf = allocate_nonzero(buf_size); + memset(comptime_buf,0,buf_size); + memset(result_buf,0,buf_size); + + bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian); + + size_t bit_i = 0; + size_t bit_rev_i = num_bits - 1; + for (; bit_i < num_bits; bit_i++, bit_rev_i--) { + if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) { + result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8)); + } + } + + bigint_read_twos_complement(&result->value.data.x_bigint, + result_buf, + int_type->data.integral.bit_count, + ira->codegen->is_big_endian, + int_type->data.integral.is_signed); + + return result; + } + + IrInstruction *result = ir_build_bit_reverse(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_op); + result->value.type = int_type; + return result; +} + + static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { Error err; IrInstruction *target = instruction->target->child; @@ -20981,6 +22036,12 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdDeclVarGen: + case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdArrayToVector: + case IrInstructionIdVectorToArray: + case IrInstructionIdAssertZero: zig_unreachable(); case IrInstructionIdReturn: @@ -20991,8 +22052,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: @@ -21037,8 +22098,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -21079,8 +22140,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); - case IrInstructionIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -21103,6 +22164,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); + case IrInstructionIdVectorType: + return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction); case IrInstructionIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); case IrInstructionIdMemset: @@ -21147,8 +22210,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); - case IrInstructionIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: @@ -21233,6 +22296,10 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); + case IrInstructionIdBswap: + return ir_analyze_instruction_bswap(ira, (IrInstructionBswap *)instruction); + case IrInstructionIdBitReverse: + return ir_analyze_instruction_bit_reverse(ira, (IrInstructionBitReverse *)instruction); case IrInstructionIdIntToErr: return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: @@ -21328,7 +22395,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: @@ -21343,7 +22411,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -21371,6 +22438,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: + case IrInstructionIdAssertZero: return true; case IrInstructionIdPhi: @@ -21396,7 +22466,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: @@ -21407,6 +22477,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdEmbedFile: case IrInstructionIdTruncate: case IrInstructionIdIntType: + case IrInstructionIdVectorType: case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: @@ -21423,7 +22494,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: @@ -21454,6 +22526,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: + case IrInstructionIdBswap: + case IrInstructionIdBitReverse: case IrInstructionIdAtomicLoad: case IrInstructionIdIntCast: case IrInstructionIdFloatCast: @@ -21464,6 +22538,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdVectorToArray: + case IrInstructionIdArrayToVector: return false; case IrInstructionIdAsm: diff --git a/src/ir.hpp b/src/ir.hpp index b298750dec..0b85ad2c55 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -13,15 +13,18 @@ bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable); bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *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); + IrExecutable *parent_exec, AstNode *expected_type_source_node); ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, ZigType *expected_type, AstNode *expected_type_source_node); bool ir_has_side_effects(IrInstruction *instruction); -ConstExprValue *const_ptr_pointee(CodeGen *codegen, ConstExprValue *const_val); + +struct IrAnalyze; +ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, + AstNode *source_node); #endif diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 13c06e4e2d..75da24d1a9 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -172,7 +172,7 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction } } -static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instruction) { +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 = buf_ptr(&decl_var_instruction->var->name); if (decl_var_instruction->var_type) { @@ -332,8 +332,8 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { } static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { - fprintf(irp->f, "*"); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ".*"); } static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { @@ -479,15 +479,15 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { fprintf(irp->f, ")"); } -static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { - fprintf(irp->f, "*"); +static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { - fprintf(irp->f, "&??*"); - ir_print_other_instruction(irp, instruction->value); +static void ir_print_optional_unwrap_ptr(IrPrint *irp, IrInstructionOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_instruction(irp, instruction->base_ptr); + fprintf(irp->f, ".*.?"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } @@ -613,7 +613,7 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio fprintf(irp->f, ")"); } -static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { +static void ir_print_cmpxchg_src(IrPrint *irp, IrInstructionCmpxchgSrc *instruction) { fprintf(irp->f, "@cmpxchg("); ir_print_other_instruction(irp, instruction->ptr); fprintf(irp->f, ", "); @@ -627,6 +627,16 @@ static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { fprintf(irp->f, ")"); } +static void ir_print_cmpxchg_gen(IrPrint *irp, IrInstructionCmpxchgGen *instruction) { + fprintf(irp->f, "@cmpxchg("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->new_value); + fprintf(irp->f, ", TODO print atomic orders)"); +} + static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, "@fence("); ir_print_other_instruction(irp, instruction->order_value); @@ -709,6 +719,14 @@ static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, ")"); } +static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruction) { + fprintf(irp->f, "@Vector("); + ir_print_other_instruction(irp, instruction->len); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ")"); +} + static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) { fprintf(irp->f, "! "); ir_print_other_instruction(irp, instruction->value); @@ -820,13 +838,13 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) { } static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) { - fprintf(irp->f, "@unwrapErrorCode("); - ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, "UnwrapErrorCode("); + ir_print_other_instruction(irp, instruction->err_union); fprintf(irp->f, ")"); } static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) { - fprintf(irp->f, "@unwrapErrorPayload("); + fprintf(irp->f, "ErrorUnionFieldPayload("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); if (!instruction->safety_check_on) { @@ -879,7 +897,7 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { +static void ir_print_ptr_cast_src(IrPrint *irp, IrInstructionPtrCastSrc *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { ir_print_other_instruction(irp, instruction->dest_type); @@ -889,6 +907,12 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, ")"); } +static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruction) { + fprintf(irp->f, "@ptrCast("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); if (instruction->dest_type) { @@ -900,7 +924,7 @@ static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { } static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { - fprintf(irp->f, "@widenOrShorten("); + fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -948,6 +972,24 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime fprintf(irp->f, ")"); } +static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) { + fprintf(irp->f, "ArrayToVector("); + ir_print_other_instruction(irp, instruction->array); + fprintf(irp->f, ")"); +} + +static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) { + fprintf(irp->f, "VectorToArray("); + ir_print_other_instruction(irp, instruction->vector); + fprintf(irp->f, ")"); +} + +static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruction) { + fprintf(irp->f, "AssertZero("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1323,6 +1365,44 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { fprintf(irp->f, ")"); } +static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_var_instruction) { + ZigVar *var = decl_var_instruction->var; + const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; + const char *name = buf_ptr(&decl_var_instruction->var->name); + fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name), + var->align_bytes); + + ir_print_other_instruction(irp, decl_var_instruction->init_value); + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); + } +} + +static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { + fprintf(irp->f, "@bswap("); + 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->op); + fprintf(irp->f, ")"); +} + +static void ir_print_bit_reverse(IrPrint *irp, IrInstructionBitReverse *instruction) { + fprintf(irp->f, "@bitreverse("); + 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->op); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1337,8 +1417,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBinOp: ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); break; - case IrInstructionIdDeclVar: - ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + ir_print_decl_var_src(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); @@ -1428,10 +1508,10 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); break; case IrInstructionIdTestNonNull: - ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); + ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapOptional: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstructionOptionalUnwrapPtr *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1484,8 +1564,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdEmbedFile: ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction); break; - case IrInstructionIdCmpxchg: - ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + ir_print_cmpxchg_src(irp, (IrInstructionCmpxchgSrc *)instruction); + break; + case IrInstructionIdCmpxchgGen: + ir_print_cmpxchg_gen(irp, (IrInstructionCmpxchgGen *)instruction); break; case IrInstructionIdFence: ir_print_fence(irp, (IrInstructionFence *)instruction); @@ -1520,6 +1603,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; + case IrInstructionIdVectorType: + ir_print_vector_type(irp, (IrInstructionVectorType *)instruction); + break; case IrInstructionIdBoolNot: ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction); break; @@ -1583,8 +1669,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdPtrCast: - ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + ir_print_ptr_cast_src(irp, (IrInstructionPtrCastSrc *)instruction); + break; + case IrInstructionIdPtrCastGen: + ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); @@ -1736,6 +1825,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSqrt: ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); break; + case IrInstructionIdBswap: + ir_print_bswap(irp, (IrInstructionBswap *)instruction); + break; + case IrInstructionIdBitReverse: + ir_print_bit_reverse(irp, (IrInstructionBitReverse *)instruction); + break; case IrInstructionIdAtomicLoad: ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); break; @@ -1745,6 +1840,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCheckRuntimeScope: ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); break; + case IrInstructionIdDeclVarGen: + ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); + break; + case IrInstructionIdArrayToVector: + ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction); + break; + case IrInstructionIdVectorToArray: + ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction); + break; + case IrInstructionIdAssertZero: + ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/link.cpp b/src/link.cpp index 188f976a86..58221a99ea 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) } CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir); + parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir); child_gen->out_h_path = nullptr; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; @@ -444,73 +444,20 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si return ZigLLDLink(oformat, args, arg_count, link_diag_callback, diag); } -static void construct_linker_job_coff(LinkJob *lj) { +static void add_uefi_link_args(LinkJob *lj) { + lj->args.append("/BASE:0"); + lj->args.append("/ENTRY:EfiMain"); + lj->args.append("/OPT:REF"); + lj->args.append("/SAFESEH:NO"); + lj->args.append("/MERGE:.rdata=.data"); + lj->args.append("/ALIGN:32"); + lj->args.append("/NODEFAULTLIB"); + lj->args.append("/SECTION:.xdata,D"); +} + +static void add_nt_link_args(LinkJob *lj, bool is_library) { CodeGen *g = lj->codegen; - lj->args.append("/ERRORLIMIT:0"); - - if (g->libc_link_lib != nullptr) { - find_libc_lib_path(g); - } - - lj->args.append("-NOLOGO"); - - if (!g->strip_debug_symbols) { - lj->args.append("-DEBUG"); - } - - if (g->out_type == OutTypeExe) { - // TODO compile time stack upper bound detection - lj->args.append("/STACK:16777216"); - } - - coff_append_machine_arg(g, &lj->args); - - if (g->windows_subsystem_windows) { - lj->args.append("/SUBSYSTEM:windows"); - } else if (g->windows_subsystem_console) { - lj->args.append("/SUBSYSTEM:console"); - } - // The commented out stuff is from when we linked with MinGW - // Now that we're linking with LLD it remains to be determined - // how to handle --target-environ gnu - // These comments are a clue - - bool is_library = g->out_type == OutTypeLib; - //bool dll = g->out_type == OutTypeLib; - //bool shared = !g->is_static && dll; - //if (g->is_static) { - // lj->args.append("-Bstatic"); - //} else { - // if (dll) { - // lj->args.append("--dll"); - // } else if (shared) { - // lj->args.append("--shared"); - // } - // lj->args.append("-Bdynamic"); - // if (dll || shared) { - // lj->args.append("-e"); - // if (g->zig_target.arch.arch == ZigLLVM_x86) { - // lj->args.append("_DllMainCRTStartup@12"); - // } else { - // lj->args.append("DllMainCRTStartup"); - // } - // lj->args.append("--enable-auto-image-base"); - // } - //} - - lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); - - if (g->libc_link_lib != nullptr) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir)))); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir)))); - - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir)))); - if (g->libc_static_lib_dir != nullptr) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir)))); - } - } - if (lj->link_in_crt) { const char *lib_str = g->is_static ? "lib" : ""; const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : ""; @@ -533,29 +480,180 @@ static void construct_linker_job_coff(LinkJob *lj) { //https://msdn.microsoft.com/en-us/library/bb531344.aspx lj->args.append("legacy_stdio_definitions.lib"); - //if (shared || dll) { - // lj->args.append(get_libc_file(g, "dllcrt2.o")); - //} else { - // if (g->windows_linker_unicode) { - // lj->args.append(get_libc_file(g, "crt2u.o")); - // } else { - // lj->args.append(get_libc_file(g, "crt2.o")); - // } - //} - //lj->args.append(get_libc_static_file(g, "crtbegin.o")); - // msvcrt depends on kernel32 lj->args.append("kernel32.lib"); } else { - lj->args.append("-NODEFAULTLIB"); + lj->args.append("/NODEFAULTLIB"); if (!is_library) { if (g->have_winmain) { - lj->args.append("-ENTRY:WinMain"); + lj->args.append("/ENTRY:WinMain"); } else { - lj->args.append("-ENTRY:WinMainCRTStartup"); + lj->args.append("/ENTRY:WinMainCRTStartup"); } } } +} + +// These are n actual command lines from LINK.EXE UEFI builds (app & driver) to be used as guidance cleaning +// up a bit for building the COFF linker args: +// /OUT:"J:\coding\nebulae\k\x64\Release\k.efi" /LTCG:incremental /Driver /PDB:"J:\coding\nebulae\k\x64\Release\k.pdb" "UefiApplicationEntryPoint.lib" "UefiRuntimeLib.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\nebulae\k\x64\Release\k.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_APPLICATION /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" +// /OUT:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.efi" /LTCG:incremental /Driver /PDB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.pdb" "UefiDriverEntryPoint.lib" "UefiHiiLib.lib" "UefiHiiServicesLib.lib" "UefiSortLib.lib" "UefiShellLib.lib" "GlueLib.lib" "BaseLib.lib" "BaseDebugPrintErrorLevelLib.lib" "BasePrintLib.lib" "UefiLib.lib" "UefiBootServicesTableLib.lib" "UefiRuntimeServicesTableLib.lib" "UefiDevicePathLibDevicePathProtocol.lib" "UefiDebugLibConOut.lib" "UefiMemoryLib.lib" "UefiMemoryAllocationLib.lib" "BaseSynchronizationLib.lib" "UefiFileHandleLib.lib" /IMPLIB:"J:\coding\VisualUefi\samples\x64\Release\UefiDriver.lib" /DEBUG:FASTLINK /BASE:"0" /MACHINE:X64 /ENTRY:"EfiMain" /OPT:REF /SAFESEH:NO /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /MERGE:".rdata=.data" /NOLOGO /ALIGN:32 /NODEFAULTLIB /SECTION:".xdata,D" +// This commented out stuff is from when we linked with MinGW +// Now that we're linking with LLD it remains to be determined +// how to handle --target-environ gnu +// These comments are a clue +//bool dll = g->out_type == OutTypeLib; +//bool shared = !g->is_static && dll; +//if (g->is_static) { +// lj->args.append("-Bstatic"); +//} else { +// if (dll) { +// lj->args.append("--dll"); +// } else if (shared) { +// lj->args.append("--shared"); +// } +// lj->args.append("-Bdynamic"); +// if (dll || shared) { +// lj->args.append("-e"); +// if (g->zig_target.arch.arch == ZigLLVM_x86) { +// lj->args.append("_DllMainCRTStartup@12"); +// } else { +// lj->args.append("DllMainCRTStartup"); +// } +// lj->args.append("--enable-auto-image-base"); +// } +//} +//if (shared || dll) { +// lj->args.append(get_libc_file(g, "dllcrt2.o")); +//} else { +// if (g->windows_linker_unicode) { +// lj->args.append(get_libc_file(g, "crt2u.o")); +// } else { +// lj->args.append(get_libc_file(g, "crt2.o")); +// } +//} +//lj->args.append(get_libc_static_file(g, "crtbegin.o")); +//if (g->libc_link_lib != nullptr) { +//if (g->is_static) { +// lj->args.append("--start-group"); +//} + +//lj->args.append("-lmingw32"); + +//lj->args.append("-lgcc"); +//bool is_android = (g->zig_target.env_type == ZigLLVM_Android); +//bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); +//if (!g->is_static && !is_android) { +// if (!is_cyg_ming) { +// lj->args.append("--as-needed"); +// } +// //lj->args.append("-lgcc_s"); +// if (!is_cyg_ming) { +// lj->args.append("--no-as-needed"); +// } +//} +//if (g->is_static && !is_android) { +// //lj->args.append("-lgcc_eh"); +//} +//if (is_android && !g->is_static) { +// lj->args.append("-ldl"); +//} + +//lj->args.append("-lmoldname"); +//lj->args.append("-lmingwex"); +//lj->args.append("-lmsvcrt"); + + +//if (g->windows_subsystem_windows) { +// lj->args.append("-lgdi32"); +// lj->args.append("-lcomdlg32"); +//} +//lj->args.append("-ladvapi32"); +//lj->args.append("-lshell32"); +//lj->args.append("-luser32"); +//lj->args.append("-lkernel32"); + +//if (g->is_static) { +// lj->args.append("--end-group"); +//} + +//if (lj->link_in_crt) { +// lj->args.append(get_libc_static_file(g, "crtend.o")); +//} +//} + + +static void construct_linker_job_coff(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("/ERRORLIMIT:0"); + + if (g->libc_link_lib != nullptr) { + find_libc_lib_path(g); + } + + lj->args.append("/NOLOGO"); + + if (!g->strip_debug_symbols) { + lj->args.append("/DEBUG"); + } + + if (g->out_type == OutTypeExe) { + // TODO compile time stack upper bound detection + lj->args.append("/STACK:16777216"); + } + + coff_append_machine_arg(g, &lj->args); + + bool is_library = g->out_type == OutTypeLib; + switch (g->subsystem) { + case TargetSubsystemAuto: + break; + case TargetSubsystemConsole: + lj->args.append("/SUBSYSTEM:console"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemEfiApplication: + lj->args.append("/SUBSYSTEM:efi_application"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiBootServiceDriver: + lj->args.append("/SUBSYSTEM:efi_boot_service_driver"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiRom: + lj->args.append("/SUBSYSTEM:efi_rom"); + add_uefi_link_args(lj); + break; + case TargetSubsystemEfiRuntimeDriver: + lj->args.append("/SUBSYSTEM:efi_runtime_driver"); + add_uefi_link_args(lj); + break; + case TargetSubsystemNative: + lj->args.append("/SUBSYSTEM:native"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemPosix: + lj->args.append("/SUBSYSTEM:posix"); + add_nt_link_args(lj, is_library); + break; + case TargetSubsystemWindows: + lj->args.append("/SUBSYSTEM:windows"); + add_nt_link_args(lj, is_library); + break; + } + + lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); + + if (g->libc_link_lib != nullptr) { + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir)))); + + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir)))); + if (g->libc_static_lib_dir != nullptr) { + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir)))); + } + } if (is_library && !g->is_static) { lj->args.append("-DLL"); @@ -627,54 +725,6 @@ static void construct_linker_job_coff(LinkJob *lj) { } } - //if (g->libc_link_lib != nullptr) { - //if (g->is_static) { - // lj->args.append("--start-group"); - //} - - //lj->args.append("-lmingw32"); - - //lj->args.append("-lgcc"); - //bool is_android = (g->zig_target.env_type == ZigLLVM_Android); - //bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); - //if (!g->is_static && !is_android) { - // if (!is_cyg_ming) { - // lj->args.append("--as-needed"); - // } - // //lj->args.append("-lgcc_s"); - // if (!is_cyg_ming) { - // lj->args.append("--no-as-needed"); - // } - //} - //if (g->is_static && !is_android) { - // //lj->args.append("-lgcc_eh"); - //} - //if (is_android && !g->is_static) { - // lj->args.append("-ldl"); - //} - - //lj->args.append("-lmoldname"); - //lj->args.append("-lmingwex"); - //lj->args.append("-lmsvcrt"); - - - //if (g->windows_subsystem_windows) { - // lj->args.append("-lgdi32"); - // lj->args.append("-lcomdlg32"); - //} - //lj->args.append("-ladvapi32"); - //lj->args.append("-lshell32"); - //lj->args.append("-luser32"); - //lj->args.append("-lkernel32"); - - //if (g->is_static) { - // lj->args.append("--end-group"); - //} - - //if (lj->link_in_crt) { - // lj->args.append(get_libc_static_file(g, "crtend.o")); - //} - //} } diff --git a/src/main.cpp b/src/main.cpp index 078dfb25f9..78446f9e98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) { return EXIT_FAILURE; } -static int print_full_usage(const char *arg0) { - fprintf(stdout, +static int print_full_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, "Usage: %s [command] [options]\n" "\n" "Commands:\n" @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) { " 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" + " fmt parse files and render in canonical zig format\n" " help show this usage information\n" " id print the base64-encoded compiler id\n" " init-exe initialize a `zig build` application in the cwd\n" @@ -59,6 +60,7 @@ static int print_full_usage(const char *arg0) { " --release-fast build with optimizations on and safety off\n" " --release-safe build with optimizations on and safety on\n" " --release-small build with size optimizations on and safety off\n" + " --single-threaded source may assume it is only used single-threaded\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" @@ -73,6 +75,7 @@ static int print_full_usage(const char *arg0) { " -dirafter [dir] same as -isystem but do it last\n" " -isystem [dir] add additional search path for other .h files\n" " -mllvm [arg] forward an arg to LLVM's option processing\n" + " --override-std-dir [arg] use an alternate Zig standard library\n" "\n" "Link Options:\n" " --dynamic-linker [path] set the path to ld.so\n" @@ -90,8 +93,7 @@ static int print_full_usage(const char *arg0) { " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" " --no-rosegment compromise security to workaround valgrind bug\n" - " -mconsole (windows) --subsystem console to the linker\n" - " -mwindows (windows) --subsystem windows to the linker\n" + " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" @@ -105,7 +107,7 @@ static int print_full_usage(const char *arg0) { " --test-cmd [arg] specify test execution command one arg at a time\n" " --test-cmd-bin appends test binary path to test cmd args\n" , arg0); - return EXIT_SUCCESS; + return return_code; } static const char *ZIG_ZEN = "\n" @@ -371,8 +373,6 @@ int main(int argc, char **argv) { const char *target_arch = nullptr; const char *target_os = nullptr; const char *target_environ = nullptr; - bool mwindows = false; - bool mconsole = false; bool rdynamic = false; const char *mmacosx_version_min = nullptr; const char *mios_version_min = nullptr; @@ -395,6 +395,9 @@ int main(int argc, char **argv) { int runtime_args_start = -1; bool no_rosegment_workaround = false; bool system_linker_hack = false; + TargetSubsystem subsystem = TargetSubsystemAuto; + bool is_single_threaded = false; + Buf *override_std_dir = nullptr; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -430,7 +433,8 @@ int main(int argc, char **argv) { Buf *build_runner_path = buf_alloc(); os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path); - CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir()); + CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + override_std_dir); g->enable_time_report = timing_info; buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_create_from_str("build")); @@ -512,6 +516,31 @@ int main(int argc, char **argv) { fprintf(stderr, "\n"); } return (term.how == TerminationIdClean) ? term.code : -1; + } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { + init_all_targets(); + Buf *fmt_runner_path = buf_alloc(); + os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); + CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + nullptr); + g->is_single_threaded = true; + codegen_set_out_name(g, buf_create_from_str("fmt")); + g->enable_cache = true; + + codegen_build_and_link(g); + + ZigList args = {0}; + for (int i = 2; i < argc; i += 1) { + args.append(argv[i]); + } + args.append(nullptr); + const char *exec_path = buf_ptr(&g->output_file_path); + + os_execv(exec_path, args.items); + + args.pop(); + Termination term; + os_spawn_process(exec_path, args, &term); + return term.code; } for (int i = 1; i < argc; i += 1) { @@ -524,6 +553,8 @@ int main(int argc, char **argv) { build_mode = BuildModeSafeRelease; } else if (strcmp(arg, "--release-small") == 0) { build_mode = BuildModeSmallRelease; + } else if (strcmp(arg, "--help") == 0) { + return print_full_usage(arg0, stderr, EXIT_FAILURE); } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { @@ -540,10 +571,6 @@ int main(int argc, char **argv) { verbose_llvm_ir = true; } else if (strcmp(arg, "--verbose-cimport") == 0) { verbose_cimport = true; - } else if (strcmp(arg, "-mwindows") == 0) { - mwindows = true; - } else if (strcmp(arg, "-mconsole") == 0) { - mconsole = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; } else if (strcmp(arg, "--no-rosegment") == 0) { @@ -556,6 +583,8 @@ int main(int argc, char **argv) { disable_pic = true; } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; + } else if (strcmp(arg, "--single-threaded") == 0) { + is_single_threaded = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { test_exec_args.append(nullptr); } else if (arg[1] == 'L' && arg[2] != 0) { @@ -647,6 +676,8 @@ int main(int argc, char **argv) { clang_argv.append(argv[i]); llvm_argv.append(argv[i]); + } else if (strcmp(arg, "--override-std-dir") == 0) { + override_std_dir = buf_create_from_str(argv[i]); } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { lib_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { @@ -687,6 +718,37 @@ int main(int argc, char **argv) { ver_patch = atoi(argv[i]); } else if (strcmp(arg, "--test-cmd") == 0) { test_exec_args.append(argv[i]); + } else if (strcmp(arg, "--subsystem") == 0) { + if (strcmp(argv[i], "console") == 0) { + subsystem = TargetSubsystemConsole; + } else if (strcmp(argv[i], "windows") == 0) { + subsystem = TargetSubsystemWindows; + } else if (strcmp(argv[i], "posix") == 0) { + subsystem = TargetSubsystemPosix; + } else if (strcmp(argv[i], "native") == 0) { + subsystem = TargetSubsystemNative; + } else if (strcmp(argv[i], "efi_application") == 0) { + subsystem = TargetSubsystemEfiApplication; + } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) { + subsystem = TargetSubsystemEfiBootServiceDriver; + } else if (strcmp(argv[i], "efi_rom") == 0) { + subsystem = TargetSubsystemEfiRom; + } else if (strcmp(argv[i], "efi_runtime_driver") == 0) { + subsystem = TargetSubsystemEfiRuntimeDriver; + } else { + fprintf(stderr, "invalid: --subsystem %s\n" + "Options are:\n" + " console\n" + " windows\n" + " posix\n" + " native\n" + " efi_application\n" + " efi_boot_service_driver\n" + " efi_rom\n" + " efi_runtime_driver\n" + , argv[i]); + return EXIT_FAILURE; + } } else { fprintf(stderr, "Invalid argument: %s\n", arg); return print_error_usage(arg0); @@ -790,7 +852,8 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuiltin: { - CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir()); + CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); + g->is_single_threaded = is_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))); @@ -848,7 +911,10 @@ int main(int argc, char **argv) { if (cmd == CmdRun && buf_out_name == nullptr) { buf_out_name = buf_create_from_str("run"); } - CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); + CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), + override_std_dir); + g->subsystem = subsystem; + if (disable_pic) { if (out_type != OutTypeLib || !is_static) { fprintf(stderr, "--disable-pic only applies to static libraries"); @@ -862,6 +928,7 @@ int main(int argc, char **argv) { codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); codegen_set_is_test(g, cmd == CmdTest); + g->is_single_threaded = is_single_threaded; codegen_set_linker_script(g, linker_script); if (each_lib_rpath) codegen_set_each_lib_rpath(g, each_lib_rpath); @@ -909,7 +976,6 @@ int main(int argc, char **argv) { codegen_add_rpath(g, rpath_list.at(i)); } - codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_rdynamic(g, rdynamic); g->no_rosegment_workaround = no_rosegment_workaround; if (mmacosx_version_min && mios_version_min) { @@ -1042,7 +1108,7 @@ int main(int argc, char **argv) { } } case CmdHelp: - return print_full_usage(arg0); + return print_full_usage(arg0, stdout, EXIT_SUCCESS); case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; diff --git a/src/os.cpp b/src/os.cpp index eba95b9f2f..8eb6b34654 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1456,7 +1456,7 @@ Error os_self_exe_path(Buf *out_path) { if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { return ErrorUnexpected; } - buf_resize(out_path, cb); + buf_resize(out_path, cb - 1); return ErrorNone; #endif return ErrorFileNotFound; @@ -1808,7 +1808,7 @@ Error os_self_exe_shared_libs(ZigList &paths) { #endif } -Error os_file_open_r(Buf *full_path, OsFile *out_file) { +Error os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime) { #if defined(ZIG_OS_WINDOWS) // TODO use CreateFileW HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -1834,8 +1834,18 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) { return ErrorUnexpected; } } - *out_file = result; + + if (mtime != nullptr) { + FILETIME last_write_time; + if (!GetFileTime(result, nullptr, nullptr, &last_write_time)) { + CloseHandle(result); + return ErrorUnexpected; + } + mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime; + mtime->nsec = 0; + } + return ErrorNone; #else for (;;) { @@ -1858,7 +1868,26 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) { return ErrorFileSystem; } } + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) { + close(fd); + return ErrorFileSystem; + } + if (S_ISDIR(statbuf.st_mode)) { + close(fd); + return ErrorIsDir; + } *out_file = fd; + + if (mtime != nullptr) { +#if defined(ZIG_OS_DARWIN) + mtime->sec = statbuf.st_mtimespec.tv_sec; + mtime->nsec = statbuf.st_mtimespec.tv_nsec; +#else + mtime->sec = statbuf.st_mtim.tv_sec; + mtime->nsec = statbuf.st_mtim.tv_nsec; +#endif + } return ErrorNone; } #endif @@ -1948,35 +1977,6 @@ Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { #endif } -Error os_file_mtime(OsFile file, OsTimeStamp *mtime) { -#if defined(ZIG_OS_WINDOWS) - FILETIME last_write_time; - if (!GetFileTime(file, nullptr, nullptr, &last_write_time)) - return ErrorUnexpected; - mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime; - mtime->nsec = 0; - return ErrorNone; -#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) - struct stat statbuf; - if (fstat(file, &statbuf) == -1) - return ErrorFileSystem; - - mtime->sec = statbuf.st_mtim.tv_sec; - mtime->nsec = statbuf.st_mtim.tv_nsec; - return ErrorNone; -#elif defined(ZIG_OS_DARWIN) - struct stat statbuf; - if (fstat(file, &statbuf) == -1) - return ErrorFileSystem; - - mtime->sec = statbuf.st_mtimespec.tv_sec; - mtime->nsec = statbuf.st_mtimespec.tv_nsec; - return ErrorNone; -#else -#error unimplemented -#endif -} - Error os_file_read(OsFile file, void *ptr, size_t *len) { #if defined(ZIG_OS_WINDOWS) DWORD amt_read; diff --git a/src/os.hpp b/src/os.hpp index d81a2362a5..68ac6e0171 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -101,9 +101,8 @@ bool os_path_is_absolute(Buf *path); Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); -Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file); +Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime); Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); -Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime); Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e..3a6ce04647 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -381,7 +381,7 @@ static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parse else_body = ast_expect(pc, body_parser); } - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { // VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON static AstNode *ast_parse_var_decl(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordConst); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordVar); - if (first == nullptr) - return nullptr; - + Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); + Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst); + if (mut_kw == nullptr) + mut_kw = eat_token_if(pc, TokenIdKeywordVar); + if (mut_kw == nullptr) { + if (thread_local_kw == nullptr) { + return nullptr; + } else { + ast_invalid_token_error(pc, peek_token(pc)); + } + } Token *identifier = expect_token(pc, TokenIdSymbol); AstNode *type_expr = nullptr; if (eat_token_if(pc, TokenIdColon) != nullptr) @@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) { expect_token(pc, TokenIdSemicolon); - AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first); - res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst; + AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); + res->data.variable_declaration.threadlocal_tok = thread_local_kw; + res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst; res->data.variable_declaration.symbol = token_buf(identifier); res->data.variable_declaration.type = type_expr; res->data.variable_declaration.align_expr = align_expr; @@ -990,7 +996,7 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) { if (requires_semi && else_body == nullptr) expect_token(pc, TokenIdSemicolon); - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -2204,7 +2210,7 @@ static AstNode *ast_parse_if_prefix(ParseContext *pc) { Optional opt_payload = ast_parse_ptr_payload(pc); PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeTestExpr, first); + AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); res->data.test_expr.target_node = condition; if (opt_payload.unwrap(&payload)) { res->data.test_expr.var_symbol = token_buf(payload.payload); @@ -2772,7 +2778,8 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // PtrTypeStart // <- ASTERISK // / ASTERISK2 -// / LBRACKET ASTERISK RBRACKET +// / PTRUNKNOWN +// / PTRC static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { @@ -2798,6 +2805,13 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { 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; + } + return nullptr; } @@ -2999,7 +3013,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.if_err_expr.then_node, visit, context); visit_field(&node->data.if_err_expr.else_node, visit, context); break; - case NodeTypeTestExpr: + case NodeTypeIfOptional: visit_field(&node->data.test_expr.target_node, visit, context); visit_field(&node->data.test_expr.then_node, visit, context); visit_field(&node->data.test_expr.else_node, visit, context); diff --git a/src/target.cpp b/src/target.cpp index 5a4b5252f1..b1434c6871 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -174,6 +174,7 @@ static const Os os_list[] = { OsContiki, OsAMDPAL, OsZen, + OsUefi, }; // Coordinate with zig_llvm.h @@ -282,6 +283,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) { case OsSolaris: return ZigLLVM_Solaris; case OsWindows: + case OsUefi: return ZigLLVM_Win32; case OsHaiku: return ZigLLVM_Haiku; @@ -394,6 +396,8 @@ const char *get_target_os_name(Os os_type) { return "freestanding"; case OsZen: return "zen"; + case OsUefi: + return "uefi"; case OsAnanas: case OsCloudABI: case OsDragonFly: @@ -756,6 +760,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case CIntTypeCount: zig_unreachable(); } + case OsUefi: case OsWindows: switch (id) { case CIntTypeShort: @@ -802,8 +807,12 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { zig_unreachable(); } +bool target_allows_addr_zero(const ZigTarget *target) { + return target->os == OsFreestanding; +} + const char *target_o_file_ext(ZigTarget *target) { - if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows) { + if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { return ".obj"; } else { return ".o"; @@ -821,13 +830,15 @@ const char *target_llvm_ir_file_ext(ZigTarget *target) { const char *target_exe_file_ext(ZigTarget *target) { if (target->os == OsWindows) { return ".exe"; + } else if (target->os == OsUefi) { + return ".efi"; } else { return ""; } } const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch) { - if (target->os == OsWindows) { + if (target->os == OsWindows || target->os == OsUefi) { if (is_static) { return ".lib"; } else { diff --git a/src/target.hpp b/src/target.hpp index 04652179d2..99d1cadf56 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -51,6 +51,19 @@ enum Os { OsContiki, OsAMDPAL, OsZen, + OsUefi, +}; + +enum TargetSubsystem { + TargetSubsystemAuto, // Zig should infer the subsystem + TargetSubsystemConsole, + TargetSubsystemWindows, + TargetSubsystemPosix, + TargetSubsystemNative, + TargetSubsystemEfiApplication, + TargetSubsystemEfiBootServiceDriver, + TargetSubsystemEfiRom, + TargetSubsystemEfiRuntimeDriver, }; struct ZigTarget { @@ -122,5 +135,6 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target ZigLLVM_OSType get_llvm_os_type(Os os_type); bool target_is_arm(const ZigTarget *target); +bool target_allows_addr_zero(const ZigTarget *target); #endif diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 921ee4de09..9ff6ed3bbe 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = { {"suspend", TokenIdKeywordSuspend}, {"switch", TokenIdKeywordSwitch}, {"test", TokenIdKeywordTest}, + {"threadlocal", TokenIdKeywordThreadLocal}, {"true", TokenIdKeywordTrue}, {"try", TokenIdKeywordTry}, {"undefined", TokenIdKeywordUndefined}, @@ -220,6 +221,7 @@ enum TokenizeState { TokenizeStateError, TokenizeStateLBracket, TokenizeStateLBracketStar, + TokenizeStateLBracketStarC, }; @@ -248,13 +250,8 @@ ATTRIBUTE_PRINTF(2, 3) static void tokenize_error(Tokenize *t, const char *format, ...) { t->state = TokenizeStateError; - if (t->cur_tok) { - t->out->err_line = t->cur_tok->start_line; - t->out->err_column = t->cur_tok->start_column; - } else { - t->out->err_line = t->line; - t->out->err_column = t->column; - } + t->out->err_line = t->line; + t->out->err_column = t->column; va_list ap; va_start(ap, format); @@ -850,7 +847,6 @@ void tokenize(Buf *buf, Tokenization *out) { switch (c) { case '*': t.state = TokenizeStateLBracketStar; - set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); break; default: // reinterpret as just an lbracket @@ -861,6 +857,21 @@ void tokenize(Buf *buf, Tokenization *out) { } 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); @@ -886,6 +897,9 @@ void tokenize(Buf *buf, Tokenization *out) { break; case TokenizeStateSawAmpersand: switch (c) { + case '&': + tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND."); + break; case '=': set_token_id(&t, t.cur_tok, TokenIdBitAndEq); end_token(&t); @@ -1492,6 +1506,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineStringContinue: case TokenizeStateLineStringContinueC: case TokenizeStateLBracketStar: + case TokenizeStateLBracketStarC: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: @@ -1529,6 +1544,7 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; case TokenIdBracketStarBracket: return "[*]"; + case TokenIdBracketStarCBracket: return "[*c]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; @@ -1588,6 +1604,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordTest: return "test"; + case TokenIdKeywordThreadLocal: return "threadlocal"; case TokenIdKeywordTrue: return "true"; case TokenIdKeywordTry: return "try"; case TokenIdKeywordUndefined: return "undefined"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 1574e95571..62117b5779 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -29,6 +29,7 @@ enum TokenId { TokenIdBitShiftRightEq, TokenIdBitXorEq, TokenIdBracketStarBracket, + TokenIdBracketStarCBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, @@ -88,6 +89,7 @@ enum TokenId { TokenIdKeywordSuspend, TokenIdKeywordSwitch, TokenIdKeywordTest, + TokenIdKeywordThreadLocal, TokenIdKeywordTrue, TokenIdKeywordTry, TokenIdKeywordUndefined, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 10f2124eb6..a51a671b06 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4,7 +4,6 @@ * 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" @@ -13,7 +12,7 @@ #include "os.hpp" #include "translate_c.hpp" #include "parser.hpp" - +#include "zig_clang.h" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -30,8 +29,6 @@ #include -using namespace clang; - struct Alias { Buf *new_name; Buf *canon_name; @@ -87,13 +84,13 @@ struct Context { HashMap decl_table; HashMap macro_table; HashMap global_table; - SourceManager *source_manager; + ZigClangSourceManager *source_manager; ZigList aliases; AstNode *source_node; bool warnings_on; CodeGen *codegen; - ASTContext *ctx; + ZigClangASTContext *ctx; TransScopeRoot *global_scope; HashMap ptr_params; @@ -117,21 +114,37 @@ static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *paren static TransScopeBlock *trans_scope_block_find(TransScope *scope); -static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl); -static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl); -static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl); +static AstNode *resolve_record_decl(Context *c, const clang::RecordDecl *record_decl); +static AstNode *resolve_enum_decl(Context *c, const clang::EnumDecl *enum_decl); +static AstNode *resolve_typedef_decl(Context *c, const clang::TypedefNameDecl *typedef_decl); -static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, +static int trans_stmt_extra(Context *c, TransScope *scope, const clang::Stmt *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 Stmt *stmt, AstNode **out_node); -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); -static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc); -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); +static TransScope *trans_stmt(Context *c, TransScope *scope, const clang::Stmt *stmt, AstNode **out_node); +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval); +static AstNode *trans_qual_type(Context *c, clang::QualType qt, const clang::SourceLocation &source_loc); +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval); + +static ZigClangSourceLocation bitcast(clang::SourceLocation src) { + ZigClangSourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} +static ZigClangQualType bitcast(clang::QualType src) { + ZigClangQualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} +static clang::QualType bitcast(ZigClangQualType src) { + clang::QualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} ATTRIBUTE_PRINTF(3, 4) -static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) { +static void emit_warning(Context *c, const clang::SourceLocation &clang_sl, const char *format, ...) { if (!c->warnings_on) { return; } @@ -141,16 +154,17 @@ static void emit_warning(Context *c, const SourceLocation &sl, const char *forma Buf *msg = buf_vprintf(format, ap); va_end(ap); - StringRef filename = c->source_manager->getFilename(c->source_manager->getSpellingLoc(sl)); - const char *filename_bytes = (const char *)filename.bytes_begin(); + ZigClangSourceLocation sl = bitcast(clang_sl); + 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 = c->source_manager->getSpellingLineNumber(sl); - unsigned column = c->source_manager->getSpellingColumnNumber(sl); + 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)); } @@ -291,11 +305,22 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod 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 == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket; - node->data.pointer_type.is_const = is_const; + 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; @@ -460,15 +485,15 @@ static Buf *string_ref_to_buf(StringRef string_ref) { return buf_create_from_mem((const char *)string_ref.bytes_begin(), string_ref.size()); } -static const char *decl_name(const Decl *decl) { - const NamedDecl *named_decl = static_cast(decl); +static const char *decl_name(const clang::Decl *decl) { + const clang::NamedDecl *named_decl = static_cast(decl); return (const char *)named_decl->getName().bytes_begin(); } static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) { AstNode *node = trans_create_node(c, NodeTypeIntLiteral); node->data.int_literal.bigint = allocate(1); - bool is_negative = aps_int.isNegative(); + bool is_negative = aps_int.isSigned() && aps_int.isNegative(); if (!is_negative) { bigint_init_data(node->data.int_literal.bigint, aps_int.getRawData(), aps_int.getNumWords(), false); return node; @@ -479,41 +504,41 @@ static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) } -static const Type *qual_type_canon(QualType qt) { +static const clang::Type *qual_type_canon(clang::QualType qt) { return qt.getCanonicalType().getTypePtr(); } -static QualType get_expr_qual_type(Context *c, const Expr *expr) { +static clang::QualType get_expr_qual_type(Context *c, const clang::Expr *expr) { // String literals in C are `char *` but they should really be `const char *`. - if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *cast_expr = static_cast(expr); - if (cast_expr->getCastKind() == CK_ArrayToPointerDecay) { - const Expr *sub_expr = cast_expr->getSubExpr(); - if (sub_expr->getStmtClass() == Stmt::StringLiteralClass) { - QualType array_qt = sub_expr->getType(); - const ArrayType *array_type = static_cast(array_qt.getTypePtr()); - QualType pointee_qt = array_type->getElementType(); + if (expr->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *cast_expr = static_cast(expr); + if (cast_expr->getCastKind() == clang::CK_ArrayToPointerDecay) { + const clang::Expr *sub_expr = cast_expr->getSubExpr(); + if (sub_expr->getStmtClass() == clang::Stmt::StringLiteralClass) { + clang::QualType array_qt = sub_expr->getType(); + const clang::ArrayType *array_type = static_cast(array_qt.getTypePtr()); + clang::QualType pointee_qt = array_type->getElementType(); pointee_qt.addConst(); - return c->ctx->getPointerType(pointee_qt); + return bitcast(ZigClangASTContext_getPointerType(c->ctx, bitcast(pointee_qt))); } } } return expr->getType(); } -static QualType get_expr_qual_type_before_implicit_cast(Context *c, const Expr *expr) { - if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *cast_expr = static_cast(expr); +static clang::QualType get_expr_qual_type_before_implicit_cast(Context *c, const clang::Expr *expr) { + if (expr->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *cast_expr = static_cast(expr); return get_expr_qual_type(c, cast_expr->getSubExpr()); } return expr->getType(); } -static AstNode *get_expr_type(Context *c, const Expr *expr) { +static AstNode *get_expr_type(Context *c, const clang::Expr *expr) { return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart()); } -static bool qual_types_equal(QualType t1, QualType t2) { +static bool qual_types_equal(clang::QualType t1, clang::QualType t2) { if (t1.isConstQualified() != t2.isConstQualified()) { return false; } @@ -530,37 +555,37 @@ static bool is_c_void_type(AstNode *node) { return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void")); } -static bool expr_types_equal(Context *c, const Expr *expr1, const Expr *expr2) { - QualType t1 = get_expr_qual_type(c, expr1); - QualType t2 = get_expr_qual_type(c, expr2); +static bool expr_types_equal(Context *c, const clang::Expr *expr1, const clang::Expr *expr2) { + clang::QualType t1 = get_expr_qual_type(c, expr1); + clang::QualType t2 = get_expr_qual_type(c, expr2); return qual_types_equal(t1, t2); } -static bool qual_type_is_ptr(QualType qt) { - const Type *ty = qual_type_canon(qt); - return ty->getTypeClass() == Type::Pointer; +static bool qual_type_is_ptr(clang::QualType qt) { + const clang::Type *ty = qual_type_canon(qt); + return ty->getTypeClass() == clang::Type::Pointer; } -static const FunctionProtoType *qual_type_get_fn_proto(QualType qt, bool *is_ptr) { - const Type *ty = qual_type_canon(qt); +static const clang::FunctionProtoType *qual_type_get_fn_proto(clang::QualType qt, bool *is_ptr) { + const clang::Type *ty = qual_type_canon(qt); *is_ptr = false; - if (ty->getTypeClass() == Type::Pointer) { + if (ty->getTypeClass() == clang::Type::Pointer) { *is_ptr = true; - const PointerType *pointer_ty = static_cast(ty); - QualType child_qt = pointer_ty->getPointeeType(); + const clang::PointerType *pointer_ty = static_cast(ty); + clang::QualType child_qt = pointer_ty->getPointeeType(); ty = child_qt.getTypePtr(); } - if (ty->getTypeClass() == Type::FunctionProto) { - return static_cast(ty); + if (ty->getTypeClass() == clang::Type::FunctionProto) { + return static_cast(ty); } return nullptr; } -static bool qual_type_is_fn_ptr(QualType qt) { +static bool qual_type_is_fn_ptr(clang::QualType qt) { bool is_ptr; if (qual_type_get_fn_proto(qt, &is_ptr)) { return is_ptr; @@ -569,30 +594,30 @@ static bool qual_type_is_fn_ptr(QualType qt) { return false; } -static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { - const Type *ty = qt.getTypePtr(); +static uint32_t qual_type_int_bit_width(Context *c, const clang::QualType &qt, const clang::SourceLocation &source_loc) { + const clang::Type *ty = qt.getTypePtr(); switch (ty->getTypeClass()) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: return 8; - case BuiltinType::UInt128: - case BuiltinType::Int128: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Int128: return 128; default: return 0; } zig_unreachable(); } - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); const char *type_name = decl_name(typedef_decl); if (strcmp(type_name, "uint8_t") == 0 || strcmp(type_name, "int8_t") == 0) { return 8; @@ -613,8 +638,8 @@ static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const So } -static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, - const SourceLocation &source_loc) +static AstNode *qual_type_to_log2_int_ref(Context *c, const clang::QualType &qt, + const clang::SourceLocation &source_loc) { uint32_t int_bit_width = qual_type_int_bit_width(c, qt, source_loc); if (int_bit_width != 0) { @@ -632,7 +657,7 @@ static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, // FieldAccess // FnCall (.builtin = true) // Symbol "import" -// StringLiteral "std" +// clang::StringLiteral "std" // Symbol "math" // Symbol "Log2Int" // zig_type_node @@ -646,21 +671,21 @@ static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, return log2int_fn_call; } -static bool qual_type_child_is_fn_proto(const QualType &qt) { - if (qt.getTypePtr()->getTypeClass() == Type::Paren) { - const ParenType *paren_type = static_cast(qt.getTypePtr()); - if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { +static bool qual_type_child_is_fn_proto(const clang::QualType &qt) { + if (qt.getTypePtr()->getTypeClass() == clang::Type::Paren) { + const clang::ParenType *paren_type = static_cast(qt.getTypePtr()); + if (paren_type->getInnerType()->getTypeClass() == clang::Type::FunctionProto) { return true; } - } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) { - const AttributedType *attr_type = static_cast(qt.getTypePtr()); + } else if (qt.getTypePtr()->getTypeClass() == clang::Type::Attributed) { + const clang::AttributedType *attr_type = static_cast(qt.getTypePtr()); return qual_type_child_is_fn_proto(attr_type->getEquivalentType()); } return false; } -static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, QualType dest_type, - QualType src_type, AstNode *expr) +static AstNode* trans_c_cast(Context *c, const clang::SourceLocation &source_location, clang::QualType dest_type, + clang::QualType src_type, AstNode *expr) { if (qual_types_equal(dest_type, src_type)) { return expr; @@ -677,72 +702,72 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, 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, QualType qt) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_signed_integer(Context *c, clang::QualType qt) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::SChar: - case BuiltinType::Short: - case BuiltinType::Int: - case BuiltinType::Long: - case BuiltinType::LongLong: - case BuiltinType::Int128: - case BuiltinType::WChar_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + case clang::BuiltinType::WChar_S: return true; default: return false; } } -static bool c_is_unsigned_integer(Context *c, QualType qt) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_unsigned_integer(Context *c, clang::QualType qt) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::UShort: - case BuiltinType::UInt: - case BuiltinType::ULong: - case BuiltinType::ULongLong: - case BuiltinType::UInt128: - case BuiltinType::WChar_U: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::WChar_U: return true; default: return false; } } -static bool c_is_builtin_type(Context *c, QualType qt, BuiltinType::Kind kind) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_builtin_type(Context *c, clang::QualType qt, clang::BuiltinType::Kind kind) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); return builtin_ty->getKind() == kind; } -static bool c_is_float(Context *c, QualType qt) { - const Type *c_type = qt.getTypePtr(); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_float(Context *c, clang::QualType qt) { + const clang::Type *c_type = qt.getTypePtr(); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::Half: - case BuiltinType::Float: - case BuiltinType::Double: - case BuiltinType::Float128: - case BuiltinType::LongDouble: + case clang::BuiltinType::Half: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::Float128: + case clang::BuiltinType::LongDouble: return true; default: return false; } } -static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { +static bool qual_type_has_wrapping_overflow(Context *c, clang::QualType qt) { if (c_is_signed_integer(c, qt) || c_is_float(c, qt)) { // float and signed integer overflow is undefined behavior. return false; @@ -752,23 +777,23 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { } } -static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) { +static bool type_is_opaque(Context *c, const clang::Type *ty, const clang::SourceLocation &source_loc) { switch (ty->getTypeClass()) { - case Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); - return builtin_ty->getKind() == BuiltinType::Void; + case clang::Type::Builtin: { + const clang::BuiltinType *builtin_ty = static_cast(ty); + return builtin_ty->getKind() == clang::BuiltinType::Void; } - case Type::Record: { - const RecordType *record_ty = static_cast(ty); + case clang::Type::Record: { + const clang::RecordType *record_ty = static_cast(ty); return record_ty->getDecl()->getDefinition() == nullptr; } - case Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + case clang::Type::Elaborated: { + const clang::ElaboratedType *elaborated_ty = static_cast(ty); return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc); } - case Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + case clang::Type::Typedef: { + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc); } default: @@ -776,145 +801,145 @@ static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &sou } } -static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { +static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::SourceLocation &source_loc) { switch (ty->getTypeClass()) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Void: + case clang::BuiltinType::Void: return trans_create_node_symbol_str(c, "c_void"); - case BuiltinType::Bool: + case clang::BuiltinType::Bool: return trans_create_node_symbol_str(c, "bool"); - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::Char8: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::Char8: return trans_create_node_symbol_str(c, "u8"); - case BuiltinType::SChar: + case clang::BuiltinType::SChar: return trans_create_node_symbol_str(c, "i8"); - case BuiltinType::UShort: + case clang::BuiltinType::UShort: return trans_create_node_symbol_str(c, "c_ushort"); - case BuiltinType::UInt: + case clang::BuiltinType::UInt: return trans_create_node_symbol_str(c, "c_uint"); - case BuiltinType::ULong: + case clang::BuiltinType::ULong: return trans_create_node_symbol_str(c, "c_ulong"); - case BuiltinType::ULongLong: + case clang::BuiltinType::ULongLong: return trans_create_node_symbol_str(c, "c_ulonglong"); - case BuiltinType::Short: + case clang::BuiltinType::Short: return trans_create_node_symbol_str(c, "c_short"); - case BuiltinType::Int: + case clang::BuiltinType::Int: return trans_create_node_symbol_str(c, "c_int"); - case BuiltinType::Long: + case clang::BuiltinType::Long: return trans_create_node_symbol_str(c, "c_long"); - case BuiltinType::LongLong: + case clang::BuiltinType::LongLong: return trans_create_node_symbol_str(c, "c_longlong"); - case BuiltinType::UInt128: + case clang::BuiltinType::UInt128: return trans_create_node_symbol_str(c, "u128"); - case BuiltinType::Int128: + case clang::BuiltinType::Int128: return trans_create_node_symbol_str(c, "i128"); - case BuiltinType::Float: + case clang::BuiltinType::Float: return trans_create_node_symbol_str(c, "f32"); - case BuiltinType::Double: + case clang::BuiltinType::Double: return trans_create_node_symbol_str(c, "f64"); - case BuiltinType::Float128: + case clang::BuiltinType::Float128: return trans_create_node_symbol_str(c, "f128"); - case BuiltinType::Float16: + case clang::BuiltinType::Float16: return trans_create_node_symbol_str(c, "f16"); - case BuiltinType::LongDouble: + case clang::BuiltinType::LongDouble: return trans_create_node_symbol_str(c, "c_longdouble"); - case BuiltinType::WChar_U: - case BuiltinType::Char16: - case BuiltinType::Char32: - case BuiltinType::WChar_S: - case BuiltinType::Half: - case BuiltinType::NullPtr: - case BuiltinType::ObjCId: - case BuiltinType::ObjCClass: - case BuiltinType::ObjCSel: - case BuiltinType::OMPArraySection: - case BuiltinType::Dependent: - case BuiltinType::Overload: - case BuiltinType::BoundMember: - case BuiltinType::PseudoObject: - case BuiltinType::UnknownAny: - case BuiltinType::BuiltinFn: - case BuiltinType::ARCUnbridgedCast: - case BuiltinType::ShortAccum: - case BuiltinType::Accum: - case BuiltinType::LongAccum: - case BuiltinType::UShortAccum: - case BuiltinType::UAccum: - case BuiltinType::ULongAccum: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Half: + case clang::BuiltinType::NullPtr: + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + case clang::BuiltinType::ObjCSel: + case clang::BuiltinType::OMPArraySection: + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::BuiltinFn: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::ShortAccum: + case clang::BuiltinType::Accum: + case clang::BuiltinType::LongAccum: + case clang::BuiltinType::UShortAccum: + case clang::BuiltinType::UAccum: + case clang::BuiltinType::ULongAccum: - case BuiltinType::OCLImage1dRO: - case BuiltinType::OCLImage1dArrayRO: - case BuiltinType::OCLImage1dBufferRO: - case BuiltinType::OCLImage2dRO: - case BuiltinType::OCLImage2dArrayRO: - case BuiltinType::OCLImage2dDepthRO: - case BuiltinType::OCLImage2dArrayDepthRO: - case BuiltinType::OCLImage2dMSAARO: - case BuiltinType::OCLImage2dArrayMSAARO: - case BuiltinType::OCLImage2dMSAADepthRO: - case BuiltinType::OCLImage2dArrayMSAADepthRO: - case BuiltinType::OCLImage3dRO: - case BuiltinType::OCLImage1dWO: - case BuiltinType::OCLImage1dArrayWO: - case BuiltinType::OCLImage1dBufferWO: - case BuiltinType::OCLImage2dWO: - case BuiltinType::OCLImage2dArrayWO: - case BuiltinType::OCLImage2dDepthWO: - case BuiltinType::OCLImage2dArrayDepthWO: - case BuiltinType::OCLImage2dMSAAWO: - case BuiltinType::OCLImage2dArrayMSAAWO: - case BuiltinType::OCLImage2dMSAADepthWO: - case BuiltinType::OCLImage2dArrayMSAADepthWO: - case BuiltinType::OCLImage3dWO: - case BuiltinType::OCLImage1dRW: - case BuiltinType::OCLImage1dArrayRW: - case BuiltinType::OCLImage1dBufferRW: - case BuiltinType::OCLImage2dRW: - case BuiltinType::OCLImage2dArrayRW: - case BuiltinType::OCLImage2dDepthRW: - case BuiltinType::OCLImage2dArrayDepthRW: - case BuiltinType::OCLImage2dMSAARW: - case BuiltinType::OCLImage2dArrayMSAARW: - case BuiltinType::OCLImage2dMSAADepthRW: - case BuiltinType::OCLImage2dArrayMSAADepthRW: - case BuiltinType::OCLImage3dRW: - case BuiltinType::OCLSampler: - case BuiltinType::OCLEvent: - case BuiltinType::OCLClkEvent: - case BuiltinType::OCLQueue: - case BuiltinType::OCLReserveID: - case BuiltinType::ShortFract: - case BuiltinType::Fract: - case BuiltinType::LongFract: - case BuiltinType::UShortFract: - case BuiltinType::UFract: - case BuiltinType::ULongFract: - case BuiltinType::SatShortAccum: - case BuiltinType::SatAccum: - case BuiltinType::SatLongAccum: - case BuiltinType::SatUShortAccum: - case BuiltinType::SatUAccum: - case BuiltinType::SatULongAccum: - case BuiltinType::SatShortFract: - case BuiltinType::SatFract: - case BuiltinType::SatLongFract: - case BuiltinType::SatUShortFract: - case BuiltinType::SatUFract: - case BuiltinType::SatULongFract: + case clang::BuiltinType::OCLImage1dRO: + case clang::BuiltinType::OCLImage1dArrayRO: + case clang::BuiltinType::OCLImage1dBufferRO: + case clang::BuiltinType::OCLImage2dRO: + case clang::BuiltinType::OCLImage2dArrayRO: + case clang::BuiltinType::OCLImage2dDepthRO: + case clang::BuiltinType::OCLImage2dArrayDepthRO: + case clang::BuiltinType::OCLImage2dMSAARO: + case clang::BuiltinType::OCLImage2dArrayMSAARO: + case clang::BuiltinType::OCLImage2dMSAADepthRO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRO: + case clang::BuiltinType::OCLImage3dRO: + case clang::BuiltinType::OCLImage1dWO: + case clang::BuiltinType::OCLImage1dArrayWO: + case clang::BuiltinType::OCLImage1dBufferWO: + case clang::BuiltinType::OCLImage2dWO: + case clang::BuiltinType::OCLImage2dArrayWO: + case clang::BuiltinType::OCLImage2dDepthWO: + case clang::BuiltinType::OCLImage2dArrayDepthWO: + case clang::BuiltinType::OCLImage2dMSAAWO: + case clang::BuiltinType::OCLImage2dArrayMSAAWO: + case clang::BuiltinType::OCLImage2dMSAADepthWO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthWO: + case clang::BuiltinType::OCLImage3dWO: + case clang::BuiltinType::OCLImage1dRW: + case clang::BuiltinType::OCLImage1dArrayRW: + case clang::BuiltinType::OCLImage1dBufferRW: + case clang::BuiltinType::OCLImage2dRW: + case clang::BuiltinType::OCLImage2dArrayRW: + case clang::BuiltinType::OCLImage2dDepthRW: + case clang::BuiltinType::OCLImage2dArrayDepthRW: + case clang::BuiltinType::OCLImage2dMSAARW: + case clang::BuiltinType::OCLImage2dArrayMSAARW: + case clang::BuiltinType::OCLImage2dMSAADepthRW: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRW: + case clang::BuiltinType::OCLImage3dRW: + case clang::BuiltinType::OCLSampler: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLClkEvent: + case clang::BuiltinType::OCLQueue: + case clang::BuiltinType::OCLReserveID: + case clang::BuiltinType::ShortFract: + case clang::BuiltinType::Fract: + case clang::BuiltinType::LongFract: + case clang::BuiltinType::UShortFract: + case clang::BuiltinType::UFract: + case clang::BuiltinType::ULongFract: + case clang::BuiltinType::SatShortAccum: + case clang::BuiltinType::SatAccum: + case clang::BuiltinType::SatLongAccum: + case clang::BuiltinType::SatUShortAccum: + case clang::BuiltinType::SatUAccum: + case clang::BuiltinType::SatULongAccum: + case clang::BuiltinType::SatShortFract: + case clang::BuiltinType::SatFract: + case clang::BuiltinType::SatLongFract: + case clang::BuiltinType::SatUShortFract: + case clang::BuiltinType::SatUFract: + case clang::BuiltinType::SatULongFract: emit_warning(c, source_loc, "unsupported builtin type"); return nullptr; } break; } - case Type::Pointer: + case clang::Type::Pointer: { - const PointerType *pointer_ty = static_cast(ty); - QualType child_qt = pointer_ty->getPointeeType(); + const clang::PointerType *pointer_ty = static_cast(ty); + clang::QualType child_qt = pointer_ty->getPointeeType(); AstNode *child_node = trans_qual_type(c, child_qt, source_loc); if (child_node == nullptr) { emit_warning(c, source_loc, "pointer to unsupported type"); @@ -925,90 +950,93 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); } - PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; - - AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_node, ptr_len); - return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); + if (type_is_opaque(c, child_qt.getTypePtr(), source_loc)) { + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), + child_qt.isVolatileQualified(), child_node, PtrLenSingle); + return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); + } else { + return trans_create_node_ptr_type(c, child_qt.isConstQualified(), + child_qt.isVolatileQualified(), child_node, PtrLenC); + } } - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); return resolve_typedef_decl(c, typedef_decl); } - case Type::Elaborated: + case clang::Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + const clang::ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { - case ETK_Struct: - case ETK_Enum: - case ETK_Union: + case clang::ETK_Struct: + case clang::ETK_Enum: + case clang::ETK_Union: return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc); - case ETK_Interface: - case ETK_Class: - case ETK_Typename: - case ETK_None: + case clang::ETK_Interface: + case clang::ETK_Class: + case clang::ETK_Typename: + case clang::ETK_None: emit_warning(c, source_loc, "unsupported elaborated type"); return nullptr; } } - case Type::FunctionProto: + case clang::Type::FunctionProto: { - const FunctionProtoType *fn_proto_ty = static_cast(ty); + const clang::FunctionProtoType *fn_proto_ty = static_cast(ty); AstNode *proto_node = trans_create_node(c, NodeTypeFnProto); switch (fn_proto_ty->getCallConv()) { - case CC_C: // __attribute__((cdecl)) + case clang::CC_C: // __attribute__((cdecl)) proto_node->data.fn_proto.cc = CallingConventionC; proto_node->data.fn_proto.is_extern = true; break; - case CC_X86StdCall: // __attribute__((stdcall)) + case clang::CC_X86StdCall: // __attribute__((stdcall)) proto_node->data.fn_proto.cc = CallingConventionStdcall; break; - case CC_X86FastCall: // __attribute__((fastcall)) + case clang::CC_X86FastCall: // __attribute__((fastcall)) emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall"); return nullptr; - case CC_X86ThisCall: // __attribute__((thiscall)) + case clang::CC_X86ThisCall: // __attribute__((thiscall)) emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall"); return nullptr; - case CC_X86VectorCall: // __attribute__((vectorcall)) + case clang::CC_X86VectorCall: // __attribute__((vectorcall)) emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall"); return nullptr; - case CC_X86Pascal: // __attribute__((pascal)) + case clang::CC_X86Pascal: // __attribute__((pascal)) emit_warning(c, source_loc, "unsupported calling convention: x86 pascal"); return nullptr; - case CC_Win64: // __attribute__((ms_abi)) + case clang::CC_Win64: // __attribute__((ms_abi)) emit_warning(c, source_loc, "unsupported calling convention: win64"); return nullptr; - case CC_X86_64SysV: // __attribute__((sysv_abi)) + case clang::CC_X86_64SysV: // __attribute__((sysv_abi)) emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv"); return nullptr; - case CC_X86RegCall: + case clang::CC_X86RegCall: emit_warning(c, source_loc, "unsupported calling convention: x86 reg"); return nullptr; - case CC_AAPCS: // __attribute__((pcs("aapcs"))) + case clang::CC_AAPCS: // __attribute__((pcs("aapcs"))) emit_warning(c, source_loc, "unsupported calling convention: aapcs"); return nullptr; - case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) + case clang::CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp"); return nullptr; - case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) + case clang::CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc"); return nullptr; - case CC_SpirFunction: // default for OpenCL functions on SPIR target + case clang::CC_SpirFunction: // default for OpenCL functions on SPIR target emit_warning(c, source_loc, "unsupported calling convention: SPIR function"); return nullptr; - case CC_OpenCLKernel: + case clang::CC_OpenCLKernel: emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel"); return nullptr; - case CC_Swift: + case clang::CC_Swift: emit_warning(c, source_loc, "unsupported calling convention: Swift"); return nullptr; - case CC_PreserveMost: + case clang::CC_PreserveMost: emit_warning(c, source_loc, "unsupported calling convention: PreserveMost"); return nullptr; - case CC_PreserveAll: + case clang::CC_PreserveAll: emit_warning(c, source_loc, "unsupported calling convention: PreserveAll"); return nullptr; } @@ -1026,7 +1054,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return nullptr; } // convert c_void to actual void (only for return type) - // we do want to look at the AstNode instead of QualType, because + // we do want to look at the AstNode instead of clang::QualType, because // if they do something like: // typedef Foo void; // void foo(void) -> Foo; @@ -1043,7 +1071,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou } for (size_t i = 0; i < param_count; i += 1) { - QualType qt = fn_proto_ty->getParamType(i); + clang::QualType qt = fn_proto_ty->getParamType(i); AstNode *param_type_node = trans_qual_type(c, qt, source_loc); if (param_type_node == nullptr) { @@ -1066,19 +1094,19 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return proto_node; } - case Type::Record: + case clang::Type::Record: { - const RecordType *record_ty = static_cast(ty); + const clang::RecordType *record_ty = static_cast(ty); return resolve_record_decl(c, record_ty->getDecl()); } - case Type::Enum: + case clang::Type::Enum: { - const EnumType *enum_ty = static_cast(ty); + const clang::EnumType *enum_ty = static_cast(ty); return resolve_enum_decl(c, enum_ty->getDecl()); } - case Type::ConstantArray: + case clang::Type::ConstantArray: { - const ConstantArrayType *const_arr_ty = static_cast(ty); + const clang::ConstantArrayType *const_arr_ty = static_cast(ty); AstNode *child_type_node = trans_qual_type(c, const_arr_ty->getElementType(), source_loc); if (child_type_node == nullptr) { emit_warning(c, source_loc, "unresolved array element type"); @@ -1088,84 +1116,84 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou AstNode *size_node = trans_create_node_unsigned(c, size); return trans_create_node_array_type(c, size_node, child_type_node); } - case Type::Paren: + case clang::Type::Paren: { - const ParenType *paren_ty = static_cast(ty); + const clang::ParenType *paren_ty = static_cast(ty); return trans_qual_type(c, paren_ty->getInnerType(), source_loc); } - case Type::Decayed: + case clang::Type::Decayed: { - const DecayedType *decayed_ty = static_cast(ty); + const clang::DecayedType *decayed_ty = static_cast(ty); return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc); } - case Type::Attributed: + case clang::Type::Attributed: { - const AttributedType *attributed_ty = static_cast(ty); + const clang::AttributedType *attributed_ty = static_cast(ty); return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc); } - case Type::IncompleteArray: + case clang::Type::IncompleteArray: { - const IncompleteArrayType *incomplete_array_ty = static_cast(ty); - QualType child_qt = incomplete_array_ty->getElementType(); + const clang::IncompleteArrayType *incomplete_array_ty = static_cast(ty); + clang::QualType child_qt = incomplete_array_ty->getElementType(); 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, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown); + child_qt.isVolatileQualified(), child_type_node, PtrLenC); return pointer_node; } - case Type::BlockPointer: - case Type::LValueReference: - case Type::RValueReference: - case Type::MemberPointer: - case Type::VariableArray: - case Type::DependentSizedArray: - case Type::DependentSizedExtVector: - case Type::Vector: - case Type::ExtVector: - case Type::FunctionNoProto: - case Type::UnresolvedUsing: - case Type::Adjusted: - case Type::TypeOfExpr: - case Type::TypeOf: - case Type::Decltype: - case Type::UnaryTransform: - case Type::TemplateTypeParm: - case Type::SubstTemplateTypeParm: - case Type::SubstTemplateTypeParmPack: - case Type::TemplateSpecialization: - case Type::Auto: - case Type::InjectedClassName: - case Type::DependentName: - case Type::DependentTemplateSpecialization: - case Type::PackExpansion: - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::Complex: - case Type::ObjCObjectPointer: - case Type::Atomic: - case Type::Pipe: - case Type::ObjCTypeParam: - case Type::DeducedTemplateSpecialization: - case Type::DependentAddressSpace: - case Type::DependentVector: + case clang::Type::BlockPointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: + case clang::Type::VariableArray: + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::Vector: + case clang::Type::ExtVector: + case clang::Type::FunctionNoProto: + case clang::Type::UnresolvedUsing: + case clang::Type::Adjusted: + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::UnaryTransform: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::TemplateSpecialization: + case clang::Type::Auto: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + case clang::Type::Complex: + case clang::Type::ObjCObjectPointer: + case clang::Type::Atomic: + case clang::Type::Pipe: + case clang::Type::ObjCTypeParam: + case clang::Type::DeducedTemplateSpecialization: + case clang::Type::DependentAddressSpace: + case clang::Type::DependentVector: emit_warning(c, source_loc, "unsupported type: '%s'", ty->getTypeClassName()); return nullptr; } zig_unreachable(); } -static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc) { +static AstNode *trans_qual_type(Context *c, clang::QualType qt, const clang::SourceLocation &source_loc) { return trans_type(c, qt.getTypePtr(), source_loc); } -static int trans_compound_stmt_inline(Context *c, TransScope *scope, const CompoundStmt *stmt, +static int trans_compound_stmt_inline(Context *c, TransScope *scope, const clang::CompoundStmt *stmt, AstNode *block_node, TransScope **out_node_scope) { assert(block_node->type == NodeTypeBlock); - for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) { + for (clang::CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) { AstNode *child_node; scope = trans_stmt(c, scope, *it, &child_node); if (scope == nullptr) @@ -1179,7 +1207,7 @@ static int trans_compound_stmt_inline(Context *c, TransScope *scope, const Compo return ErrorNone; } -static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt, +static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const clang::CompoundStmt *stmt, TransScope **out_node_scope) { TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope); @@ -1188,8 +1216,8 @@ static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const Compoun return child_scope_block->node; } -static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStmt *stmt) { - const Expr *value_expr = stmt->getRetValue(); +static AstNode *trans_return_stmt(Context *c, TransScope *scope, const clang::ReturnStmt *stmt) { + const clang::Expr *value_expr = stmt->getRetValue(); if (value_expr == nullptr) { return trans_create_node(c, NodeTypeReturnExpr); } else { @@ -1201,9 +1229,9 @@ static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStm } } -static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) { +static AstNode *trans_integer_literal(Context *c, const clang::IntegerLiteral *stmt) { llvm::APSInt result; - if (!stmt->EvaluateAsInt(result, *c->ctx)) { + if (!stmt->EvaluateAsInt(result, *reinterpret_cast(c->ctx))) { emit_warning(c, stmt->getLocStart(), "invalid integer literal"); return nullptr; } @@ -1211,13 +1239,13 @@ static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) { } static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ConditionalOperator *stmt) + const clang::ConditionalOperator *stmt) { AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr); - Expr *cond_expr = stmt->getCond(); - Expr *true_expr = stmt->getTrueExpr(); - Expr *false_expr = stmt->getFalseExpr(); + clang::Expr *cond_expr = stmt->getCond(); + clang::Expr *true_expr = stmt->getTrueExpr(); + clang::Expr *false_expr = stmt->getFalseExpr(); node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue); if (node->data.if_bool_expr.condition == nullptr) @@ -1234,7 +1262,7 @@ static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, T return maybe_suppress_result(c, result_used, node); } -static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) { +static AstNode *trans_create_bin_op(Context *c, TransScope *scope, clang::Expr *lhs, BinOpType bin_op, clang::Expr *rhs) { AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); node->data.bin_op_expr.bin_op = bin_op; @@ -1249,7 +1277,7 @@ static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, Bi return node; } -static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) { +static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, clang::Expr *lhs, BinOpType bin_op, clang::Expr *rhs) { assert(bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeBoolOr); AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); node->data.bin_op_expr.bin_op = bin_op; @@ -1265,7 +1293,7 @@ static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lh return node; } -static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) { +static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, clang::Expr *lhs, clang::Expr *rhs) { if (result_used == ResultUsedNo) { // common case AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); @@ -1316,10 +1344,10 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco } } -static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType result_type, - Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) +static AstNode *trans_create_shift_op(Context *c, TransScope *scope, clang::QualType result_type, + clang::Expr *lhs_expr, BinOpType bin_op, clang::Expr *rhs_expr) { - const SourceLocation &rhs_location = rhs_expr->getLocStart(); + const clang::SourceLocation &rhs_location = rhs_expr->getLocStart(); AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location); // lhs >> u5(rh) @@ -1333,22 +1361,22 @@ static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType re 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 BinaryOperator *stmt) { +static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const clang::BinaryOperator *stmt) { switch (stmt->getOpcode()) { - case BO_PtrMemD: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD"); + case clang::BO_PtrMemD: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_PtrMemD"); return nullptr; - case BO_PtrMemI: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI"); + case clang::BO_PtrMemI: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_PtrMemI"); return nullptr; - case BO_Cmp: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Cmp"); + case clang::BO_Cmp: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_Cmp"); return nullptr; - case BO_Mul: + case clang::BO_Mul: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult, stmt->getRHS()); - case BO_Div: + case clang::BO_Div: if (qual_type_has_wrapping_overflow(c, stmt->getType())) { // unsigned/float division uses the operator return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS()); @@ -1363,7 +1391,7 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS fn_call->data.fn_call_expr.params.append(rhs); return fn_call; } - case BO_Rem: + case clang::BO_Rem: if (qual_type_has_wrapping_overflow(c, stmt->getType())) { // unsigned/float division uses the operator return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeMod, stmt->getRHS()); @@ -1378,43 +1406,43 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS fn_call->data.fn_call_expr.params.append(rhs); return fn_call; } - case BO_Add: + case clang::BO_Add: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd, stmt->getRHS()); - case BO_Sub: + case clang::BO_Sub: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub, stmt->getRHS()); - case BO_Shl: + case clang::BO_Shl: return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS()); - case BO_Shr: + case clang::BO_Shr: return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS()); - case BO_LT: + case clang::BO_LT: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS()); - case BO_GT: + case clang::BO_GT: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS()); - case BO_LE: + case clang::BO_LE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS()); - case BO_GE: + case clang::BO_GE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS()); - case BO_EQ: + case clang::BO_EQ: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS()); - case BO_NE: + case clang::BO_NE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS()); - case BO_And: + case clang::BO_And: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS()); - case BO_Xor: + case clang::BO_Xor: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS()); - case BO_Or: + case clang::BO_Or: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS()); - case BO_LAnd: + case clang::BO_LAnd: return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS()); - case BO_LOr: + case clang::BO_LOr: return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); - case BO_Assign: + case clang::BO_Assign: return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS()); - case BO_Comma: + case clang::BO_Comma: { TransScopeBlock *scope_block = trans_scope_block_create(c, scope); Buf *label_name = buf_create_from_str("x"); @@ -1431,16 +1459,16 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS scope_block->node->data.block.statements.append(trans_create_node_break(c, label_name, maybe_suppress_result(c, result_used, rhs))); return scope_block->node; } - case BO_MulAssign: - case BO_DivAssign: - case BO_RemAssign: - case BO_AddAssign: - case BO_SubAssign: - case BO_ShlAssign: - case BO_ShrAssign: - case BO_AndAssign: - case BO_XorAssign: - case BO_OrAssign: + case clang::BO_MulAssign: + case clang::BO_DivAssign: + case clang::BO_RemAssign: + case clang::BO_AddAssign: + case clang::BO_SubAssign: + case clang::BO_ShlAssign: + case clang::BO_ShrAssign: + case clang::BO_AndAssign: + case clang::BO_XorAssign: + case clang::BO_OrAssign: zig_unreachable(); } @@ -1448,9 +1476,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS } static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) + const clang::CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { - const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); + const clang::SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); @@ -1530,7 +1558,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result } static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) + const clang::CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { if (result_used == ResultUsedNo) { // simple common case, where the C and Zig are identical: @@ -1590,76 +1618,76 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt) + const clang::CompoundAssignOperator *stmt) { switch (stmt->getOpcode()) { - case BO_MulAssign: + case clang::BO_MulAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) 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 BO_DivAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign"); + case clang::BO_DivAssign: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_DivAssign"); return nullptr; - case BO_RemAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign"); + case clang::BO_RemAssign: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_RemAssign"); return nullptr; - case BO_Cmp: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_Cmp"); + case clang::BO_Cmp: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_Cmp"); return nullptr; - case BO_AddAssign: + case clang::BO_AddAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) 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 BO_SubAssign: + case clang::BO_SubAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) 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 BO_ShlAssign: + case clang::BO_ShlAssign: return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft); - case BO_ShrAssign: + case clang::BO_ShrAssign: return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight); - case BO_AndAssign: + case clang::BO_AndAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd); - case BO_XorAssign: + case clang::BO_XorAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor); - case BO_OrAssign: + case clang::BO_OrAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr); - case BO_PtrMemD: - case BO_PtrMemI: - case BO_Assign: - case BO_Mul: - case BO_Div: - case BO_Rem: - case BO_Add: - case BO_Sub: - case BO_Shl: - case BO_Shr: - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - case BO_And: - case BO_Xor: - case BO_Or: - case BO_LAnd: - case BO_LOr: - case BO_Comma: + case clang::BO_PtrMemD: + case clang::BO_PtrMemI: + case clang::BO_Assign: + case clang::BO_Mul: + case clang::BO_Div: + case clang::BO_Rem: + case clang::BO_Add: + case clang::BO_Sub: + case clang::BO_Shl: + case clang::BO_Shr: + case clang::BO_LT: + case clang::BO_GT: + case clang::BO_LE: + case clang::BO_GE: + case clang::BO_EQ: + case clang::BO_NE: + case clang::BO_And: + case clang::BO_Xor: + case clang::BO_Or: + case clang::BO_LAnd: + case clang::BO_LOr: + case clang::BO_Comma: zig_unreachable(); } zig_unreachable(); } -static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const ImplicitCastExpr *stmt) { +static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const clang::ImplicitCastExpr *stmt) { switch (stmt->getCastKind()) { - case CK_LValueToRValue: + case clang::CK_LValueToRValue: return trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); - case CK_IntegralCast: + case clang::CK_IntegralCast: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) @@ -1667,15 +1695,15 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(), stmt->getSubExpr()->getType(), target_node); } - case CK_FunctionToPointerDecay: - case CK_ArrayToPointerDecay: + case clang::CK_FunctionToPointerDecay: + case clang::CK_ArrayToPointerDecay: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) return nullptr; return target_node; } - case CK_BitCast: + case clang::CK_BitCast: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) @@ -1692,170 +1720,170 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im node->data.fn_call_expr.params.append(target_node); return node; } - case CK_NullToPointer: - return trans_create_node(c, NodeTypeNullLiteral); - case CK_Dependent: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent"); + case clang::CK_NullToPointer: + return trans_create_node_unsigned(c, 0); + case clang::CK_Dependent: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_Dependent"); return nullptr; - case CK_LValueBitCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_LValueBitCast"); + case clang::CK_LValueBitCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_LValueBitCast"); return nullptr; - case CK_NoOp: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NoOp"); + case clang::CK_NoOp: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NoOp"); return nullptr; - case CK_BaseToDerived: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerived"); + case clang::CK_BaseToDerived: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BaseToDerived"); return nullptr; - case CK_DerivedToBase: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBase"); + case clang::CK_DerivedToBase: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_DerivedToBase"); return nullptr; - case CK_UncheckedDerivedToBase: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UncheckedDerivedToBase"); + case clang::CK_UncheckedDerivedToBase: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_UncheckedDerivedToBase"); return nullptr; - case CK_Dynamic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dynamic"); + case clang::CK_Dynamic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_Dynamic"); return nullptr; - case CK_ToUnion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion"); + case clang::CK_ToUnion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ToUnion"); return nullptr; - case CK_NullToMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer"); + case clang::CK_NullToMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NullToMemberPointer"); return nullptr; - case CK_BaseToDerivedMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerivedMemberPointer"); + case clang::CK_BaseToDerivedMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BaseToDerivedMemberPointer"); return nullptr; - case CK_DerivedToBaseMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBaseMemberPointer"); + case clang::CK_DerivedToBaseMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_DerivedToBaseMemberPointer"); return nullptr; - case CK_MemberPointerToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_MemberPointerToBoolean"); + case clang::CK_MemberPointerToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_MemberPointerToBoolean"); return nullptr; - case CK_ReinterpretMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ReinterpretMemberPointer"); + case clang::CK_ReinterpretMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ReinterpretMemberPointer"); return nullptr; - case CK_UserDefinedConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UserDefinedConversion"); + case clang::CK_UserDefinedConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_UserDefinedConversion"); return nullptr; - case CK_ConstructorConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ConstructorConversion"); + case clang::CK_ConstructorConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ConstructorConversion"); return nullptr; - case CK_IntegralToPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToPointer"); + case clang::CK_IntegralToPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToPointer"); return nullptr; - case CK_PointerToIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToIntegral"); + case clang::CK_PointerToIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_PointerToIntegral"); return nullptr; - case CK_PointerToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToBoolean"); + case clang::CK_PointerToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_PointerToBoolean"); return nullptr; - case CK_ToVoid: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToVoid"); + case clang::CK_ToVoid: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ToVoid"); return nullptr; - case CK_VectorSplat: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_VectorSplat"); + case clang::CK_VectorSplat: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_VectorSplat"); return nullptr; - case CK_IntegralToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToBoolean"); + case clang::CK_IntegralToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToBoolean"); return nullptr; - case CK_IntegralToFloating: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToFloating"); + case clang::CK_IntegralToFloating: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToFloating"); return nullptr; - case CK_FloatingToIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToIntegral"); + case clang::CK_FloatingToIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingToIntegral"); return nullptr; - case CK_FloatingToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToBoolean"); + case clang::CK_FloatingToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingToBoolean"); return nullptr; - case CK_BooleanToSignedIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BooleanToSignedIntegral"); + case clang::CK_BooleanToSignedIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BooleanToSignedIntegral"); return nullptr; - case CK_FloatingCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingCast"); + case clang::CK_FloatingCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingCast"); return nullptr; - case CK_CPointerToObjCPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CPointerToObjCPointerCast"); + case clang::CK_CPointerToObjCPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_CPointerToObjCPointerCast"); return nullptr; - case CK_BlockPointerToObjCPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BlockPointerToObjCPointerCast"); + case clang::CK_BlockPointerToObjCPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BlockPointerToObjCPointerCast"); return nullptr; - case CK_AnyPointerToBlockPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AnyPointerToBlockPointerCast"); + case clang::CK_AnyPointerToBlockPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AnyPointerToBlockPointerCast"); return nullptr; - case CK_ObjCObjectLValueCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ObjCObjectLValueCast"); + case clang::CK_ObjCObjectLValueCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ObjCObjectLValueCast"); return nullptr; - case CK_FloatingRealToComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingRealToComplex"); + case clang::CK_FloatingRealToComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingRealToComplex"); return nullptr; - case CK_FloatingComplexToReal: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToReal"); + case clang::CK_FloatingComplexToReal: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToReal"); return nullptr; - case CK_FloatingComplexToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToBoolean"); + case clang::CK_FloatingComplexToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToBoolean"); return nullptr; - case CK_FloatingComplexCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexCast"); + case clang::CK_FloatingComplexCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexCast"); return nullptr; - case CK_FloatingComplexToIntegralComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToIntegralComplex"); + case clang::CK_FloatingComplexToIntegralComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToIntegralComplex"); return nullptr; - case CK_IntegralRealToComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralRealToComplex"); + case clang::CK_IntegralRealToComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralRealToComplex"); return nullptr; - case CK_IntegralComplexToReal: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToReal"); + case clang::CK_IntegralComplexToReal: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToReal"); return nullptr; - case CK_IntegralComplexToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToBoolean"); + case clang::CK_IntegralComplexToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToBoolean"); return nullptr; - case CK_IntegralComplexCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexCast"); + case clang::CK_IntegralComplexCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexCast"); return nullptr; - case CK_IntegralComplexToFloatingComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToFloatingComplex"); + case clang::CK_IntegralComplexToFloatingComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToFloatingComplex"); return nullptr; - case CK_ARCProduceObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCProduceObject"); + case clang::CK_ARCProduceObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCProduceObject"); return nullptr; - case CK_ARCConsumeObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCConsumeObject"); + case clang::CK_ARCConsumeObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCConsumeObject"); return nullptr; - case CK_ARCReclaimReturnedObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCReclaimReturnedObject"); + case clang::CK_ARCReclaimReturnedObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCReclaimReturnedObject"); return nullptr; - case CK_ARCExtendBlockObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCExtendBlockObject"); + case clang::CK_ARCExtendBlockObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCExtendBlockObject"); return nullptr; - case CK_AtomicToNonAtomic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AtomicToNonAtomic"); + case clang::CK_AtomicToNonAtomic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AtomicToNonAtomic"); return nullptr; - case CK_NonAtomicToAtomic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NonAtomicToAtomic"); + case clang::CK_NonAtomicToAtomic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NonAtomicToAtomic"); return nullptr; - case CK_CopyAndAutoreleaseBlockObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CopyAndAutoreleaseBlockObject"); + case clang::CK_CopyAndAutoreleaseBlockObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_CopyAndAutoreleaseBlockObject"); return nullptr; - case CK_BuiltinFnToFnPtr: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BuiltinFnToFnPtr"); + case clang::CK_BuiltinFnToFnPtr: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BuiltinFnToFnPtr"); return nullptr; - case CK_ZeroToOCLEvent: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLEvent"); + case clang::CK_ZeroToOCLEvent: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ZeroToOCLEvent"); return nullptr; - case CK_ZeroToOCLQueue: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLQueue"); + case clang::CK_ZeroToOCLQueue: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ZeroToOCLQueue"); return nullptr; - case CK_AddressSpaceConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AddressSpaceConversion"); + case clang::CK_AddressSpaceConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AddressSpaceConversion"); return nullptr; - case CK_IntToOCLSampler: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntToOCLSampler"); + case clang::CK_IntToOCLSampler: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntToOCLSampler"); return nullptr; } zig_unreachable(); } -static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRefExpr *stmt, TransLRValue lrval) { - const ValueDecl *value_decl = stmt->getDecl(); +static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const clang::DeclRefExpr *stmt, TransLRValue lrval) { + const clang::ValueDecl *value_decl = stmt->getDecl(); Buf *c_symbol_name = buf_create_from_str(decl_name(value_decl)); Buf *zig_symbol_name = trans_lookup_zig_symbol(c, scope, c_symbol_name); if (lrval == TransLValue) { @@ -1865,9 +1893,9 @@ static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRef } static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope, - const UnaryOperator *stmt, BinOpType assign_op) + const clang::UnaryOperator *stmt, BinOpType assign_op) { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (result_used == ResultUsedNo) { // common case @@ -1921,9 +1949,9 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr } static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope, - const UnaryOperator *stmt, BinOpType assign_op) + const clang::UnaryOperator *stmt, BinOpType assign_op) { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (result_used == ResultUsedNo) { // common case @@ -1970,36 +1998,36 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra return child_scope->node; } -static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const UnaryOperator *stmt) { +static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const clang::UnaryOperator *stmt) { switch (stmt->getOpcode()) { - case UO_PostInc: + case clang::UO_PostInc: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); else return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case UO_PostDec: + case clang::UO_PostDec: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); else return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case UO_PreInc: + case clang::UO_PreInc: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); else return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case UO_PreDec: + case clang::UO_PreDec: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); else return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case UO_AddrOf: + case clang::UO_AddrOf: { AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue); if (value_node == nullptr) return value_node; return trans_create_node_addr_of(c, value_node); } - case UO_Deref: + case clang::UO_Deref: { AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue); if (value_node == nullptr) @@ -2010,12 +2038,12 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node); return trans_create_node_ptr_deref(c, unwrapped); } - case UO_Plus: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); + case clang::UO_Plus: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Plus"); return nullptr; - case UO_Minus: + case clang::UO_Minus: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (!qual_type_has_wrapping_overflow(c, op_expr->getType())) { AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); node->data.prefix_op_expr.prefix_op = PrefixOpNegation; @@ -2041,41 +2069,41 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc return nullptr; } } - case UO_Not: + case clang::UO_Not: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); 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 UO_LNot: + case clang::UO_LNot: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); 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 UO_Real: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real"); + case clang::UO_Real: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Real"); return nullptr; - case UO_Imag: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Imag"); + case clang::UO_Imag: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Imag"); return nullptr; - case UO_Extension: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Extension"); + case clang::UO_Extension: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Extension"); return nullptr; - case UO_Coawait: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Coawait"); + case clang::UO_Coawait: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Coawait"); return nullptr; } zig_unreachable(); } -static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt *stmt, +static int trans_local_declaration(Context *c, TransScope *scope, const clang::DeclStmt *stmt, AstNode **out_node, TransScope **out_scope) { // declarations are added via the scope @@ -2085,11 +2113,11 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt assert(scope_block != nullptr); for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) { - Decl *decl = *iter; + clang::Decl *decl = *iter; switch (decl->getKind()) { - case Decl::Var: { - VarDecl *var_decl = (VarDecl *)decl; - QualType qual_type = var_decl->getTypeSourceInfo()->getType(); + case clang::Decl::Var: { + clang::VarDecl *var_decl = (clang::VarDecl *)decl; + clang::QualType qual_type = var_decl->getTypeSourceInfo()->getType(); AstNode *init_node = nullptr; if (var_decl->hasInit()) { init_node = trans_expr(c, ResultUsedYes, scope, var_decl->getInit(), TransRValue); @@ -2114,220 +2142,220 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt scope_block->node->data.block.statements.append(node); continue; } - case Decl::AccessSpec: + case clang::Decl::AccessSpec: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind AccessSpec"); return ErrorUnexpected; - case Decl::Block: + case clang::Decl::Block: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Block"); return ErrorUnexpected; - case Decl::Captured: + case clang::Decl::Captured: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Captured"); return ErrorUnexpected; - case Decl::ClassScopeFunctionSpecialization: + case clang::Decl::ClassScopeFunctionSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassScopeFunctionSpecialization"); return ErrorUnexpected; - case Decl::Empty: + case clang::Decl::Empty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Empty"); return ErrorUnexpected; - case Decl::Export: + case clang::Decl::Export: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Export"); return ErrorUnexpected; - case Decl::ExternCContext: + case clang::Decl::ExternCContext: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ExternCContext"); return ErrorUnexpected; - case Decl::FileScopeAsm: + case clang::Decl::FileScopeAsm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FileScopeAsm"); return ErrorUnexpected; - case Decl::Friend: + case clang::Decl::Friend: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Friend"); return ErrorUnexpected; - case Decl::FriendTemplate: + case clang::Decl::FriendTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FriendTemplate"); return ErrorUnexpected; - case Decl::Import: + case clang::Decl::Import: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Import"); return ErrorUnexpected; - case Decl::LinkageSpec: + case clang::Decl::LinkageSpec: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind LinkageSpec"); return ErrorUnexpected; - case Decl::Label: + case clang::Decl::Label: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Label"); return ErrorUnexpected; - case Decl::Namespace: + case clang::Decl::Namespace: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Namespace"); return ErrorUnexpected; - case Decl::NamespaceAlias: + case clang::Decl::NamespaceAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NamespaceAlias"); return ErrorUnexpected; - case Decl::ObjCCompatibleAlias: + case clang::Decl::ObjCCompatibleAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCompatibleAlias"); return ErrorUnexpected; - case Decl::ObjCCategory: + case clang::Decl::ObjCCategory: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategory"); return ErrorUnexpected; - case Decl::ObjCCategoryImpl: + case clang::Decl::ObjCCategoryImpl: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategoryImpl"); return ErrorUnexpected; - case Decl::ObjCImplementation: + case clang::Decl::ObjCImplementation: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCImplementation"); return ErrorUnexpected; - case Decl::ObjCInterface: + case clang::Decl::ObjCInterface: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCInterface"); return ErrorUnexpected; - case Decl::ObjCProtocol: + case clang::Decl::ObjCProtocol: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProtocol"); return ErrorUnexpected; - case Decl::ObjCMethod: + case clang::Decl::ObjCMethod: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCMethod"); return ErrorUnexpected; - case Decl::ObjCProperty: + case clang::Decl::ObjCProperty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProperty"); return ErrorUnexpected; - case Decl::BuiltinTemplate: + case clang::Decl::BuiltinTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind BuiltinTemplate"); return ErrorUnexpected; - case Decl::ClassTemplate: + case clang::Decl::ClassTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplate"); return ErrorUnexpected; - case Decl::FunctionTemplate: + case clang::Decl::FunctionTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FunctionTemplate"); return ErrorUnexpected; - case Decl::TypeAliasTemplate: + case clang::Decl::TypeAliasTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAliasTemplate"); return ErrorUnexpected; - case Decl::VarTemplate: + case clang::Decl::VarTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplate"); return ErrorUnexpected; - case Decl::TemplateTemplateParm: + case clang::Decl::TemplateTemplateParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTemplateParm"); return ErrorUnexpected; - case Decl::Enum: + case clang::Decl::Enum: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Enum"); return ErrorUnexpected; - case Decl::Record: + case clang::Decl::Record: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Record"); return ErrorUnexpected; - case Decl::CXXRecord: + case clang::Decl::CXXRecord: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXRecord"); return ErrorUnexpected; - case Decl::ClassTemplateSpecialization: + case clang::Decl::ClassTemplateSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplateSpecialization"); return ErrorUnexpected; - case Decl::ClassTemplatePartialSpecialization: + case clang::Decl::ClassTemplatePartialSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplatePartialSpecialization"); return ErrorUnexpected; - case Decl::TemplateTypeParm: + case clang::Decl::TemplateTypeParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTypeParm"); return ErrorUnexpected; - case Decl::ObjCTypeParam: + case clang::Decl::ObjCTypeParam: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCTypeParam"); return ErrorUnexpected; - case Decl::TypeAlias: + case clang::Decl::TypeAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAlias"); return ErrorUnexpected; - case Decl::Typedef: + case clang::Decl::Typedef: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Typedef"); return ErrorUnexpected; - case Decl::UnresolvedUsingTypename: + case clang::Decl::UnresolvedUsingTypename: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingTypename"); return ErrorUnexpected; - case Decl::Using: + case clang::Decl::Using: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Using"); return ErrorUnexpected; - case Decl::UsingDirective: + case clang::Decl::UsingDirective: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingDirective"); return ErrorUnexpected; - case Decl::UsingPack: + case clang::Decl::UsingPack: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingPack"); return ErrorUnexpected; - case Decl::UsingShadow: + case clang::Decl::UsingShadow: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingShadow"); return ErrorUnexpected; - case Decl::ConstructorUsingShadow: + case clang::Decl::ConstructorUsingShadow: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ConstructorUsingShadow"); return ErrorUnexpected; - case Decl::Binding: + case clang::Decl::Binding: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Binding"); return ErrorUnexpected; - case Decl::Field: + case clang::Decl::Field: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Field"); return ErrorUnexpected; - case Decl::ObjCAtDefsField: + case clang::Decl::ObjCAtDefsField: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCAtDefsField"); return ErrorUnexpected; - case Decl::ObjCIvar: + case clang::Decl::ObjCIvar: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCIvar"); return ErrorUnexpected; - case Decl::Function: + case clang::Decl::Function: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Function"); return ErrorUnexpected; - case Decl::CXXDeductionGuide: + case clang::Decl::CXXDeductionGuide: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDeductionGuide"); return ErrorUnexpected; - case Decl::CXXMethod: + case clang::Decl::CXXMethod: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXMethod"); return ErrorUnexpected; - case Decl::CXXConstructor: + case clang::Decl::CXXConstructor: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConstructor"); return ErrorUnexpected; - case Decl::CXXConversion: + case clang::Decl::CXXConversion: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConversion"); return ErrorUnexpected; - case Decl::CXXDestructor: + case clang::Decl::CXXDestructor: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDestructor"); return ErrorUnexpected; - case Decl::MSProperty: + case clang::Decl::MSProperty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind MSProperty"); return ErrorUnexpected; - case Decl::NonTypeTemplateParm: + case clang::Decl::NonTypeTemplateParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NonTypeTemplateParm"); return ErrorUnexpected; - case Decl::Decomposition: + case clang::Decl::Decomposition: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Decomposition"); return ErrorUnexpected; - case Decl::ImplicitParam: + case clang::Decl::ImplicitParam: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ImplicitParam"); return ErrorUnexpected; - case Decl::OMPCapturedExpr: + case clang::Decl::OMPCapturedExpr: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPCapturedExpr"); return ErrorUnexpected; - case Decl::ParmVar: + case clang::Decl::ParmVar: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ParmVar"); return ErrorUnexpected; - case Decl::VarTemplateSpecialization: + case clang::Decl::VarTemplateSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplateSpecialization"); return ErrorUnexpected; - case Decl::VarTemplatePartialSpecialization: + case clang::Decl::VarTemplatePartialSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplatePartialSpecialization"); return ErrorUnexpected; - case Decl::EnumConstant: + case clang::Decl::EnumConstant: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind EnumConstant"); return ErrorUnexpected; - case Decl::IndirectField: + case clang::Decl::IndirectField: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind IndirectField"); return ErrorUnexpected; - case Decl::OMPDeclareReduction: + case clang::Decl::OMPDeclareReduction: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPDeclareReduction"); return ErrorUnexpected; - case Decl::UnresolvedUsingValue: + case clang::Decl::UnresolvedUsingValue: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingValue"); return ErrorUnexpected; - case Decl::OMPThreadPrivate: + case clang::Decl::OMPThreadPrivate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPThreadPrivate"); return ErrorUnexpected; - case Decl::ObjCPropertyImpl: + case clang::Decl::ObjCPropertyImpl: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCPropertyImpl"); return ErrorUnexpected; - case Decl::PragmaComment: + case clang::Decl::PragmaComment: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaComment"); return ErrorUnexpected; - case Decl::PragmaDetectMismatch: + case clang::Decl::PragmaDetectMismatch: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaDetectMismatch"); return ErrorUnexpected; - case Decl::StaticAssert: + case clang::Decl::StaticAssert: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind StaticAssert"); return ErrorUnexpected; - case Decl::TranslationUnit: + case clang::Decl::TranslationUnit: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TranslationUnit"); return ErrorUnexpected; } @@ -2354,7 +2382,7 @@ static AstNode *to_enum_zero_cmp(Context *c, AstNode *expr, AstNode *enum_type) return trans_create_node_bin_op(c, expr, BinOpTypeCmpNotEq, bitcast); } -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval) { +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval) { AstNode *res = trans_expr(c, result_used, scope, expr, lrval); if (res == nullptr) return nullptr; @@ -2391,131 +2419,133 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope * } - const Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr(); + const clang::Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr(); auto classs = ty->getTypeClass(); switch (classs) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Bool: - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - case BuiltinType::UShort: - case BuiltinType::UInt: - case BuiltinType::ULong: - case BuiltinType::ULongLong: - case BuiltinType::Short: - case BuiltinType::Int: - case BuiltinType::Long: - case BuiltinType::LongLong: - case BuiltinType::UInt128: - case BuiltinType::Int128: - case BuiltinType::Float: - case BuiltinType::Double: - case BuiltinType::Float128: - case BuiltinType::LongDouble: - case BuiltinType::WChar_U: - case BuiltinType::Char8: - case BuiltinType::Char16: - case BuiltinType::Char32: - case BuiltinType::WChar_S: - case BuiltinType::Float16: + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Int128: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::Float128: + case clang::BuiltinType::LongDouble: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char8: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Float16: return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); - case BuiltinType::NullPtr: - return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral)); + case clang::BuiltinType::NullPtr: + return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, + trans_create_node_unsigned(c, 0)); - case BuiltinType::Void: - case BuiltinType::Half: - case BuiltinType::ObjCId: - case BuiltinType::ObjCClass: - case BuiltinType::ObjCSel: - case BuiltinType::OMPArraySection: - case BuiltinType::Dependent: - case BuiltinType::Overload: - case BuiltinType::BoundMember: - case BuiltinType::PseudoObject: - case BuiltinType::UnknownAny: - case BuiltinType::BuiltinFn: - case BuiltinType::ARCUnbridgedCast: - case BuiltinType::OCLImage1dRO: - case BuiltinType::OCLImage1dArrayRO: - case BuiltinType::OCLImage1dBufferRO: - case BuiltinType::OCLImage2dRO: - case BuiltinType::OCLImage2dArrayRO: - case BuiltinType::OCLImage2dDepthRO: - case BuiltinType::OCLImage2dArrayDepthRO: - case BuiltinType::OCLImage2dMSAARO: - case BuiltinType::OCLImage2dArrayMSAARO: - case BuiltinType::OCLImage2dMSAADepthRO: - case BuiltinType::OCLImage2dArrayMSAADepthRO: - case BuiltinType::OCLImage3dRO: - case BuiltinType::OCLImage1dWO: - case BuiltinType::OCLImage1dArrayWO: - case BuiltinType::OCLImage1dBufferWO: - case BuiltinType::OCLImage2dWO: - case BuiltinType::OCLImage2dArrayWO: - case BuiltinType::OCLImage2dDepthWO: - case BuiltinType::OCLImage2dArrayDepthWO: - case BuiltinType::OCLImage2dMSAAWO: - case BuiltinType::OCLImage2dArrayMSAAWO: - case BuiltinType::OCLImage2dMSAADepthWO: - case BuiltinType::OCLImage2dArrayMSAADepthWO: - case BuiltinType::OCLImage3dWO: - case BuiltinType::OCLImage1dRW: - case BuiltinType::OCLImage1dArrayRW: - case BuiltinType::OCLImage1dBufferRW: - case BuiltinType::OCLImage2dRW: - case BuiltinType::OCLImage2dArrayRW: - case BuiltinType::OCLImage2dDepthRW: - case BuiltinType::OCLImage2dArrayDepthRW: - case BuiltinType::OCLImage2dMSAARW: - case BuiltinType::OCLImage2dArrayMSAARW: - case BuiltinType::OCLImage2dMSAADepthRW: - case BuiltinType::OCLImage2dArrayMSAADepthRW: - case BuiltinType::OCLImage3dRW: - case BuiltinType::OCLSampler: - case BuiltinType::OCLEvent: - case BuiltinType::OCLClkEvent: - case BuiltinType::OCLQueue: - case BuiltinType::OCLReserveID: - case BuiltinType::ShortAccum: - case BuiltinType::Accum: - case BuiltinType::LongAccum: - case BuiltinType::UShortAccum: - case BuiltinType::UAccum: - case BuiltinType::ULongAccum: - case BuiltinType::ShortFract: - case BuiltinType::Fract: - case BuiltinType::LongFract: - case BuiltinType::UShortFract: - case BuiltinType::UFract: - case BuiltinType::ULongFract: - case BuiltinType::SatShortAccum: - case BuiltinType::SatAccum: - case BuiltinType::SatLongAccum: - case BuiltinType::SatUShortAccum: - case BuiltinType::SatUAccum: - case BuiltinType::SatULongAccum: - case BuiltinType::SatShortFract: - case BuiltinType::SatFract: - case BuiltinType::SatLongFract: - case BuiltinType::SatUShortFract: - case BuiltinType::SatUFract: - case BuiltinType::SatULongFract: + case clang::BuiltinType::Void: + case clang::BuiltinType::Half: + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + case clang::BuiltinType::ObjCSel: + case clang::BuiltinType::OMPArraySection: + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::BuiltinFn: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::OCLImage1dRO: + case clang::BuiltinType::OCLImage1dArrayRO: + case clang::BuiltinType::OCLImage1dBufferRO: + case clang::BuiltinType::OCLImage2dRO: + case clang::BuiltinType::OCLImage2dArrayRO: + case clang::BuiltinType::OCLImage2dDepthRO: + case clang::BuiltinType::OCLImage2dArrayDepthRO: + case clang::BuiltinType::OCLImage2dMSAARO: + case clang::BuiltinType::OCLImage2dArrayMSAARO: + case clang::BuiltinType::OCLImage2dMSAADepthRO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRO: + case clang::BuiltinType::OCLImage3dRO: + case clang::BuiltinType::OCLImage1dWO: + case clang::BuiltinType::OCLImage1dArrayWO: + case clang::BuiltinType::OCLImage1dBufferWO: + case clang::BuiltinType::OCLImage2dWO: + case clang::BuiltinType::OCLImage2dArrayWO: + case clang::BuiltinType::OCLImage2dDepthWO: + case clang::BuiltinType::OCLImage2dArrayDepthWO: + case clang::BuiltinType::OCLImage2dMSAAWO: + case clang::BuiltinType::OCLImage2dArrayMSAAWO: + case clang::BuiltinType::OCLImage2dMSAADepthWO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthWO: + case clang::BuiltinType::OCLImage3dWO: + case clang::BuiltinType::OCLImage1dRW: + case clang::BuiltinType::OCLImage1dArrayRW: + case clang::BuiltinType::OCLImage1dBufferRW: + case clang::BuiltinType::OCLImage2dRW: + case clang::BuiltinType::OCLImage2dArrayRW: + case clang::BuiltinType::OCLImage2dDepthRW: + case clang::BuiltinType::OCLImage2dArrayDepthRW: + case clang::BuiltinType::OCLImage2dMSAARW: + case clang::BuiltinType::OCLImage2dArrayMSAARW: + case clang::BuiltinType::OCLImage2dMSAADepthRW: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRW: + case clang::BuiltinType::OCLImage3dRW: + case clang::BuiltinType::OCLSampler: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLClkEvent: + case clang::BuiltinType::OCLQueue: + case clang::BuiltinType::OCLReserveID: + case clang::BuiltinType::ShortAccum: + case clang::BuiltinType::Accum: + case clang::BuiltinType::LongAccum: + case clang::BuiltinType::UShortAccum: + case clang::BuiltinType::UAccum: + case clang::BuiltinType::ULongAccum: + case clang::BuiltinType::ShortFract: + case clang::BuiltinType::Fract: + case clang::BuiltinType::LongFract: + case clang::BuiltinType::UShortFract: + case clang::BuiltinType::UFract: + case clang::BuiltinType::ULongFract: + case clang::BuiltinType::SatShortAccum: + case clang::BuiltinType::SatAccum: + case clang::BuiltinType::SatLongAccum: + case clang::BuiltinType::SatUShortAccum: + case clang::BuiltinType::SatUAccum: + case clang::BuiltinType::SatULongAccum: + case clang::BuiltinType::SatShortFract: + case clang::BuiltinType::SatFract: + case clang::BuiltinType::SatLongFract: + case clang::BuiltinType::SatUShortFract: + case clang::BuiltinType::SatUFract: + case clang::BuiltinType::SatULongFract: return res; } break; } - case Type::Pointer: - return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral)); + case clang::Type::Pointer: + return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, + trans_create_node_unsigned(c, 0)); - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -2524,79 +2554,79 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope * return res; } - case Type::Enum: + case clang::Type::Enum: { - const EnumType *enum_ty = static_cast(ty); + const clang::EnumType *enum_ty = static_cast(ty); AstNode *enum_type = resolve_enum_decl(c, enum_ty->getDecl()); return to_enum_zero_cmp(c, res, enum_type); } - case Type::Elaborated: + case clang::Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + const clang::ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { - case ETK_Enum: { + case clang::ETK_Enum: { AstNode *enum_type = trans_qual_type(c, elaborated_ty->getNamedType(), expr->getLocStart()); return to_enum_zero_cmp(c, res, enum_type); } - case ETK_Struct: - case ETK_Union: - case ETK_Interface: - case ETK_Class: - case ETK_Typename: - case ETK_None: + case clang::ETK_Struct: + case clang::ETK_Union: + case clang::ETK_Interface: + case clang::ETK_Class: + case clang::ETK_Typename: + case clang::ETK_None: return res; } } - case Type::FunctionProto: - case Type::Record: - case Type::ConstantArray: - case Type::Paren: - case Type::Decayed: - case Type::Attributed: - case Type::IncompleteArray: - case Type::BlockPointer: - case Type::LValueReference: - case Type::RValueReference: - case Type::MemberPointer: - case Type::VariableArray: - case Type::DependentSizedArray: - case Type::DependentSizedExtVector: - case Type::Vector: - case Type::ExtVector: - case Type::FunctionNoProto: - case Type::UnresolvedUsing: - case Type::Adjusted: - case Type::TypeOfExpr: - case Type::TypeOf: - case Type::Decltype: - case Type::UnaryTransform: - case Type::TemplateTypeParm: - case Type::SubstTemplateTypeParm: - case Type::SubstTemplateTypeParmPack: - case Type::TemplateSpecialization: - case Type::Auto: - case Type::InjectedClassName: - case Type::DependentName: - case Type::DependentTemplateSpecialization: - case Type::PackExpansion: - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::Complex: - case Type::ObjCObjectPointer: - case Type::Atomic: - case Type::Pipe: - case Type::ObjCTypeParam: - case Type::DeducedTemplateSpecialization: - case Type::DependentAddressSpace: - case Type::DependentVector: + case clang::Type::FunctionProto: + case clang::Type::Record: + case clang::Type::ConstantArray: + case clang::Type::Paren: + case clang::Type::Decayed: + case clang::Type::Attributed: + case clang::Type::IncompleteArray: + case clang::Type::BlockPointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: + case clang::Type::VariableArray: + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::Vector: + case clang::Type::ExtVector: + case clang::Type::FunctionNoProto: + case clang::Type::UnresolvedUsing: + case clang::Type::Adjusted: + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::UnaryTransform: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::TemplateSpecialization: + case clang::Type::Auto: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + case clang::Type::Complex: + case clang::Type::ObjCObjectPointer: + case clang::Type::Atomic: + case clang::Type::Pipe: + case clang::Type::ObjCTypeParam: + case clang::Type::DeducedTemplateSpecialization: + case clang::Type::DependentAddressSpace: + case clang::Type::DependentVector: return res; } zig_unreachable(); } -static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) { +static AstNode *trans_while_loop(Context *c, TransScope *scope, const clang::WhileStmt *stmt) { TransScopeWhile *while_scope = trans_scope_while_create(c, scope); while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); @@ -2611,7 +2641,7 @@ static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt return while_scope->node; } -static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) { +static AstNode *trans_if_statement(Context *c, TransScope *scope, const clang::IfStmt *stmt) { // if (c) t // if (c) t else e AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr); @@ -2633,7 +2663,7 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * return if_node; } -static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) { +static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::CallExpr *stmt) { AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, stmt->getCallee(), TransRValue); @@ -2641,16 +2671,16 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * return nullptr; bool is_ptr = false; - const FunctionProtoType *fn_ty = qual_type_get_fn_proto(stmt->getCallee()->getType(), &is_ptr); + const clang::FunctionProtoType *fn_ty = qual_type_get_fn_proto(stmt->getCallee()->getType(), &is_ptr); AstNode *callee_node = nullptr; if (is_ptr && fn_ty) { - if (stmt->getCallee()->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee()); - if (implicit_cast->getCastKind() == CK_FunctionToPointerDecay) { - if (implicit_cast->getSubExpr()->getStmtClass() == Stmt::DeclRefExprClass) { - const DeclRefExpr *decl_ref = static_cast(implicit_cast->getSubExpr()); - const Decl *decl = decl_ref->getFoundDecl(); - if (decl->getKind() == Decl::Function) { + if (stmt->getCallee()->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee()); + if (implicit_cast->getCastKind() == clang::CK_FunctionToPointerDecay) { + if (implicit_cast->getSubExpr()->getStmtClass() == clang::Stmt::DeclRefExprClass) { + const clang::DeclRefExpr *decl_ref = static_cast(implicit_cast->getSubExpr()); + const clang::Decl *decl = decl_ref->getFoundDecl(); + if (decl->getKind() == clang::Decl::Function) { callee_node = callee_raw_node; } } @@ -2666,7 +2696,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * node->data.fn_call_expr.fn_ref_expr = callee_node; unsigned num_args = stmt->getNumArgs(); - const Expr * const* args = stmt->getArgs(); + const clang::Expr * const* args = stmt->getArgs(); for (unsigned i = 0; i < num_args; i += 1) { AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue); if (arg_node == nullptr) @@ -2682,7 +2712,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * return node; } -static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExpr *stmt) { +static AstNode *trans_member_expr(Context *c, TransScope *scope, const clang::MemberExpr *stmt) { AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue); if (container_node == nullptr) return nullptr; @@ -2697,7 +2727,7 @@ static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExp return node; } -static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const ArraySubscriptExpr *stmt) { +static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const clang::ArraySubscriptExpr *stmt) { AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue); if (container_node == nullptr) return nullptr; @@ -2714,7 +2744,7 @@ static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const } static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope, - const CStyleCastExpr *stmt, TransLRValue lrvalue) + const clang::CStyleCastExpr *stmt, TransLRValue lrvalue) { AstNode *sub_expr_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), lrvalue); if (sub_expr_node == nullptr) @@ -2724,7 +2754,7 @@ static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, Tran } static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scope, - const UnaryExprOrTypeTraitExpr *stmt) + const clang::UnaryExprOrTypeTraitExpr *stmt) { AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart()); if (type_node == nullptr) @@ -2735,14 +2765,14 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scop return node; } -static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) { +static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const clang::DoStmt *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 (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) { + if (stmt->getBody()->getStmtClass() == clang::Stmt::CompoundStmtClass) { // there's already a block in C, so we'll append our condition to it. // c: do { // c: a; @@ -2795,11 +2825,11 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt return while_scope->node; } -static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) { +static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const clang::ForStmt *stmt) { AstNode *loop_block_node; TransScopeWhile *while_scope; TransScope *cond_scope; - const Stmt *init_stmt = stmt->getInit(); + const clang::Stmt *init_stmt = stmt->getInit(); if (init_stmt == nullptr) { while_scope = trans_scope_while_create(c, parent_scope); loop_block_node = while_scope->node; @@ -2820,12 +2850,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt child_scope->node->data.block.statements.append(while_scope->node); } - const Stmt *cond_stmt = stmt->getCond(); + const clang::Stmt *cond_stmt = stmt->getCond(); if (cond_stmt == nullptr) { while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); } else { - if (Expr::classof(cond_stmt)) { - const Expr *cond_expr = static_cast(cond_stmt); + if (clang::Expr::classof(cond_stmt)) { + const clang::Expr *cond_expr = static_cast(cond_stmt); 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) @@ -2838,7 +2868,7 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt } } - const Stmt *inc_stmt = stmt->getInc(); + const clang::Stmt *inc_stmt = stmt->getInc(); if (inc_stmt != nullptr) { AstNode *inc_node; TransScope *inc_scope = trans_stmt(c, cond_scope, inc_stmt, &inc_node); @@ -2861,12 +2891,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt return loop_block_node; } -static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) { +static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const clang::SwitchStmt *stmt) { TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); TransScopeSwitch *switch_scope; - const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); + const clang::DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); if (var_decl_stmt == nullptr) { switch_scope = trans_scope_switch_create(c, &block_scope->base); } else { @@ -2885,7 +2915,7 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw switch_scope->end_label_name = end_label_name; block_scope->node->data.block.name = end_label_name; - const Expr *cond_expr = stmt->getCond(); + const clang::Expr *cond_expr = stmt->getCond(); assert(cond_expr != nullptr); AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue); @@ -2894,9 +2924,9 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw switch_scope->switch_node->data.switch_expr.expr = expr_node; AstNode *body_node; - const Stmt *body_stmt = stmt->getBody(); - if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) { - if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt, + const clang::Stmt *body_stmt = stmt->getBody(); + if (body_stmt->getStmtClass() == clang::Stmt::CompoundStmtClass) { + if (trans_compound_stmt_inline(c, &switch_scope->base, (const clang::CompoundStmt *)body_stmt, block_scope->node, nullptr)) { return nullptr; @@ -2928,7 +2958,7 @@ static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) { return nullptr; } -static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node, +static int trans_switch_case(Context *c, TransScope *parent_scope, const clang::CaseStmt *stmt, AstNode **out_node, TransScope **out_scope) { *out_node = nullptr; @@ -2973,7 +3003,7 @@ static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStm return ErrorNone; } -static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node, +static int trans_switch_default(Context *c, TransScope *parent_scope, const clang::DefaultStmt *stmt, AstNode **out_node, TransScope **out_scope) { *out_node = nullptr; @@ -3010,25 +3040,25 @@ static int trans_switch_default(Context *c, TransScope *parent_scope, const Defa return ErrorNone; } -static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) { +static AstNode *trans_string_literal(Context *c, TransScope *scope, const clang::StringLiteral *stmt) { switch (stmt->getKind()) { - case StringLiteral::Ascii: - case StringLiteral::UTF8: + case clang::StringLiteral::Ascii: + case clang::StringLiteral::UTF8: return trans_create_node_str_lit_c(c, string_ref_to_buf(stmt->getString())); - case StringLiteral::UTF16: + case clang::StringLiteral::UTF16: emit_warning(c, stmt->getLocStart(), "TODO support UTF16 string literals"); return nullptr; - case StringLiteral::UTF32: + case clang::StringLiteral::UTF32: emit_warning(c, stmt->getLocStart(), "TODO support UTF32 string literals"); return nullptr; - case StringLiteral::Wide: + case clang::StringLiteral::Wide: emit_warning(c, stmt->getLocStart(), "TODO support wide string literals"); return nullptr; } zig_unreachable(); } -static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) { +static AstNode *trans_break_stmt(Context *c, TransScope *scope, const clang::BreakStmt *stmt) { TransScope *cur_scope = scope; while (cur_scope != nullptr) { if (cur_scope->id == TransScopeIdWhile) { @@ -3042,7 +3072,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt zig_unreachable(); } -static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) { +static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const clang::ContinueStmt *stmt) { return trans_create_node(c, NodeTypeContinue); } @@ -3055,47 +3085,47 @@ static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_ return ErrorNone; } -static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, +static int trans_stmt_extra(Context *c, TransScope *scope, const clang::Stmt *stmt, ResultUsed result_used, TransLRValue lrvalue, AstNode **out_node, TransScope **out_child_scope, TransScope **out_node_scope) { - Stmt::StmtClass sc = stmt->getStmtClass(); + clang::Stmt::StmtClass sc = stmt->getStmtClass(); switch (sc) { - case Stmt::ReturnStmtClass: + case clang::Stmt::ReturnStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_return_stmt(c, scope, (const ReturnStmt *)stmt)); - case Stmt::CompoundStmtClass: + trans_return_stmt(c, scope, (const clang::ReturnStmt *)stmt)); + case clang::Stmt::CompoundStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_stmt(c, scope, (const CompoundStmt *)stmt, out_node_scope)); - case Stmt::IntegerLiteralClass: + trans_compound_stmt(c, scope, (const clang::CompoundStmt *)stmt, out_node_scope)); + case clang::Stmt::IntegerLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_integer_literal(c, (const IntegerLiteral *)stmt)); - case Stmt::ConditionalOperatorClass: + trans_integer_literal(c, (const clang::IntegerLiteral *)stmt)); + case clang::Stmt::ConditionalOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_conditional_operator(c, result_used, scope, (const ConditionalOperator *)stmt)); - case Stmt::BinaryOperatorClass: + trans_conditional_operator(c, result_used, scope, (const clang::ConditionalOperator *)stmt)); + case clang::Stmt::BinaryOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_binary_operator(c, result_used, scope, (const BinaryOperator *)stmt)); - case Stmt::CompoundAssignOperatorClass: + trans_binary_operator(c, result_used, scope, (const clang::BinaryOperator *)stmt)); + case clang::Stmt::CompoundAssignOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_assign_operator(c, result_used, scope, (const CompoundAssignOperator *)stmt)); - case Stmt::ImplicitCastExprClass: + trans_compound_assign_operator(c, result_used, scope, (const clang::CompoundAssignOperator *)stmt)); + case clang::Stmt::ImplicitCastExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_implicit_cast_expr(c, scope, (const ImplicitCastExpr *)stmt)); - case Stmt::DeclRefExprClass: + trans_implicit_cast_expr(c, scope, (const clang::ImplicitCastExpr *)stmt)); + case clang::Stmt::DeclRefExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_decl_ref_expr(c, scope, (const DeclRefExpr *)stmt, lrvalue)); - case Stmt::UnaryOperatorClass: + trans_decl_ref_expr(c, scope, (const clang::DeclRefExpr *)stmt, lrvalue)); + case clang::Stmt::UnaryOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); - case Stmt::DeclStmtClass: - return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); - case Stmt::DoStmtClass: - case Stmt::WhileStmtClass: { - AstNode *while_node = sc == Stmt::DoStmtClass - ? trans_do_loop(c, scope, (const DoStmt *)stmt) - : trans_while_loop(c, scope, (const WhileStmt *)stmt); + trans_unary_operator(c, result_used, scope, (const clang::UnaryOperator *)stmt)); + case clang::Stmt::DeclStmtClass: + return trans_local_declaration(c, scope, (const clang::DeclStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::DoStmtClass: + case clang::Stmt::WhileStmtClass: { + AstNode *while_node = sc == clang::Stmt::DoStmtClass + ? trans_do_loop(c, scope, (const clang::DoStmt *)stmt) + : trans_while_loop(c, scope, (const clang::WhileStmt *)stmt); if (while_node == nullptr) return ErrorUnexpected; @@ -3106,556 +3136,556 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return wrap_stmt(out_node, out_child_scope, scope, while_node); } - case Stmt::IfStmtClass: + case clang::Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_if_statement(c, scope, (const IfStmt *)stmt)); - case Stmt::CallExprClass: + trans_if_statement(c, scope, (const clang::IfStmt *)stmt)); + case clang::Stmt::CallExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_call_expr(c, result_used, scope, (const CallExpr *)stmt)); - case Stmt::NullStmtClass: + trans_call_expr(c, result_used, scope, (const clang::CallExpr *)stmt)); + case clang::Stmt::NullStmtClass: *out_node = nullptr; *out_child_scope = scope; return ErrorNone; - case Stmt::MemberExprClass: + case clang::Stmt::MemberExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_member_expr(c, scope, (const MemberExpr *)stmt)); - case Stmt::ArraySubscriptExprClass: + trans_member_expr(c, scope, (const clang::MemberExpr *)stmt)); + case clang::Stmt::ArraySubscriptExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_array_subscript_expr(c, scope, (const ArraySubscriptExpr *)stmt)); - case Stmt::CStyleCastExprClass: + trans_array_subscript_expr(c, scope, (const clang::ArraySubscriptExpr *)stmt)); + case clang::Stmt::CStyleCastExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_c_style_cast_expr(c, result_used, scope, (const CStyleCastExpr *)stmt, lrvalue)); - case Stmt::UnaryExprOrTypeTraitExprClass: + trans_c_style_cast_expr(c, result_used, scope, (const clang::CStyleCastExpr *)stmt, lrvalue)); + case clang::Stmt::UnaryExprOrTypeTraitExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); - case Stmt::ForStmtClass: { - AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); + trans_unary_expr_or_type_trait_expr(c, scope, (const clang::UnaryExprOrTypeTraitExpr *)stmt)); + case clang::Stmt::ForStmtClass: { + AstNode *node = trans_for_loop(c, scope, (const clang::ForStmt *)stmt); return wrap_stmt(out_node, out_child_scope, scope, node); } - case Stmt::StringLiteralClass: + case clang::Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_string_literal(c, scope, (const StringLiteral *)stmt)); - case Stmt::BreakStmtClass: + trans_string_literal(c, scope, (const clang::StringLiteral *)stmt)); + case clang::Stmt::BreakStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_break_stmt(c, scope, (const BreakStmt *)stmt)); - case Stmt::ContinueStmtClass: + trans_break_stmt(c, scope, (const clang::BreakStmt *)stmt)); + case clang::Stmt::ContinueStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_continue_stmt(c, scope, (const ContinueStmt *)stmt)); - case Stmt::ParenExprClass: + trans_continue_stmt(c, scope, (const clang::ContinueStmt *)stmt)); + case clang::Stmt::ParenExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue)); - case Stmt::SwitchStmtClass: + trans_expr(c, result_used, scope, ((const clang::ParenExpr*)stmt)->getSubExpr(), lrvalue)); + case clang::Stmt::SwitchStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_switch_stmt(c, scope, (const SwitchStmt *)stmt)); - case Stmt::CaseStmtClass: - return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope); - case Stmt::DefaultStmtClass: - return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope); - case Stmt::NoStmtClass: + trans_switch_stmt(c, scope, (const clang::SwitchStmt *)stmt)); + case clang::Stmt::CaseStmtClass: + return trans_switch_case(c, scope, (const clang::CaseStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::DefaultStmtClass: + return trans_switch_default(c, scope, (const clang::DefaultStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::NoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass"); return ErrorUnexpected; - case Stmt::GCCAsmStmtClass: + case clang::Stmt::GCCAsmStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass"); return ErrorUnexpected; - case Stmt::MSAsmStmtClass: + case clang::Stmt::MSAsmStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass"); return ErrorUnexpected; - case Stmt::AttributedStmtClass: + case clang::Stmt::AttributedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass"); return ErrorUnexpected; - case Stmt::CXXCatchStmtClass: + case clang::Stmt::CXXCatchStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass"); return ErrorUnexpected; - case Stmt::CXXForRangeStmtClass: + case clang::Stmt::CXXForRangeStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass"); return ErrorUnexpected; - case Stmt::CXXTryStmtClass: + case clang::Stmt::CXXTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass"); return ErrorUnexpected; - case Stmt::CapturedStmtClass: + case clang::Stmt::CapturedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass"); return ErrorUnexpected; - case Stmt::CoreturnStmtClass: + case clang::Stmt::CoreturnStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass"); return ErrorUnexpected; - case Stmt::CoroutineBodyStmtClass: + case clang::Stmt::CoroutineBodyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass"); return ErrorUnexpected; - case Stmt::BinaryConditionalOperatorClass: + case clang::Stmt::BinaryConditionalOperatorClass: emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass"); return ErrorUnexpected; - case Stmt::AddrLabelExprClass: + case clang::Stmt::AddrLabelExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass"); return ErrorUnexpected; - case Stmt::ArrayInitIndexExprClass: + case clang::Stmt::ArrayInitIndexExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass"); return ErrorUnexpected; - case Stmt::ArrayInitLoopExprClass: + case clang::Stmt::ArrayInitLoopExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass"); return ErrorUnexpected; - case Stmt::ArrayTypeTraitExprClass: + case clang::Stmt::ArrayTypeTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass"); return ErrorUnexpected; - case Stmt::AsTypeExprClass: + case clang::Stmt::AsTypeExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass"); return ErrorUnexpected; - case Stmt::AtomicExprClass: + case clang::Stmt::AtomicExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass"); return ErrorUnexpected; - case Stmt::BlockExprClass: + case clang::Stmt::BlockExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass"); return ErrorUnexpected; - case Stmt::CXXBindTemporaryExprClass: + case clang::Stmt::CXXBindTemporaryExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass"); return ErrorUnexpected; - case Stmt::CXXBoolLiteralExprClass: + case clang::Stmt::CXXBoolLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass"); return ErrorUnexpected; - case Stmt::CXXConstructExprClass: + case clang::Stmt::CXXConstructExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass"); return ErrorUnexpected; - case Stmt::CXXTemporaryObjectExprClass: + case clang::Stmt::CXXTemporaryObjectExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass"); return ErrorUnexpected; - case Stmt::CXXDefaultArgExprClass: + case clang::Stmt::CXXDefaultArgExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass"); return ErrorUnexpected; - case Stmt::CXXDefaultInitExprClass: + case clang::Stmt::CXXDefaultInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass"); return ErrorUnexpected; - case Stmt::CXXDeleteExprClass: + case clang::Stmt::CXXDeleteExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass"); return ErrorUnexpected; - case Stmt::CXXDependentScopeMemberExprClass: + case clang::Stmt::CXXDependentScopeMemberExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass"); return ErrorUnexpected; - case Stmt::CXXFoldExprClass: + case clang::Stmt::CXXFoldExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass"); return ErrorUnexpected; - case Stmt::CXXInheritedCtorInitExprClass: + case clang::Stmt::CXXInheritedCtorInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass"); return ErrorUnexpected; - case Stmt::CXXNewExprClass: + case clang::Stmt::CXXNewExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass"); return ErrorUnexpected; - case Stmt::CXXNoexceptExprClass: + case clang::Stmt::CXXNoexceptExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass"); return ErrorUnexpected; - case Stmt::CXXNullPtrLiteralExprClass: + case clang::Stmt::CXXNullPtrLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass"); return ErrorUnexpected; - case Stmt::CXXPseudoDestructorExprClass: + case clang::Stmt::CXXPseudoDestructorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass"); return ErrorUnexpected; - case Stmt::CXXScalarValueInitExprClass: + case clang::Stmt::CXXScalarValueInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass"); return ErrorUnexpected; - case Stmt::CXXStdInitializerListExprClass: + case clang::Stmt::CXXStdInitializerListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass"); return ErrorUnexpected; - case Stmt::CXXThisExprClass: + case clang::Stmt::CXXThisExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass"); return ErrorUnexpected; - case Stmt::CXXThrowExprClass: + case clang::Stmt::CXXThrowExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass"); return ErrorUnexpected; - case Stmt::CXXTypeidExprClass: + case clang::Stmt::CXXTypeidExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass"); return ErrorUnexpected; - case Stmt::CXXUnresolvedConstructExprClass: + case clang::Stmt::CXXUnresolvedConstructExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass"); return ErrorUnexpected; - case Stmt::CXXUuidofExprClass: + case clang::Stmt::CXXUuidofExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass"); return ErrorUnexpected; - case Stmt::CUDAKernelCallExprClass: + case clang::Stmt::CUDAKernelCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass"); return ErrorUnexpected; - case Stmt::CXXMemberCallExprClass: + case clang::Stmt::CXXMemberCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass"); return ErrorUnexpected; - case Stmt::CXXOperatorCallExprClass: + case clang::Stmt::CXXOperatorCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass"); return ErrorUnexpected; - case Stmt::UserDefinedLiteralClass: + case clang::Stmt::UserDefinedLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass"); return ErrorUnexpected; - case Stmt::CXXFunctionalCastExprClass: + case clang::Stmt::CXXFunctionalCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass"); return ErrorUnexpected; - case Stmt::CXXConstCastExprClass: + case clang::Stmt::CXXConstCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass"); return ErrorUnexpected; - case Stmt::CXXDynamicCastExprClass: + case clang::Stmt::CXXDynamicCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass"); return ErrorUnexpected; - case Stmt::CXXReinterpretCastExprClass: + case clang::Stmt::CXXReinterpretCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass"); return ErrorUnexpected; - case Stmt::CXXStaticCastExprClass: + case clang::Stmt::CXXStaticCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass"); return ErrorUnexpected; - case Stmt::ObjCBridgedCastExprClass: + case clang::Stmt::ObjCBridgedCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass"); return ErrorUnexpected; - case Stmt::CharacterLiteralClass: + case clang::Stmt::CharacterLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass"); return ErrorUnexpected; - case Stmt::ChooseExprClass: + case clang::Stmt::ChooseExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass"); return ErrorUnexpected; - case Stmt::CompoundLiteralExprClass: + case clang::Stmt::CompoundLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass"); return ErrorUnexpected; - case Stmt::ConvertVectorExprClass: + case clang::Stmt::ConvertVectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass"); return ErrorUnexpected; - case Stmt::CoawaitExprClass: + case clang::Stmt::CoawaitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass"); return ErrorUnexpected; - case Stmt::CoyieldExprClass: + case clang::Stmt::CoyieldExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass"); return ErrorUnexpected; - case Stmt::DependentCoawaitExprClass: + case clang::Stmt::DependentCoawaitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass"); return ErrorUnexpected; - case Stmt::DependentScopeDeclRefExprClass: + case clang::Stmt::DependentScopeDeclRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass"); return ErrorUnexpected; - case Stmt::DesignatedInitExprClass: + case clang::Stmt::DesignatedInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass"); return ErrorUnexpected; - case Stmt::DesignatedInitUpdateExprClass: + case clang::Stmt::DesignatedInitUpdateExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass"); return ErrorUnexpected; - case Stmt::ExprWithCleanupsClass: + case clang::Stmt::ExprWithCleanupsClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass"); return ErrorUnexpected; - case Stmt::ExpressionTraitExprClass: + case clang::Stmt::ExpressionTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass"); return ErrorUnexpected; - case Stmt::ExtVectorElementExprClass: + case clang::Stmt::ExtVectorElementExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass"); return ErrorUnexpected; - case Stmt::FloatingLiteralClass: + case clang::Stmt::FloatingLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass"); return ErrorUnexpected; - case Stmt::FunctionParmPackExprClass: + case clang::Stmt::FunctionParmPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass"); return ErrorUnexpected; - case Stmt::GNUNullExprClass: + case clang::Stmt::GNUNullExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass"); return ErrorUnexpected; - case Stmt::GenericSelectionExprClass: + case clang::Stmt::GenericSelectionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass"); return ErrorUnexpected; - case Stmt::ImaginaryLiteralClass: + case clang::Stmt::ImaginaryLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass"); return ErrorUnexpected; - case Stmt::ImplicitValueInitExprClass: + case clang::Stmt::ImplicitValueInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass"); return ErrorUnexpected; - case Stmt::InitListExprClass: + case clang::Stmt::InitListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass"); return ErrorUnexpected; - case Stmt::LambdaExprClass: + case clang::Stmt::LambdaExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass"); return ErrorUnexpected; - case Stmt::MSPropertyRefExprClass: + case clang::Stmt::MSPropertyRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass"); return ErrorUnexpected; - case Stmt::MSPropertySubscriptExprClass: + case clang::Stmt::MSPropertySubscriptExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass"); return ErrorUnexpected; - case Stmt::MaterializeTemporaryExprClass: + case clang::Stmt::MaterializeTemporaryExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass"); return ErrorUnexpected; - case Stmt::NoInitExprClass: + case clang::Stmt::NoInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass"); return ErrorUnexpected; - case Stmt::OMPArraySectionExprClass: + case clang::Stmt::OMPArraySectionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass"); return ErrorUnexpected; - case Stmt::ObjCArrayLiteralClass: + case clang::Stmt::ObjCArrayLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCAvailabilityCheckExprClass: + case clang::Stmt::ObjCAvailabilityCheckExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass"); return ErrorUnexpected; - case Stmt::ObjCBoolLiteralExprClass: + case clang::Stmt::ObjCBoolLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass"); return ErrorUnexpected; - case Stmt::ObjCBoxedExprClass: + case clang::Stmt::ObjCBoxedExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass"); return ErrorUnexpected; - case Stmt::ObjCDictionaryLiteralClass: + case clang::Stmt::ObjCDictionaryLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCEncodeExprClass: + case clang::Stmt::ObjCEncodeExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass"); return ErrorUnexpected; - case Stmt::ObjCIndirectCopyRestoreExprClass: + case clang::Stmt::ObjCIndirectCopyRestoreExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass"); return ErrorUnexpected; - case Stmt::ObjCIsaExprClass: + case clang::Stmt::ObjCIsaExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass"); return ErrorUnexpected; - case Stmt::ObjCIvarRefExprClass: + case clang::Stmt::ObjCIvarRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass"); return ErrorUnexpected; - case Stmt::ObjCMessageExprClass: + case clang::Stmt::ObjCMessageExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass"); return ErrorUnexpected; - case Stmt::ObjCPropertyRefExprClass: + case clang::Stmt::ObjCPropertyRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass"); return ErrorUnexpected; - case Stmt::ObjCProtocolExprClass: + case clang::Stmt::ObjCProtocolExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass"); return ErrorUnexpected; - case Stmt::ObjCSelectorExprClass: + case clang::Stmt::ObjCSelectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass"); return ErrorUnexpected; - case Stmt::ObjCStringLiteralClass: + case clang::Stmt::ObjCStringLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCSubscriptRefExprClass: + case clang::Stmt::ObjCSubscriptRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass"); return ErrorUnexpected; - case Stmt::OffsetOfExprClass: + case clang::Stmt::OffsetOfExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass"); return ErrorUnexpected; - case Stmt::OpaqueValueExprClass: + case clang::Stmt::OpaqueValueExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass"); return ErrorUnexpected; - case Stmt::UnresolvedLookupExprClass: + case clang::Stmt::UnresolvedLookupExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass"); return ErrorUnexpected; - case Stmt::UnresolvedMemberExprClass: + case clang::Stmt::UnresolvedMemberExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass"); return ErrorUnexpected; - case Stmt::PackExpansionExprClass: + case clang::Stmt::PackExpansionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass"); return ErrorUnexpected; - case Stmt::ParenListExprClass: + case clang::Stmt::ParenListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass"); return ErrorUnexpected; - case Stmt::PredefinedExprClass: + case clang::Stmt::PredefinedExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass"); return ErrorUnexpected; - case Stmt::PseudoObjectExprClass: + case clang::Stmt::PseudoObjectExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass"); return ErrorUnexpected; - case Stmt::ShuffleVectorExprClass: + case clang::Stmt::ShuffleVectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass"); return ErrorUnexpected; - case Stmt::SizeOfPackExprClass: + case clang::Stmt::SizeOfPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass"); return ErrorUnexpected; - case Stmt::StmtExprClass: + case clang::Stmt::StmtExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass"); return ErrorUnexpected; - case Stmt::SubstNonTypeTemplateParmExprClass: + case clang::Stmt::SubstNonTypeTemplateParmExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass"); return ErrorUnexpected; - case Stmt::SubstNonTypeTemplateParmPackExprClass: + case clang::Stmt::SubstNonTypeTemplateParmPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass"); return ErrorUnexpected; - case Stmt::TypeTraitExprClass: + case clang::Stmt::TypeTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass"); return ErrorUnexpected; - case Stmt::TypoExprClass: + case clang::Stmt::TypoExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass"); return ErrorUnexpected; - case Stmt::VAArgExprClass: + case clang::Stmt::VAArgExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass"); return ErrorUnexpected; - case Stmt::GotoStmtClass: + case clang::Stmt::GotoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass"); return ErrorUnexpected; - case Stmt::IndirectGotoStmtClass: + case clang::Stmt::IndirectGotoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass"); return ErrorUnexpected; - case Stmt::LabelStmtClass: + case clang::Stmt::LabelStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass"); return ErrorUnexpected; - case Stmt::MSDependentExistsStmtClass: + case clang::Stmt::MSDependentExistsStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass"); return ErrorUnexpected; - case Stmt::OMPAtomicDirectiveClass: + case clang::Stmt::OMPAtomicDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPBarrierDirectiveClass: + case clang::Stmt::OMPBarrierDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCancelDirectiveClass: + case clang::Stmt::OMPCancelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCancellationPointDirectiveClass: + case clang::Stmt::OMPCancellationPointDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCriticalDirectiveClass: + case clang::Stmt::OMPCriticalDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPFlushDirectiveClass: + case clang::Stmt::OMPFlushDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeDirectiveClass: + case clang::Stmt::OMPDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeParallelForDirectiveClass: + case clang::Stmt::OMPDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeSimdDirectiveClass: + case clang::Stmt::OMPDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPForDirectiveClass: + case clang::Stmt::OMPForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPForSimdDirectiveClass: + case clang::Stmt::OMPForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelForDirectiveClass: + case clang::Stmt::OMPParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelForSimdDirectiveClass: + case clang::Stmt::OMPParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSimdDirectiveClass: + case clang::Stmt::OMPSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelForSimdDirectiveClass: + case clang::Stmt::OMPTargetParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetSimdDirectiveClass: + case clang::Stmt::OMPTargetSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskLoopDirectiveClass: + case clang::Stmt::OMPTaskLoopDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskLoopSimdDirectiveClass: + case clang::Stmt::OMPTaskLoopSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeDirectiveClass: + case clang::Stmt::OMPTeamsDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case clang::Stmt::OMPTeamsDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case clang::Stmt::OMPTeamsDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPMasterDirectiveClass: + case clang::Stmt::OMPMasterDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPOrderedDirectiveClass: + case clang::Stmt::OMPOrderedDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelDirectiveClass: + case clang::Stmt::OMPParallelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelSectionsDirectiveClass: + case clang::Stmt::OMPParallelSectionsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSectionDirectiveClass: + case clang::Stmt::OMPSectionDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSectionsDirectiveClass: + case clang::Stmt::OMPSectionsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSingleDirectiveClass: + case clang::Stmt::OMPSingleDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetDataDirectiveClass: + case clang::Stmt::OMPTargetDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetDirectiveClass: + case clang::Stmt::OMPTargetDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetEnterDataDirectiveClass: + case clang::Stmt::OMPTargetEnterDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetExitDataDirectiveClass: + case clang::Stmt::OMPTargetExitDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelDirectiveClass: + case clang::Stmt::OMPTargetParallelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelForDirectiveClass: + case clang::Stmt::OMPTargetParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDirectiveClass: + case clang::Stmt::OMPTargetTeamsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetUpdateDirectiveClass: + case clang::Stmt::OMPTargetUpdateDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskDirectiveClass: + case clang::Stmt::OMPTaskDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskgroupDirectiveClass: + case clang::Stmt::OMPTaskgroupDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskwaitDirectiveClass: + case clang::Stmt::OMPTaskwaitDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskyieldDirectiveClass: + case clang::Stmt::OMPTaskyieldDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDirectiveClass: + case clang::Stmt::OMPTeamsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass"); return ErrorUnexpected; - case Stmt::ObjCAtCatchStmtClass: + case clang::Stmt::ObjCAtCatchStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtFinallyStmtClass: + case clang::Stmt::ObjCAtFinallyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtSynchronizedStmtClass: + case clang::Stmt::ObjCAtSynchronizedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtThrowStmtClass: + case clang::Stmt::ObjCAtThrowStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtTryStmtClass: + case clang::Stmt::ObjCAtTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAutoreleasePoolStmtClass: + case clang::Stmt::ObjCAutoreleasePoolStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass"); return ErrorUnexpected; - case Stmt::ObjCForCollectionStmtClass: + case clang::Stmt::ObjCForCollectionStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass"); return ErrorUnexpected; - case Stmt::SEHExceptStmtClass: + case clang::Stmt::SEHExceptStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass"); return ErrorUnexpected; - case Stmt::SEHFinallyStmtClass: + case clang::Stmt::SEHFinallyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass"); return ErrorUnexpected; - case Stmt::SEHLeaveStmtClass: + case clang::Stmt::SEHLeaveStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass"); return ErrorUnexpected; - case Stmt::SEHTryStmtClass: + case clang::Stmt::SEHTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass"); return ErrorUnexpected; - case Stmt::FixedPointLiteralClass: + case clang::Stmt::FixedPointLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FixedPointLiteralClass"); return ErrorUnexpected; } @@ -3663,7 +3693,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, } // Returns null if there was an error -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval) { AstNode *result_node; @@ -3676,7 +3706,7 @@ static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope // 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 Stmt *stmt, AstNode **out_node) { +static TransScope *trans_stmt(Context *c, TransScope *scope, const clang::Stmt *stmt, AstNode **out_node) { TransScope *child_scope; if (trans_stmt_extra(c, scope, stmt, ResultUsedNo, TransRValue, out_node, &child_scope, nullptr)) { return nullptr; @@ -3684,7 +3714,7 @@ static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, A return child_scope; } -static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { +static void visit_fn_decl(Context *c, const clang::FunctionDecl *fn_decl) { Buf *fn_name = buf_create_from_str(decl_name(fn_decl)); if (get_global(c, fn_name)) { @@ -3701,13 +3731,13 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { proto_node->data.fn_proto.name = fn_name; proto_node->data.fn_proto.is_extern = !fn_decl->hasBody(); - StorageClass sc = fn_decl->getStorageClass(); - if (sc == SC_None) { + clang::StorageClass sc = fn_decl->getStorageClass(); + if (sc == clang::SC_None) { proto_node->data.fn_proto.visib_mod = c->visib_mod; proto_node->data.fn_proto.is_export = fn_decl->hasBody() ? c->want_export : false; - } else if (sc == SC_Extern || sc == SC_Static) { + } else if (sc == clang::SC_Extern || sc == clang::SC_Static) { proto_node->data.fn_proto.visib_mod = c->visib_mod; - } else if (sc == SC_PrivateExtern) { + } else if (sc == clang::SC_PrivateExtern) { emit_warning(c, fn_decl->getLocation(), "unsupported storage class: private extern"); return; } else { @@ -3719,7 +3749,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { 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 ParmVarDecl *param = fn_decl->getParamDecl(i); + const clang::ParmVarDecl *param = fn_decl->getParamDecl(i); const char *name = decl_name(param); Buf *proto_param_name; @@ -3746,7 +3776,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { // actual function definition with body c->ptr_params.clear(); - Stmt *body = fn_decl->getBody(); + clang::Stmt *body = fn_decl->getBody(); AstNode *actual_body_node; TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node); if (result_scope == nullptr) { @@ -3788,19 +3818,19 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { 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 TypedefNameDecl *typedef_decl, const char *primitive_name) { +static AstNode *resolve_typdef_as_builtin(Context *c, const clang::TypedefNameDecl *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 TypedefNameDecl *typedef_decl) { +static AstNode *resolve_typedef_decl(Context *c, const clang::TypedefNameDecl *typedef_decl) { auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; } - QualType child_qt = typedef_decl->getUnderlyingType(); + clang::QualType child_qt = typedef_decl->getUnderlyingType(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); if (buf_eql_str(type_name, "uint8_t")) { @@ -3849,7 +3879,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ return symbol_node; } -struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl, +struct AstNode *demote_enum_to_opaque(Context *c, const clang::EnumDecl *enum_decl, Buf *full_type_name, Buf *bare_name) { AstNode *opaque_node = trans_create_node_opaque(c); @@ -3864,7 +3894,7 @@ struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl, return symbol_node; } -static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { +static AstNode *resolve_enum_decl(Context *c, const clang::EnumDecl *enum_decl) { auto existing_entry = c->decl_table.maybe_get((void*)enum_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -3875,7 +3905,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { 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 EnumDecl *enum_def = enum_decl->getDefinition(); + const clang::EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); } @@ -3887,7 +3917,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { it_end = enum_def->enumerator_end(); it != it_end; ++it, field_count += 1) { - const EnumConstantDecl *enum_const = *it; + const clang::EnumConstantDecl *enum_const = *it; if (enum_const->getInitExpr()) { pure_enum = false; } @@ -3901,8 +3931,8 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { // 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, enum_decl->getIntegerType(), BuiltinType::UInt) && - !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), clang::BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), clang::BuiltinType::Int)) { enum_node->data.container_decl.init_arg_expr = tag_int_type; } @@ -3912,7 +3942,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { it_end = enum_def->enumerator_end(); it != it_end; ++it, i += 1) { - const EnumConstantDecl *enum_const = *it; + const clang::EnumConstantDecl *enum_const = *it; Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); Buf *field_name; @@ -3953,7 +3983,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { } } -static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_decl, +static AstNode *demote_struct_to_opaque(Context *c, const clang::RecordDecl *record_decl, Buf *full_type_name, Buf *bare_name) { AstNode *opaque_node = trans_create_node_opaque(c); @@ -3968,7 +3998,7 @@ static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_dec return symbol_node; } -static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { +static AstNode *resolve_record_decl(Context *c, const clang::RecordDecl *record_decl) { auto existing_entry = c->decl_table.maybe_get((void*)record_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -3994,7 +4024,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { Buf *full_type_name = (bare_name == nullptr) ? nullptr : buf_sprintf("%s_%s", container_kind_name, buf_ptr(bare_name)); - RecordDecl *record_def = record_decl->getDefinition(); + clang::RecordDecl *record_def = record_decl->getDefinition(); if (record_def == nullptr) { return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name); } @@ -4005,7 +4035,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { it_end = record_def->field_end(); it != it_end; ++it, field_count += 1) { - const FieldDecl *field_decl = *it; + const clang::FieldDecl *field_decl = *it; if (field_decl->isBitField()) { emit_warning(c, field_decl->getLocation(), "%s %s demoted to opaque type - has bitfield", @@ -4035,7 +4065,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { it_end = record_def->field_end(); it != it_end; ++it, i += 1) { - const FieldDecl *field_decl = *it; + const clang::FieldDecl *field_decl = *it; AstNode *field_node = trans_create_node(c, NodeTypeStructField); field_node->data.struct_field.name = buf_create_from_str(decl_name(field_decl)); @@ -4062,13 +4092,13 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { } } -static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const SourceLocation &source_loc) { +static AstNode *trans_ap_value(Context *c, clang::APValue *ap_value, clang::QualType qt, const clang::SourceLocation &source_loc) { switch (ap_value->getKind()) { - case APValue::Int: + case clang::APValue::Int: return trans_create_node_apint(c, ap_value->getInt()); - case APValue::Uninitialized: + case clang::APValue::Uninitialized: return trans_create_node(c, NodeTypeUndefinedLiteral); - case APValue::Array: { + case clang::APValue::Array: { emit_warning(c, source_loc, "TODO add a test case for this code"); unsigned init_count = ap_value->getArrayInitializedElts(); @@ -4076,13 +4106,16 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const 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; - QualType child_qt = qt.getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType(); + clang::QualType child_qt = qt.getTypePtr()->getAsArrayTypeUnsafe()->getElementType(); for (size_t i = 0; i < init_count; i += 1) { - APValue &elem_ap_val = ap_value->getArrayInitializedElt(i); + clang::APValue &elem_ap_val = ap_value->getArrayInitializedElt(i); AstNode *elem_node = trans_ap_value(c, &elem_ap_val, child_qt, source_loc); if (elem_node == nullptr) return nullptr; @@ -4092,15 +4125,19 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const return init_node; } - APValue &filler_ap_val = ap_value->getArrayFiller(); + clang::APValue &filler_ap_val = ap_value->getArrayFiller(); 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); - init_node->data.container_init_expr.type = arr_type_node; - init_node->data.container_init_expr.kind = ContainerInitKindArray; - init_node->data.container_init_expr.entries.append(filler_node); + 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) { @@ -4110,62 +4147,66 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const 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 APValue::LValue: { - const APValue::LValueBase lval_base = ap_value->getLValueBase(); - if (const Expr *expr = lval_base.dyn_cast()) { + case clang::APValue::LValue: { + const clang::APValue::LValueBase lval_base = ap_value->getLValueBase(); + if (const clang::Expr *expr = lval_base.dyn_cast()) { return trans_expr(c, ResultUsedYes, &c->global_scope->base, expr, TransRValue); } - //const ValueDecl *value_decl = lval_base.get(); - emit_warning(c, source_loc, "TODO handle initializer LValue ValueDecl"); + //const clang::ValueDecl *value_decl = lval_base.get(); + emit_warning(c, source_loc, "TODO handle initializer LValue clang::ValueDecl"); return nullptr; } - case APValue::Float: + case clang::APValue::Float: emit_warning(c, source_loc, "unsupported initializer value kind: Float"); return nullptr; - case APValue::ComplexInt: + case clang::APValue::ComplexInt: emit_warning(c, source_loc, "unsupported initializer value kind: ComplexInt"); return nullptr; - case APValue::ComplexFloat: + case clang::APValue::ComplexFloat: emit_warning(c, source_loc, "unsupported initializer value kind: ComplexFloat"); return nullptr; - case APValue::Vector: + case clang::APValue::Vector: emit_warning(c, source_loc, "unsupported initializer value kind: Vector"); return nullptr; - case APValue::Struct: + case clang::APValue::Struct: emit_warning(c, source_loc, "unsupported initializer value kind: Struct"); return nullptr; - case APValue::Union: + case clang::APValue::Union: emit_warning(c, source_loc, "unsupported initializer value kind: Union"); return nullptr; - case APValue::MemberPointer: + case clang::APValue::MemberPointer: emit_warning(c, source_loc, "unsupported initializer value kind: MemberPointer"); return nullptr; - case APValue::AddrLabelDiff: + case clang::APValue::AddrLabelDiff: emit_warning(c, source_loc, "unsupported initializer value kind: AddrLabelDiff"); return nullptr; } zig_unreachable(); } -static void visit_var_decl(Context *c, const VarDecl *var_decl) { +static void visit_var_decl(Context *c, const clang::VarDecl *var_decl) { Buf *name = buf_create_from_str(decl_name(var_decl)); switch (var_decl->getTLSKind()) { - case VarDecl::TLS_None: + case clang::VarDecl::TLS_None: break; - case VarDecl::TLS_Static: + case clang::VarDecl::TLS_Static: emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - static thread local storage", buf_ptr(name)); return; - case VarDecl::TLS_Dynamic: + case clang::VarDecl::TLS_Dynamic: emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name)); return; } - QualType qt = var_decl->getType(); + clang::QualType qt = var_decl->getType(); AstNode *var_type = trans_qual_type(c, qt, var_decl->getLocation()); if (var_type == nullptr) { emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type", buf_ptr(name)); @@ -4179,7 +4220,7 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { if (is_static && !is_extern) { AstNode *init_node; if (var_decl->hasInit()) { - APValue *ap_value = var_decl->evaluateValue(); + clang::APValue *ap_value = var_decl->evaluateValue(); if (ap_value == nullptr) { emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name)); @@ -4209,24 +4250,25 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { return; } -static bool decl_visitor(void *context, const Decl *decl) { +static bool decl_visitor(void *context, const ZigClangDecl *zdecl) { + const clang::Decl *decl = reinterpret_cast(zdecl); Context *c = (Context*)context; switch (decl->getKind()) { - case Decl::Function: - visit_fn_decl(c, static_cast(decl)); + case clang::Decl::Function: + visit_fn_decl(c, static_cast(decl)); break; - case Decl::Typedef: - resolve_typedef_decl(c, static_cast(decl)); + case clang::Decl::Typedef: + resolve_typedef_decl(c, static_cast(decl)); break; - case Decl::Enum: - resolve_enum_decl(c, static_cast(decl)); + case clang::Decl::Enum: + resolve_enum_decl(c, static_cast(decl)); break; - case Decl::Record: - resolve_record_decl(c, static_cast(decl)); + case clang::Decl::Record: + resolve_record_decl(c, static_cast(decl)); break; - case Decl::Var: - visit_var_decl(c, static_cast(decl)); + case clang::Decl::Var: + visit_var_decl(c, static_cast(decl)); break; default: emit_warning(c, decl->getLocation(), "ignoring %s decl", decl->getDeclKindName()); @@ -4568,7 +4610,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown); + node = trans_create_node_ptr_type(c, false, false, node, PtrLenC); } else { return node; } @@ -4647,24 +4689,25 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch c->macro_table.put(name, result_node); } -static void process_preprocessor_entities(Context *c, ASTUnit &unit) { +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 (PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) { + for (clang::PreprocessedEntity *entity : unit->getLocalPreprocessingEntities()) { switch (entity->getKind()) { - case PreprocessedEntity::InvalidKind: - case PreprocessedEntity::InclusionDirectiveKind: - case PreprocessedEntity::MacroExpansionKind: + case clang::PreprocessedEntity::InvalidKind: + case clang::PreprocessedEntity::InclusionDirectiveKind: + case clang::PreprocessedEntity::MacroExpansionKind: continue; - case PreprocessedEntity::MacroDefinitionKind: + case clang::PreprocessedEntity::MacroDefinitionKind: { - MacroDefinitionRecord *macro = static_cast(entity); + clang::MacroDefinitionRecord *macro = static_cast(entity); const char *raw_name = macro->getName()->getNameStart(); - SourceRange range = macro->getSourceRange(); - SourceLocation begin_loc = range.getBegin(); - SourceLocation end_loc = range.getEnd(); + clang::SourceRange range = macro->getSourceRange(); + clang::SourceLocation begin_loc = range.getBegin(); + clang::SourceLocation end_loc = range.getEnd(); if (begin_loc == end_loc) { // this means it is a macro without a value @@ -4676,7 +4719,7 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) { continue; } - const char *begin_c = c->source_manager->getCharacterData(begin_loc); + const char *begin_c = ZigClangSourceManager_getCharacterData(c->source_manager, bitcast(begin_loc)); process_macro(c, &ctok, name, begin_c); } } @@ -4749,8 +4792,10 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); - clang_argv.append("-isystem"); - clang_argv.append(buf_ptr(codegen->libc_include_dir)); + if (codegen->libc_include_dir != nullptr) { + clang_argv.append("-isystem"); + clang_argv.append(buf_ptr(codegen->libc_include_dir)); + } // windows c runtime requires -D_DEBUG if using debug libraries if (codegen->build_mode == BuildModeDebug) { @@ -4776,12 +4821,20 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append(target_file); + if (codegen->verbose_cimport) { + fprintf(stderr, "clang"); + for (size_t i = 0; i < clang_argv.length; i += 1) { + fprintf(stderr, " %s", clang_argv.at(i)); + } + fprintf(stderr, "\n"); + } + // to make the [start...end] argument work clang_argv.append(nullptr); - IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + clang::IntrusiveRefCntPtr diags(clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions)); - std::shared_ptr pch_container_ops = std::make_shared(); + std::shared_ptr pch_container_ops = std::make_shared(); bool only_local_decls = true; bool capture_diagnostics = true; @@ -4790,13 +4843,13 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const bool single_file_parse = false; bool for_serialization = false; const char *resources_path = buf_ptr(codegen->zig_c_headers_dir); - std::unique_ptr err_unit; - std::unique_ptr ast_unit(ASTUnit::LoadFromCommandLine( + std::unique_ptr err_unit; + ZigClangASTUnit *ast_unit = reinterpret_cast(clang::ASTUnit::LoadFromCommandLine( &clang_argv.at(0), &clang_argv.last(), pch_container_ops, diags, resources_path, - only_local_decls, capture_diagnostics, None, true, 0, TU_Complete, - false, false, allow_pch_with_compiler_errors, SkipFunctionBodiesScope::None, - single_file_parse, user_files_are_volatile, for_serialization, None, &err_unit, + only_local_decls, capture_diagnostics, 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, clang::None, &err_unit, nullptr)); // Early failures in LoadFromCommandLine may return with ErrUnit unset. @@ -4806,29 +4859,29 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const if (diags->getClient()->getNumErrors() > 0) { if (ast_unit) { - err_unit = std::move(ast_unit); + err_unit = std::unique_ptr(reinterpret_cast(ast_unit)); } - for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), + for (clang::ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), it_end = err_unit->stored_diag_end(); it != it_end; ++it) { switch (it->getLevel()) { - case DiagnosticsEngine::Ignored: - case DiagnosticsEngine::Note: - case DiagnosticsEngine::Remark: - case DiagnosticsEngine::Warning: + case clang::DiagnosticsEngine::Ignored: + case clang::DiagnosticsEngine::Note: + case clang::DiagnosticsEngine::Remark: + case clang::DiagnosticsEngine::Warning: continue; - case DiagnosticsEngine::Error: - case DiagnosticsEngine::Fatal: + case clang::DiagnosticsEngine::Error: + case clang::DiagnosticsEngine::Fatal: break; } StringRef msg_str_ref = it->getMessage(); Buf *msg = string_ref_to_buf(msg_str_ref); - FullSourceLoc fsl = it->getLocation(); + clang::FullSourceLoc fsl = it->getLocation(); if (fsl.hasManager()) { - FileID file_id = fsl.getFileID(); - StringRef filename = fsl.getManager().getFilename(fsl); + clang::FileID file_id = fsl.getFileID(); + clang::StringRef filename = fsl.getManager().getFilename(fsl); unsigned line = fsl.getSpellingLineNumber() - 1; unsigned column = fsl.getSpellingColumnNumber() - 1; unsigned offset = fsl.getManager().getFileOffset(fsl); @@ -4853,14 +4906,14 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const return ErrorCCompileErrors; } - c->ctx = &ast_unit->getASTContext(); - c->source_manager = &ast_unit->getSourceManager(); + 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; - ast_unit->visitLocalTopLevelDecls(c, decl_visitor); + ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, decl_visitor); - process_preprocessor_entities(c, *ast_unit); + process_preprocessor_entities(c, ast_unit); render_macros(c); render_aliases(c); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp new file mode 100644 index 0000000000..4220d778a0 --- /dev/null +++ b/src/zig_clang.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + + +/* + * The point of this file is to contain all the Clang C++ API interaction so that: + * 1. The compile time of other files is kept under control. + * 2. Provide a C interface to the Clang functions we need for self-hosting purposes. + * 3. Prevent C++ from infecting the rest of the project. + */ +#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 + +// Detect additions to the enum +void zig2clang_BO(ZigClangBO op) { + switch (op) { + case ZigClangBO_PtrMemD: + case ZigClangBO_PtrMemI: + case ZigClangBO_Cmp: + 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_Assign: + case ZigClangBO_Comma: + 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: + break; + } +} + +static_assert((clang::BinaryOperatorKind)ZigClangBO_Add == clang::BO_Add, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_AddAssign == clang::BO_AddAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_And == clang::BO_And, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_AndAssign == clang::BO_AndAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Assign == clang::BO_Assign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Cmp == clang::BO_Cmp, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Comma == clang::BO_Comma, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Div == clang::BO_Div, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_DivAssign == clang::BO_DivAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_EQ == clang::BO_EQ, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_GE == clang::BO_GE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_GT == clang::BO_GT, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LAnd == clang::BO_LAnd, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LE == clang::BO_LE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LOr == clang::BO_LOr, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LT == clang::BO_LT, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Mul == clang::BO_Mul, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_MulAssign == clang::BO_MulAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_NE == clang::BO_NE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Or == clang::BO_Or, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_OrAssign == clang::BO_OrAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_PtrMemD == clang::BO_PtrMemD, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_PtrMemI == clang::BO_PtrMemI, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Rem == clang::BO_Rem, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_RemAssign == clang::BO_RemAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Shl == clang::BO_Shl, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_ShlAssign == clang::BO_ShlAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Shr == clang::BO_Shr, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_ShrAssign == clang::BO_ShrAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Sub == clang::BO_Sub, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_SubAssign == clang::BO_SubAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Xor == clang::BO_Xor, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_XorAssign == clang::BO_XorAssign, ""); + +// This function detects additions to the enum +void zig2clang_UO(ZigClangUO op) { + switch (op) { + case ZigClangUO_AddrOf: + case ZigClangUO_Coawait: + case ZigClangUO_Deref: + case ZigClangUO_Extension: + case ZigClangUO_Imag: + case ZigClangUO_LNot: + case ZigClangUO_Minus: + case ZigClangUO_Not: + case ZigClangUO_Plus: + case ZigClangUO_PostDec: + case ZigClangUO_PostInc: + case ZigClangUO_PreDec: + case ZigClangUO_PreInc: + case ZigClangUO_Real: + break; + } +} + +static_assert((clang::UnaryOperatorKind)ZigClangUO_AddrOf == clang::UO_AddrOf, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Coawait == clang::UO_Coawait, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Deref == clang::UO_Deref, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Extension == clang::UO_Extension, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Imag == clang::UO_Imag, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_LNot == clang::UO_LNot, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Minus == clang::UO_Minus, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Not == clang::UO_Not, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Plus == clang::UO_Plus, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PostDec == clang::UO_PostDec, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PostInc == clang::UO_PostInc, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PreDec == clang::UO_PreDec, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PreInc == clang::UO_PreInc, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Real == clang::UO_Real, ""); + +static_assert(sizeof(ZigClangSourceLocation) == sizeof(clang::SourceLocation), ""); +static ZigClangSourceLocation bitcast(clang::SourceLocation src) { + ZigClangSourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} +static clang::SourceLocation bitcast(ZigClangSourceLocation src) { + clang::SourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} + +static_assert(sizeof(ZigClangQualType) == sizeof(clang::QualType), ""); +static ZigClangQualType bitcast(clang::QualType src) { + ZigClangQualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} +static clang::QualType bitcast(ZigClangQualType src) { + clang::QualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} + +ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) +{ + return bitcast(reinterpret_cast(self)->getSpellingLoc(bitcast(Loc))); +} + +const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *self, + ZigClangSourceLocation SpellingLoc) +{ + StringRef s = reinterpret_cast(self)->getFilename(bitcast(SpellingLoc)); + return (const char *)s.bytes_begin(); +} + +unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) +{ + return reinterpret_cast(self)->getSpellingLineNumber(bitcast(Loc)); +} + +unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) +{ + return reinterpret_cast(self)->getSpellingColumnNumber(bitcast(Loc)); +} + +const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *self, + ZigClangSourceLocation SL) +{ + return reinterpret_cast(self)->getCharacterData(bitcast(SL)); +} + +ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext* self, ZigClangQualType T) { + return bitcast(reinterpret_cast(self)->getPointerType(bitcast(T))); +} + +ZigClangASTContext *ZigClangASTUnit_getASTContext(ZigClangASTUnit *self) { + clang::ASTContext *result = &reinterpret_cast(self)->getASTContext(); + return reinterpret_cast(result); +} + +ZigClangSourceManager *ZigClangASTUnit_getSourceManager(ZigClangASTUnit *self) { + clang::SourceManager *result = &reinterpret_cast(self)->getSourceManager(); + return reinterpret_cast(result); +} + +bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *self, void *context, + bool (*Fn)(void *context, const ZigClangDecl *decl)) +{ + return reinterpret_cast(self)->visitLocalTopLevelDecls(context, + reinterpret_cast(Fn)); +} diff --git a/src/zig_clang.h b/src/zig_clang.h new file mode 100644 index 0000000000..c7d749cbd9 --- /dev/null +++ b/src/zig_clang.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_ZIG_CLANG_H +#define ZIG_ZIG_CLANG_H + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +// ATTENTION: If you modify this file, be sure to update the corresponding +// extern function declarations in the self-hosted compiler. + +struct ZigClangSourceLocation { + unsigned ID; +}; + +struct ZigClangQualType { + void *ptr; +}; + +struct ZigClangAPValue; +struct ZigClangASTContext; +struct ZigClangASTUnit; +struct ZigClangArraySubscriptExpr; +struct ZigClangArrayType; +struct ZigClangAttributedType; +struct ZigClangBinaryOperator; +struct ZigClangBreakStmt; +struct ZigClangBuiltinType; +struct ZigClangCStyleCastExpr; +struct ZigClangCallExpr; +struct ZigClangCaseStmt; +struct ZigClangCompoundAssignOperator; +struct ZigClangCompoundStmt; +struct ZigClangConditionalOperator; +struct ZigClangConstantArrayType; +struct ZigClangContinueStmt; +struct ZigClangDecayedType; +struct ZigClangDecl; +struct ZigClangDeclRefExpr; +struct ZigClangDeclStmt; +struct ZigClangDefaultStmt; +struct ZigClangDiagnosticOptions; +struct ZigClangDiagnosticsEngine; +struct ZigClangDoStmt; +struct ZigClangElaboratedType; +struct ZigClangEnumConstantDecl; +struct ZigClangEnumDecl; +struct ZigClangEnumType; +struct ZigClangExpr; +struct ZigClangFieldDecl; +struct ZigClangFileID; +struct ZigClangForStmt; +struct ZigClangFullSourceLoc; +struct ZigClangFunctionDecl; +struct ZigClangFunctionProtoType; +struct ZigClangIfStmt; +struct ZigClangImplicitCastExpr; +struct ZigClangIncompleteArrayType; +struct ZigClangIntegerLiteral; +struct ZigClangMacroDefinitionRecord; +struct ZigClangMemberExpr; +struct ZigClangNamedDecl; +struct ZigClangNone; +struct ZigClangPCHContainerOperations; +struct ZigClangParenExpr; +struct ZigClangParenType; +struct ZigClangParmVarDecl; +struct ZigClangPointerType; +struct ZigClangPreprocessedEntity; +struct ZigClangRecordDecl; +struct ZigClangRecordType; +struct ZigClangReturnStmt; +struct ZigClangSkipFunctionBodiesScope; +struct ZigClangSourceManager; +struct ZigClangSourceRange; +struct ZigClangStmt; +struct ZigClangStorageClass; +struct ZigClangStringLiteral; +struct ZigClangStringRef; +struct ZigClangSwitchStmt; +struct ZigClangType; +struct ZigClangTypedefNameDecl; +struct ZigClangTypedefType; +struct ZigClangUnaryExprOrTypeTraitExpr; +struct ZigClangUnaryOperator; +struct ZigClangValueDecl; +struct ZigClangVarDecl; +struct ZigClangWhileStmt; + +enum ZigClangBO { + ZigClangBO_PtrMemD, + ZigClangBO_PtrMemI, + ZigClangBO_Mul, + ZigClangBO_Div, + ZigClangBO_Rem, + ZigClangBO_Add, + ZigClangBO_Sub, + ZigClangBO_Shl, + ZigClangBO_Shr, + ZigClangBO_Cmp, + ZigClangBO_LT, + ZigClangBO_GT, + ZigClangBO_LE, + ZigClangBO_GE, + ZigClangBO_EQ, + ZigClangBO_NE, + ZigClangBO_And, + ZigClangBO_Xor, + ZigClangBO_Or, + ZigClangBO_LAnd, + ZigClangBO_LOr, + ZigClangBO_Assign, + ZigClangBO_MulAssign, + ZigClangBO_DivAssign, + ZigClangBO_RemAssign, + ZigClangBO_AddAssign, + ZigClangBO_SubAssign, + ZigClangBO_ShlAssign, + ZigClangBO_ShrAssign, + ZigClangBO_AndAssign, + ZigClangBO_XorAssign, + ZigClangBO_OrAssign, + ZigClangBO_Comma, +}; + +enum ZigClangUO { + ZigClangUO_PostInc, + ZigClangUO_PostDec, + ZigClangUO_PreInc, + ZigClangUO_PreDec, + ZigClangUO_AddrOf, + ZigClangUO_Deref, + ZigClangUO_Plus, + ZigClangUO_Minus, + ZigClangUO_Not, + ZigClangUO_LNot, + ZigClangUO_Real, + ZigClangUO_Imag, + ZigClangUO_Extension, + ZigClangUO_Coawait, +}; + +//struct ZigClangCC_AAPCS; +//struct ZigClangCC_AAPCS_VFP; +//struct ZigClangCC_C; +//struct ZigClangCC_IntelOclBicc; +//struct ZigClangCC_OpenCLKernel; +//struct ZigClangCC_PreserveAll; +//struct ZigClangCC_PreserveMost; +//struct ZigClangCC_SpirFunction; +//struct ZigClangCC_Swift; +//struct ZigClangCC_Win64; +//struct ZigClangCC_X86FastCall; +//struct ZigClangCC_X86Pascal; +//struct ZigClangCC_X86RegCall; +//struct ZigClangCC_X86StdCall; +//struct ZigClangCC_X86ThisCall; +//struct ZigClangCC_X86VectorCall; +//struct ZigClangCC_X86_64SysV; + +//struct ZigClangCK_ARCConsumeObject; +//struct ZigClangCK_ARCExtendBlockObject; +//struct ZigClangCK_ARCProduceObject; +//struct ZigClangCK_ARCReclaimReturnedObject; +//struct ZigClangCK_AddressSpaceConversion; +//struct ZigClangCK_AnyPointerToBlockPointerCast; +//struct ZigClangCK_ArrayToPointerDecay; +//struct ZigClangCK_AtomicToNonAtomic; +//struct ZigClangCK_BaseToDerived; +//struct ZigClangCK_BaseToDerivedMemberPointer; +//struct ZigClangCK_BitCast; +//struct ZigClangCK_BlockPointerToObjCPointerCast; +//struct ZigClangCK_BooleanToSignedIntegral; +//struct ZigClangCK_BuiltinFnToFnPtr; +//struct ZigClangCK_CPointerToObjCPointerCast; +//struct ZigClangCK_ConstructorConversion; +//struct ZigClangCK_CopyAndAutoreleaseBlockObject; +//struct ZigClangCK_Dependent; +//struct ZigClangCK_DerivedToBase; +//struct ZigClangCK_DerivedToBaseMemberPointer; +//struct ZigClangCK_Dynamic; +//struct ZigClangCK_FloatingCast; +//struct ZigClangCK_FloatingComplexCast; +//struct ZigClangCK_FloatingComplexToBoolean; +//struct ZigClangCK_FloatingComplexToIntegralComplex; +//struct ZigClangCK_FloatingComplexToReal; +//struct ZigClangCK_FloatingRealToComplex; +//struct ZigClangCK_FloatingToBoolean; +//struct ZigClangCK_FloatingToIntegral; +//struct ZigClangCK_FunctionToPointerDecay; +//struct ZigClangCK_IntToOCLSampler; +//struct ZigClangCK_IntegralCast; +//struct ZigClangCK_IntegralComplexCast; +//struct ZigClangCK_IntegralComplexToBoolean; +//struct ZigClangCK_IntegralComplexToFloatingComplex; +//struct ZigClangCK_IntegralComplexToReal; +//struct ZigClangCK_IntegralRealToComplex; +//struct ZigClangCK_IntegralToBoolean; +//struct ZigClangCK_IntegralToFloating; +//struct ZigClangCK_IntegralToPointer; +//struct ZigClangCK_LValueBitCast; +//struct ZigClangCK_LValueToRValue; +//struct ZigClangCK_MemberPointerToBoolean; +//struct ZigClangCK_NoOp; +//struct ZigClangCK_NonAtomicToAtomic; +//struct ZigClangCK_NullToMemberPointer; +//struct ZigClangCK_NullToPointer; +//struct ZigClangCK_ObjCObjectLValueCast; +//struct ZigClangCK_PointerToBoolean; +//struct ZigClangCK_PointerToIntegral; +//struct ZigClangCK_ReinterpretMemberPointer; +//struct ZigClangCK_ToUnion; +//struct ZigClangCK_ToVoid; +//struct ZigClangCK_UncheckedDerivedToBase; +//struct ZigClangCK_UserDefinedConversion; +//struct ZigClangCK_VectorSplat; +//struct ZigClangCK_ZeroToOCLEvent; +//struct ZigClangCK_ZeroToOCLQueue; + +//struct ZigClangETK_Class; +//struct ZigClangETK_Enum; +//struct ZigClangETK_Interface; +//struct ZigClangETK_None; +//struct ZigClangETK_Struct; +//struct ZigClangETK_Typename; +//struct ZigClangETK_Union; + +//struct ZigClangSC_None; +//struct ZigClangSC_PrivateExtern; +//struct ZigClangSC_Static; + +//struct ZigClangTU_Complete; + +ZIG_EXTERN_C ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *, + ZigClangSourceLocation SpellingLoc); +ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *, + ZigClangSourceLocation SL); + +ZIG_EXTERN_C ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext*, ZigClangQualType T); + +ZIG_EXTERN_C ZigClangASTContext *ZigClangASTUnit_getASTContext(ZigClangASTUnit *); +ZIG_EXTERN_C ZigClangSourceManager *ZigClangASTUnit_getSourceManager(ZigClangASTUnit *); +ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *, void *context, + bool (*Fn)(void *context, const ZigClangDecl *decl)); +#endif diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index bda8fa0adc..e5a4890914 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch return reinterpret_cast(di_type); } +struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder, + uint64_t SizeInBits, uint32_t AlignInBits, struct ZigLLVMDIType *Ty, uint32_t elem_count) +{ + SmallVector subrange; + subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, elem_count)); + DIType *di_type = reinterpret_cast(dibuilder)->createVectorType( + SizeInBits, + AlignInBits, + reinterpret_cast(Ty), + reinterpret_cast(dibuilder)->getOrCreateArray(subrange)); + return reinterpret_cast(di_type); +} + ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits, uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count) { @@ -605,7 +618,7 @@ ZigLLVMDISubprogram *ZigLLVMCreateFunction(ZigLLVMDIBuilder *dibuilder, ZigLLVMD reinterpret_cast(file), lineno, di_sub_type, - is_local_to_unit, is_definition, scope_line, DINode::FlagZero, is_optimized, + is_local_to_unit, is_definition, scope_line, DINode::FlagStaticMember, is_optimized, nullptr, reinterpret_cast(decl_subprogram)); return reinterpret_cast(result); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 551a4a7448..b5b0e97b53 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -191,6 +191,9 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB unsigned lineno, struct ZigLLVMDIType *fn_di_type, bool is_local_to_unit, bool is_definition, unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram); +ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder, + uint64_t SizeInBits, uint32_t AlignInBits, struct ZigLLVMDIType *Ty, uint32_t elem_count); + ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram); ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder); diff --git a/std/array_list.zig b/std/array_list.zig index 3ee425fe14..e2535d6393 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -1,7 +1,7 @@ const std = @import("index.zig"); const debug = std.debug; const assert = debug.assert; -const assertError = debug.assertError; +const testing = std.testing; const mem = std.mem; const Allocator = mem.Allocator; @@ -212,8 +212,8 @@ test "std.ArrayList.init" { var list = ArrayList(i32).init(allocator); defer list.deinit(); - assert(list.count() == 0); - assert(list.capacity() == 0); + testing.expect(list.count() == 0); + testing.expect(list.capacity() == 0); } test "std.ArrayList.basic" { @@ -224,7 +224,7 @@ test "std.ArrayList.basic" { defer list.deinit(); // setting on empty list is out of bounds - assertError(list.setOrError(0, 1), error.OutOfBounds); + testing.expectError(error.OutOfBounds, list.setOrError(0, 1)); { var i: usize = 0; @@ -236,44 +236,44 @@ test "std.ArrayList.basic" { { var i: usize = 0; while (i < 10) : (i += 1) { - assert(list.items[i] == @intCast(i32, i + 1)); + testing.expect(list.items[i] == @intCast(i32, i + 1)); } } for (list.toSlice()) |v, i| { - assert(v == @intCast(i32, i + 1)); + testing.expect(v == @intCast(i32, i + 1)); } for (list.toSliceConst()) |v, i| { - assert(v == @intCast(i32, i + 1)); + testing.expect(v == @intCast(i32, i + 1)); } - assert(list.pop() == 10); - assert(list.len == 9); + testing.expect(list.pop() == 10); + testing.expect(list.len == 9); list.appendSlice([]const i32{ 1, 2, 3, }) catch unreachable; - assert(list.len == 12); - assert(list.pop() == 3); - assert(list.pop() == 2); - assert(list.pop() == 1); - assert(list.len == 9); + 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([]const i32{}) catch unreachable; - assert(list.len == 9); + testing.expect(list.len == 9); // can only set on indices < self.len list.set(7, 33); list.set(8, 42); - assertError(list.setOrError(9, 99), error.OutOfBounds); - assertError(list.setOrError(10, 123), error.OutOfBounds); + testing.expectError(error.OutOfBounds, list.setOrError(9, 99)); + testing.expectError(error.OutOfBounds, list.setOrError(10, 123)); - assert(list.pop() == 42); - assert(list.pop() == 33); + testing.expect(list.pop() == 42); + testing.expect(list.pop() == 33); } test "std.ArrayList.swapRemove" { @@ -289,18 +289,18 @@ test "std.ArrayList.swapRemove" { try list.append(7); //remove from middle - assert(list.swapRemove(3) == 4); - assert(list.at(3) == 7); - assert(list.len == 6); + testing.expect(list.swapRemove(3) == 4); + testing.expect(list.at(3) == 7); + testing.expect(list.len == 6); //remove from end - assert(list.swapRemove(5) == 6); - assert(list.len == 5); + testing.expect(list.swapRemove(5) == 6); + testing.expect(list.len == 5); //remove from front - assert(list.swapRemove(0) == 1); - assert(list.at(0) == 5); - assert(list.len == 4); + testing.expect(list.swapRemove(0) == 1); + testing.expect(list.at(0) == 5); + testing.expect(list.len == 4); } test "std.ArrayList.swapRemoveOrError" { @@ -308,27 +308,27 @@ test "std.ArrayList.swapRemoveOrError" { defer list.deinit(); // Test just after initialization - assertError(list.swapRemoveOrError(0), error.OutOfBounds); + testing.expectError(error.OutOfBounds, list.swapRemoveOrError(0)); // Test after adding one item and remote it try list.append(1); - assert((try list.swapRemoveOrError(0)) == 1); - assertError(list.swapRemoveOrError(0), error.OutOfBounds); + testing.expect((try list.swapRemoveOrError(0)) == 1); + testing.expectError(error.OutOfBounds, list.swapRemoveOrError(0)); // Test after adding two items and remote both try list.append(1); try list.append(2); - assert((try list.swapRemoveOrError(1)) == 2); - assert((try list.swapRemoveOrError(0)) == 1); - assertError(list.swapRemoveOrError(0), error.OutOfBounds); + testing.expect((try list.swapRemoveOrError(1)) == 2); + testing.expect((try list.swapRemoveOrError(0)) == 1); + testing.expectError(error.OutOfBounds, list.swapRemoveOrError(0)); // Test out of bounds with one item try list.append(1); - assertError(list.swapRemoveOrError(1), error.OutOfBounds); + testing.expectError(error.OutOfBounds, list.swapRemoveOrError(1)); // Test out of bounds with two items try list.append(2); - assertError(list.swapRemoveOrError(2), error.OutOfBounds); + testing.expectError(error.OutOfBounds, list.swapRemoveOrError(2)); } test "std.ArrayList.iterator" { @@ -342,22 +342,22 @@ test "std.ArrayList.iterator" { var count: i32 = 0; var it = list.iterator(); while (it.next()) |next| { - assert(next == count + 1); + testing.expect(next == count + 1); count += 1; } - assert(count == 3); - assert(it.next() == null); + testing.expect(count == 3); + testing.expect(it.next() == null); it.reset(); count = 0; while (it.next()) |next| { - assert(next == count + 1); + testing.expect(next == count + 1); count += 1; if (count == 2) break; } it.reset(); - assert(it.next().? == 1); + testing.expect(it.next().? == 1); } test "std.ArrayList.insert" { @@ -368,10 +368,10 @@ test "std.ArrayList.insert" { try list.append(2); try list.append(3); try list.insert(0, 5); - assert(list.items[0] == 5); - assert(list.items[1] == 1); - assert(list.items[2] == 2); - assert(list.items[3] == 3); + testing.expect(list.items[0] == 5); + testing.expect(list.items[1] == 1); + testing.expect(list.items[2] == 2); + testing.expect(list.items[3] == 3); } test "std.ArrayList.insertSlice" { @@ -386,15 +386,26 @@ test "std.ArrayList.insertSlice" { 9, 8, }); - assert(list.items[0] == 1); - assert(list.items[1] == 9); - assert(list.items[2] == 8); - assert(list.items[3] == 2); - assert(list.items[4] == 3); - assert(list.items[5] == 4); + testing.expect(list.items[0] == 1); + testing.expect(list.items[1] == 9); + testing.expect(list.items[2] == 8); + testing.expect(list.items[3] == 2); + testing.expect(list.items[4] == 3); + testing.expect(list.items[5] == 4); const items = []const i32{1}; try list.insertSlice(0, items[0..0]); - assert(list.len == 6); - assert(list.items[0] == 1); + testing.expect(list.len == 6); + testing.expect(list.items[0] == 1); +} + +const Item = struct { + integer: i32, + sub_items: ArrayList(Item), +}; + +test "std.ArrayList: ArrayList(T) of struct T" { + var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; + try root.sub_items.append( Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) } ); + testing.expect(root.sub_items.items[0].integer == 42); } diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1aab4c32de..bdc86c0981 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; const assert = std.debug.assert; +const expect = std.testing.expect; /// Many producer, many consumer, non-allocating, thread-safe. /// Uses a mutex to protect access. @@ -170,20 +171,36 @@ test "std.atomic.Queue" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + expect(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + expect(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 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); @@ -205,11 +222,12 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Queue(i32).Node{ + const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; + node.* = Queue(i32).Node{ .prev = undefined, .next = undefined, .data = x, - }) catch unreachable; + }; ctx.queue.put(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } @@ -247,7 +265,7 @@ test "std.atomic.Queue single-threaded" { }; queue.put(&node_1); - assert(queue.get().?.data == 0); + expect(queue.get().?.data == 0); var node_2 = Queue(i32).Node{ .data = 2, @@ -263,9 +281,9 @@ test "std.atomic.Queue single-threaded" { }; queue.put(&node_3); - assert(queue.get().?.data == 1); + expect(queue.get().?.data == 1); - assert(queue.get().?.data == 2); + expect(queue.get().?.data == 2); var node_4 = Queue(i32).Node{ .data = 4, @@ -274,12 +292,12 @@ test "std.atomic.Queue single-threaded" { }; queue.put(&node_4); - assert(queue.get().?.data == 3); + expect(queue.get().?.data == 3); node_3.next = null; - assert(queue.get().?.data == 4); + expect(queue.get().?.data == 4); - assert(queue.get() == null); + expect(queue.get() == null); } test "std.atomic.Queue dump" { @@ -294,7 +312,7 @@ test "std.atomic.Queue dump" { // Test empty stream sos.reset(); try queue.dumpToStream(SliceOutStream.Error, &sos.stream); - assert(mem.eql(u8, buffer[0..sos.pos], + expect(mem.eql(u8, buffer[0..sos.pos], \\head: (null) \\tail: (null) \\ @@ -318,7 +336,7 @@ test "std.atomic.Queue dump" { \\ (null) \\ , @ptrToInt(queue.head), @ptrToInt(queue.tail)); - assert(mem.eql(u8, buffer[0..sos.pos], expected)); + expect(mem.eql(u8, buffer[0..sos.pos], expected)); // Test a stream with two elements var node_1 = Queue(i32).Node{ @@ -339,5 +357,5 @@ test "std.atomic.Queue dump" { \\ (null) \\ , @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail)); - assert(mem.eql(u8, buffer[0..sos.pos], expected)); + expect(mem.eql(u8, buffer[0..sos.pos], expected)); } diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index b69a93733c..4d0d5075e0 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -1,13 +1,17 @@ const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; +const expect = std.testing.expect; /// Many reader, many writer, non-allocating, thread-safe /// Uses a spinlock to protect push() and pop() +/// When building in single threaded mode, this is a simple linked list. pub fn Stack(comptime T: type) type { return struct { root: ?*Node, - lock: u8, + lock: @typeOf(lock_init), + + const lock_init = if (builtin.single_threaded) {} else u8(0); pub const Self = @This(); @@ -19,7 +23,7 @@ pub fn Stack(comptime T: type) type { pub fn init() Self { return Self{ .root = null, - .lock = 0, + .lock = lock_init, }; } @@ -31,20 +35,31 @@ pub fn Stack(comptime T: type) type { } pub fn push(self: *Self, node: *Node) void { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + if (builtin.single_threaded) { + node.next = self.root; + self.root = node; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - node.next = self.root; - self.root = node; + node.next = self.root; + self.root = node; + } } pub fn pop(self: *Self) ?*Node { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + if (builtin.single_threaded) { + const root = self.root orelse return null; + self.root = root.next; + return root; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - const root = self.root orelse return null; - self.root = root.next; - return root; + const root = self.root orelse return null; + self.root = root.next; + return root; + } } pub fn isEmpty(self: *Self) bool { @@ -90,20 +105,36 @@ test "std.atomic.stack" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + expect(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + expect(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 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); @@ -125,10 +156,11 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Stack(i32).Node{ + const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; + node.* = Stack(i32).Node{ .next = undefined, .data = x, - }) catch unreachable; + }; ctx.stack.push(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } diff --git a/std/base64.zig b/std/base64.zig index bc0bdb1bd3..cfe8ea95f8 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -1,5 +1,6 @@ const std = @import("index.zig"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -394,7 +395,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void var buffer: [0x100]u8 = undefined; var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)]; standard_encoder.encode(encoded, expected_decoded); - assert(mem.eql(u8, encoded, expected_encoded)); + testing.expectEqualSlices(u8, expected_encoded, encoded); } // Base64Decoder @@ -402,7 +403,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void var buffer: [0x100]u8 = undefined; var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)]; try standard_decoder.decode(decoded, expected_encoded); - assert(mem.eql(u8, decoded, expected_decoded)); + testing.expectEqualSlices(u8, expected_decoded, decoded); } // Base64DecoderWithIgnore @@ -411,8 +412,8 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void var buffer: [0x100]u8 = undefined; var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded); - assert(written <= decoded.len); - assert(mem.eql(u8, decoded[0..written], expected_decoded)); + testing.expect(written <= decoded.len); + testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); } // Base64DecoderUnsafe @@ -420,7 +421,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void var buffer: [0x100]u8 = undefined; var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)]; standard_decoder_unsafe.decode(decoded, expected_encoded); - assert(mem.eql(u8, decoded, expected_decoded)); + testing.expectEqualSlices(u8, expected_decoded, decoded); } } @@ -429,7 +430,7 @@ fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !voi var buffer: [0x100]u8 = undefined; var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; var written = try standard_decoder_ignore_space.decode(decoded, encoded); - assert(mem.eql(u8, decoded[0..written], expected_decoded)); + testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); } fn testError(encoded: []const u8, expected_err: anyerror) !void { diff --git a/std/buf_map.zig b/std/buf_map.zig index a82d1b731a..f8d3d5e04d 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -2,7 +2,7 @@ const std = @import("index.zig"); const HashMap = std.HashMap; const mem = std.mem; const Allocator = mem.Allocator; -const assert = std.debug.assert; +const testing = std.testing; /// BufMap copies keys and values before they go into the map, and /// frees them when they get removed. @@ -16,7 +16,7 @@ pub const BufMap = struct { return self; } - pub fn deinit(self: *const BufMap) void { + pub fn deinit(self: *BufMap) void { var it = self.hash_map.iterator(); while (true) { const entry = it.next() orelse break; @@ -27,16 +27,34 @@ pub const BufMap = struct { self.hash_map.deinit(); } - pub fn set(self: *BufMap, key: []const u8, value: []const u8) !void { - self.delete(key); - const key_copy = try self.copy(key); - errdefer self.free(key_copy); - const value_copy = try self.copy(value); - errdefer self.free(value_copy); - _ = try self.hash_map.put(key_copy, value_copy); + /// Same as `set` but the key and value become owned by the BufMap rather + /// than being copied. + /// If `setMove` fails, the ownership of key and value does not transfer. + pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void { + const get_or_put = try self.hash_map.getOrPut(key); + if (get_or_put.found_existing) { + self.free(get_or_put.kv.key); + get_or_put.kv.key = key; + } + get_or_put.kv.value = value; } - pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 { + /// `key` and `value` are copied into the BufMap. + pub fn set(self: *BufMap, key: []const u8, value: []const u8) !void { + const value_copy = try self.copy(value); + errdefer self.free(value_copy); + // Avoid copying key if it already exists + const get_or_put = try self.hash_map.getOrPut(key); + if (!get_or_put.found_existing) { + get_or_put.kv.key = self.copy(key) catch |err| { + _ = self.hash_map.remove(key); + return err; + }; + } + get_or_put.kv.value = value_copy; + } + + pub fn get(self: BufMap, key: []const u8) ?[]const u8 { const entry = self.hash_map.get(key) orelse return null; return entry.value; } @@ -47,7 +65,7 @@ pub const BufMap = struct { self.free(entry.value); } - pub fn count(self: *const BufMap) usize { + pub fn count(self: BufMap) usize { return self.hash_map.count(); } @@ -55,11 +73,11 @@ pub const BufMap = struct { return self.hash_map.iterator(); } - fn free(self: *const BufMap, value: []const u8) void { + fn free(self: BufMap, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: *const BufMap, value: []const u8) ![]const u8 { + fn copy(self: BufMap, value: []const u8) ![]u8 { return mem.dupe(self.hash_map.allocator, u8, value); } }; @@ -72,17 +90,17 @@ test "BufMap" { defer bufmap.deinit(); try bufmap.set("x", "1"); - assert(mem.eql(u8, bufmap.get("x").?, "1")); - assert(1 == bufmap.count()); + testing.expect(mem.eql(u8, bufmap.get("x").?, "1")); + testing.expect(1 == bufmap.count()); try bufmap.set("x", "2"); - assert(mem.eql(u8, bufmap.get("x").?, "2")); - assert(1 == bufmap.count()); + testing.expect(mem.eql(u8, bufmap.get("x").?, "2")); + testing.expect(1 == bufmap.count()); try bufmap.set("x", "3"); - assert(mem.eql(u8, bufmap.get("x").?, "3")); - assert(1 == bufmap.count()); + testing.expect(mem.eql(u8, bufmap.get("x").?, "3")); + testing.expect(1 == bufmap.count()); bufmap.delete("x"); - assert(0 == bufmap.count()); + testing.expect(0 == bufmap.count()); } diff --git a/std/buf_set.zig b/std/buf_set.zig index ab2d8e7c34..7ccd94c179 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -2,7 +2,7 @@ const std = @import("index.zig"); const HashMap = @import("hash_map.zig").HashMap; const mem = @import("mem.zig"); const Allocator = mem.Allocator; -const assert = std.debug.assert; +const testing = std.testing; pub const BufSet = struct { hash_map: BufSetHashMap, @@ -68,9 +68,9 @@ test "BufSet" { defer bufset.deinit(); try bufset.put("x"); - assert(bufset.count() == 1); + testing.expect(bufset.count() == 1); bufset.delete("x"); - assert(bufset.count() == 0); + testing.expect(bufset.count() == 0); try bufset.put("x"); try bufset.put("y"); diff --git a/std/buffer.zig b/std/buffer.zig index 2b71c26749..371655f1e5 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -3,6 +3,7 @@ const debug = std.debug; const mem = std.mem; const Allocator = mem.Allocator; const assert = debug.assert; +const testing = std.testing; const ArrayList = std.ArrayList; /// A buffer that allocates memory and maintains a null byte at the end. @@ -141,19 +142,19 @@ test "simple Buffer" { const cstr = @import("cstr.zig"); var buf = try Buffer.init(debug.global_allocator, ""); - assert(buf.len() == 0); + testing.expect(buf.len() == 0); try buf.append("hello"); try buf.append(" "); try buf.append("world"); - assert(buf.eql("hello world")); - assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst())); + testing.expect(buf.eql("hello world")); + testing.expect(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst())); var buf2 = try Buffer.initFromBuffer(buf); - assert(buf.eql(buf2.toSliceConst())); + testing.expect(buf.eql(buf2.toSliceConst())); - assert(buf.startsWith("hell")); - assert(buf.endsWith("orld")); + testing.expect(buf.startsWith("hell")); + testing.expect(buf.endsWith("orld")); try buf2.resize(4); - assert(buf.startsWith(buf2.toSlice())); + testing.expect(buf.startsWith(buf2.toSlice())); } diff --git a/std/build.zig b/std/build.zig index 90f5bec656..0dbbded802 100644 --- a/std/build.zig +++ b/std/build.zig @@ -89,7 +89,7 @@ pub const Builder = struct { }; pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { - const env_map = allocator.createOne(BufMap) catch unreachable; + const env_map = allocator.create(BufMap) catch unreachable; env_map.* = os.getEnvMap(allocator) catch unreachable; var self = Builder{ .zig_exe = zig_exe, @@ -145,8 +145,8 @@ pub const Builder = struct { pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default - self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; - self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; + self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable; + self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable; } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -170,7 +170,8 @@ pub const Builder = struct { } pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { - const test_step = self.allocator.create(TestStep.init(self, root_src)) catch unreachable; + const test_step = self.allocator.create(TestStep) catch unreachable; + test_step.* = TestStep.init(self, root_src); return test_step; } @@ -202,18 +203,21 @@ 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.init(self, file_path, data)) catch unreachable; + const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; + write_file_step.* = WriteFileStep.init(self, file_path, data); return write_file_step; } pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep.init(self, data)) catch unreachable; + const log_step = self.allocator.create(LogStep) catch unreachable; + log_step.* = LogStep.init(self, data); return log_step; } pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { - const remove_dir_step = self.allocator.create(RemoveDirStep.init(self, dir_path)) catch unreachable; + const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; + remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } @@ -320,7 +324,7 @@ pub const Builder = struct { fn processNixOSEnvVars(self: *Builder) void { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { - var it = mem.split(nix_cflags_compile, " "); + var it = mem.tokenize(nix_cflags_compile, " "); while (true) { const word = it.next() orelse break; if (mem.eql(u8, word, "-isystem")) { @@ -338,7 +342,7 @@ pub const Builder = struct { assert(err == error.EnvironmentVariableNotFound); } if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { - var it = mem.split(nix_ldflags, " "); + var it = mem.tokenize(nix_ldflags, " "); while (true) { const word = it.next() orelse break; if (mem.eql(u8, word, "-rpath")) { @@ -414,10 +418,11 @@ pub const Builder = struct { } pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { - const step_info = self.allocator.create(TopLevelStep{ + const step_info = self.allocator.create(TopLevelStep) catch unreachable; + step_info.* = TopLevelStep{ .step = Step.initNoOp(name, self.allocator), .description = description, - }) catch unreachable; + }; self.top_level_steps.append(step_info) catch unreachable; return &step_info.step; } @@ -613,10 +618,14 @@ pub const Builder = struct { ///::dest_rel_path is relative to prefix path or it can be an absolute path pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { - const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; + const full_dest_path = os.path.resolve( + self.allocator, + [][]const u8{ self.prefix, dest_rel_path }, + ) catch unreachable; self.pushInstalledFile(full_dest_path); - const install_step = self.allocator.create(InstallFileStep.init(self, src_path, full_dest_path)) catch unreachable; + const install_step = self.allocator.create(InstallFileStep) catch unreachable; + install_step.* = InstallFileStep.init(self, src_path, full_dest_path); return install_step; } @@ -647,7 +656,7 @@ pub const Builder = struct { } fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { - return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable; + return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable; } pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { @@ -670,7 +679,7 @@ pub const Builder = struct { if (os.path.isAbsolute(name)) { return name; } - const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension)); + const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -683,9 +692,9 @@ pub const Builder = struct { if (os.path.isAbsolute(name)) { return name; } - var it = mem.split(PATH, []u8{os.path.delimiter}); + var it = mem.tokenize(PATH, []u8{os.path.delimiter}); while (it.next()) |path| { - const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension)); + const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -699,7 +708,7 @@ pub const Builder = struct { return name; } for (paths) |path| { - const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension)); + const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -865,43 +874,51 @@ pub const LibExeObjStep = struct { }; pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, false, ver)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); return self; } pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: Version) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Lib, version, false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Lib, version, false); return self; } pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0))) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); return self; } pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0))) catch unreachable; + 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 createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); self.object_src = src; return self; } pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0))) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0)); return self; } pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); return self; } @@ -1099,7 +1116,10 @@ pub const LibExeObjStep = struct { } pub fn getOutputPath(self: *LibExeObjStep) []const u8 { - return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; + return if (self.output_path) |output_path| output_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_filename }, + ) catch unreachable; } pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { @@ -1112,7 +1132,10 @@ pub const LibExeObjStep = struct { } pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { - return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; + return if (self.output_h_path) |output_h_path| output_h_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_h_filename }, + ) catch unreachable; } pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { @@ -1212,7 +1235,10 @@ pub const LibExeObjStep = struct { } if (self.build_options_contents.len() > 0) { - const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name)); + const build_options_file = try os.path.join( + builder.allocator, + [][]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"); try zig_args.append("build_options"); @@ -1462,7 +1488,10 @@ pub const LibExeObjStep = struct { cc_args.append("-c") catch unreachable; cc_args.append(abs_source_file) catch unreachable; - const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; + const cache_o_src = os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, source_file }, + ) catch unreachable; if (os.path.dirname(cache_o_src)) |cache_o_dir| { try builder.makePath(cache_o_dir); } @@ -1514,7 +1543,10 @@ pub const LibExeObjStep = struct { cc_args.append("-current_version") catch unreachable; cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; - const install_name = builder.pathFromRoot(os.path.join(builder.allocator, builder.cache_root, self.major_only_filename) catch unreachable); + const install_name = builder.pathFromRoot(os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, self.major_only_filename }, + ) catch unreachable); cc_args.append("-install_name") catch unreachable; cc_args.append(install_name) catch unreachable; } else { @@ -1580,7 +1612,10 @@ pub const LibExeObjStep = struct { cc_args.append("-c") catch unreachable; cc_args.append(abs_source_file) catch unreachable; - const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; + const cache_o_src = os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, source_file }, + ) catch unreachable; if (os.path.dirname(cache_o_src)) |cache_o_dir| { try builder.makePath(cache_o_dir); } @@ -1672,6 +1707,7 @@ pub const TestStep = struct { no_rosegment: bool, output_path: ?[]const u8, system_linker_hack: bool, + override_std_dir: ?[]const u8, pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1693,6 +1729,7 @@ pub const TestStep = struct { .no_rosegment = false, .output_path = null, .system_linker_hack = false, + .override_std_dir = null, }; } @@ -1723,6 +1760,10 @@ pub const TestStep = struct { self.build_mode = mode; } + pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void { + self.override_std_dir = dir_path; + } + pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { self.output_path = file_path; @@ -1737,7 +1778,10 @@ pub const TestStep = struct { return output_path; } else { const basename = self.builder.fmt("test{}", self.target.exeFileExt()); - return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable; + return os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, basename }, + ) catch unreachable; } } @@ -1900,6 +1944,10 @@ pub const TestStep = struct { if (self.system_linker_hack) { try zig_args.append("--system-linker-hack"); } + if (self.override_std_dir) |dir| { + try zig_args.append("--override-std-dir"); + try zig_args.append(builder.pathFromRoot(dir)); + } try builder.spawnChild(zig_args.toSliceConst()); } @@ -1914,13 +1962,14 @@ pub const CommandStep = struct { /// ::argv is copied. pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { - const self = builder.allocator.create(CommandStep{ + const self = builder.allocator.create(CommandStep) catch unreachable; + self.* = CommandStep{ .builder = builder, .step = Step.init(argv[0], builder.allocator, make), .argv = builder.allocator.alloc([]u8, argv.len) catch unreachable, .cwd = cwd, .env_map = env_map, - }) catch unreachable; + }; mem.copy([]const u8, self.argv, argv); self.step.name = self.argv[0]; @@ -1949,17 +1998,27 @@ const InstallArtifactStep = struct { LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; - const self = builder.allocator.create(Self{ + const self = builder.allocator.create(Self) catch unreachable; + self.* = Self{ .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, - .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable, - }) catch unreachable; + .dest_file = os.path.join( + builder.allocator, + [][]const u8{ dest_dir, artifact.out_filename }, + ) catch unreachable, + }; self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { - builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable); - builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable); + builder.pushInstalledFile(os.path.join( + builder.allocator, + [][]const u8{ builder.lib_dir, artifact.major_only_filename }, + ) catch unreachable); + builder.pushInstalledFile(os.path.join( + builder.allocator, + [][]const u8{ builder.lib_dir, artifact.name_only_filename }, + ) catch unreachable); } return self; } @@ -2115,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj const out_dir = os.path.dirname(output_path) orelse "."; const out_basename = os.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable; + const major_only_path = os.path.join( + allocator, + [][]const u8{ out_dir, filename_major_only }, + ) catch unreachable; os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { 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 = os.path.join(allocator, out_dir, filename_name_only) catch unreachable; + const name_only_path = os.path.join( + allocator, + [][]const u8{ out_dir, filename_name_only }, + ) catch unreachable; os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); return err; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 86c5e4828e..4f0ea4a02b 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -73,8 +73,8 @@ pub const sockaddr_in6 = extern struct { }; pub const timeval = extern struct { - tv_sec: isize, - tv_usec: isize, + tv_sec: c_long, + tv_usec: i32, }; pub const timezone = extern struct { @@ -176,6 +176,24 @@ pub const kevent64_s = extern struct { ext: [2]u64, }; +pub const mach_port_t = c_uint; +pub const clock_serv_t = mach_port_t; +pub const clock_res_t = c_int; +pub const mach_port_name_t = natural_t; +pub const natural_t = c_uint; +pub const mach_timespec_t = extern struct { + tv_sec: c_uint, + tv_nsec: clock_res_t, +}; +pub const kern_return_t = c_int; +pub const host_t = mach_port_t; +pub const CALENDAR_CLOCK = 1; + +pub extern fn mach_host_self() mach_port_t; +pub extern fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t; +pub extern fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t; +pub extern fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t; + // sys/types.h on macos uses #pragma pack() so these checks are // to make sure the struct is laid out the same. These values were // produced from C code using the offsetof macro. diff --git a/std/c/freebsd.zig b/std/c/freebsd.zig index 421e964827..2f2f4c0a1b 100644 --- a/std/c/freebsd.zig +++ b/std/c/freebsd.zig @@ -1,5 +1,3 @@ -const timespec = @import("../os/freebsd/index.zig").timespec; - extern "c" fn __error() *c_int; pub const _errno = __error; @@ -15,6 +13,17 @@ pub extern "c" fn kevent( pub extern "c" fn sysctl(name: [*]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 getdirentries(fd: c_int, buf_ptr: [*]u8, nbytes: usize, basep: *i64) usize; +pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; +pub extern "c" fn pipe2(arg0: *[2]c_int, arg1: u32) c_int; +pub extern "c" fn preadv(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; +pub extern "c" fn pwritev(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; +pub extern "c" fn openat(fd: c_int, path: ?[*]const u8, flags: c_int) c_int; +pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; +pub extern "c" fn setuid(uid: c_uint) c_int; +pub extern "c" fn kill(pid: c_int, sig: c_int) c_int; +pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; +pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. pub const Kevent = extern struct { @@ -31,3 +40,81 @@ pub const pthread_attr_t = extern struct { __size: [56]u8, __align: c_long, }; + +pub const msghdr = extern struct { + msg_name: *u8, + msg_namelen: socklen_t, + msg_iov: *iovec, + msg_iovlen: i32, + __pad1: i32, + msg_control: *u8, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const Stat = extern struct { + dev: u64, + ino: u64, + nlink: usize, + + mode: u16, + __pad0: u16, + uid: u32, + gid: u32, + __pad1: u32, + rdev: u64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + birthtim: timespec, + + size: i64, + blocks: i64, + blksize: isize, + flags: u32, + gen: u64, + __spare: [10]u64, +}; + +pub const timespec = extern struct { + tv_sec: isize, + tv_nsec: isize, +}; + +pub const dirent = extern struct { + d_fileno: usize, + d_off: i64, + d_reclen: u16, + d_type: u8, + d_pad0: u8, + d_namlen: u16, + d_pad1: u16, + d_name: [256]u8, +}; + +pub const in_port_t = u16; +pub const sa_family_t = u16; + +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + addr: [16]u8, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; diff --git a/std/c/index.zig b/std/c/index.zig index 4aab39d931..058d879c09 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -44,7 +44,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) 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 gettimeofday(tv: ?*timeval, tz: ?*timezone) 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; diff --git a/std/coff.zig b/std/coff.zig index 6a1aa34b46..53bd2a4b7a 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -51,7 +51,7 @@ pub const Coff = struct { // Seek to PE File Header (coff header) try self.in_file.seekTo(pe_pointer_offset); - const pe_magic_offset = try in.readIntLe(u32); + const pe_magic_offset = try in.readIntLittle(u32); try self.in_file.seekTo(pe_magic_offset); var pe_header_magic: [4]u8 = undefined; @@ -60,13 +60,13 @@ pub const Coff = struct { return error.InvalidPEHeader; self.coff_header = CoffHeader{ - .machine = try in.readIntLe(u16), - .number_of_sections = try in.readIntLe(u16), - .timedate_stamp = try in.readIntLe(u32), - .pointer_to_symbol_table = try in.readIntLe(u32), - .number_of_symbols = try in.readIntLe(u32), - .size_of_optional_header = try in.readIntLe(u16), - .characteristics = try in.readIntLe(u16), + .machine = try in.readIntLittle(u16), + .number_of_sections = try in.readIntLittle(u16), + .timedate_stamp = try in.readIntLittle(u32), + .pointer_to_symbol_table = try in.readIntLittle(u32), + .number_of_symbols = try in.readIntLittle(u32), + .size_of_optional_header = try in.readIntLittle(u16), + .characteristics = try in.readIntLittle(u16), }; switch (self.coff_header.machine) { @@ -79,7 +79,7 @@ pub const Coff = struct { fn loadOptionalHeader(self: *Coff, file_stream: *os.File.InStream) !void { const in = &file_stream.stream; - self.pe_header.magic = try in.readIntLe(u16); + self.pe_header.magic = try in.readIntLittle(u16); // For now we're only interested in finding the reference to the .pdb, // so we'll skip most of this header, which size is different in 32 // 64 bits by the way. @@ -93,14 +93,14 @@ pub const Coff = struct { try self.in_file.seekForward(skip_size); - const number_of_rva_and_sizes = try in.readIntLe(u32); + const number_of_rva_and_sizes = try in.readIntLittle(u32); if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return error.InvalidPEHeader; for (self.pe_header.data_directory) |*data_dir| { data_dir.* = OptionalHeader.DataDirectory{ - .virtual_address = try in.readIntLe(u32), - .size = try in.readIntLe(u32), + .virtual_address = try in.readIntLittle(u32), + .size = try in.readIntLittle(u32), }; } } @@ -124,7 +124,7 @@ pub const Coff = struct { if (!mem.eql(u8, cv_signature, "RSDS")) return error.InvalidPEMagic; try in.readNoEof(self.guid[0..]); - self.age = try in.readIntLe(u32); + self.age = try in.readIntLittle(u32); // Finally read the null-terminated string. var byte = try in.readByte(); @@ -157,15 +157,15 @@ pub const Coff = struct { try self.sections.append(Section{ .header = SectionHeader{ .name = name, - .misc = SectionHeader.Misc{ .physical_address = try in.readIntLe(u32) }, - .virtual_address = try in.readIntLe(u32), - .size_of_raw_data = try in.readIntLe(u32), - .pointer_to_raw_data = try in.readIntLe(u32), - .pointer_to_relocations = try in.readIntLe(u32), - .pointer_to_line_numbers = try in.readIntLe(u32), - .number_of_relocations = try in.readIntLe(u16), - .number_of_line_numbers = try in.readIntLe(u16), - .characteristics = try in.readIntLe(u32), + .misc = SectionHeader.Misc{ .physical_address = try in.readIntLittle(u32) }, + .virtual_address = try in.readIntLittle(u32), + .size_of_raw_data = try in.readIntLittle(u32), + .pointer_to_raw_data = try in.readIntLittle(u32), + .pointer_to_relocations = try in.readIntLittle(u32), + .pointer_to_line_numbers = try in.readIntLittle(u32), + .number_of_relocations = try in.readIntLittle(u16), + .number_of_line_numbers = try in.readIntLittle(u16), + .characteristics = try in.readIntLittle(u32), }, }); } diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index dc68d806d2..e3de65916a 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -123,7 +123,8 @@ fn Blake2s(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 32]; for (rr) |s, j| { - mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); } } @@ -134,7 +135,8 @@ fn Blake2s(comptime out_len: usize) type { var v: [16]u32 = undefined; for (m) |*r, i| { - r.* = mem.readIntLE(u32, b[4 * i .. 4 * i + 4]); + // TODO https://github.com/ziglang/zig/issues/863 + r.* = mem.readIntSliceLittle(u32, b[4 * i .. 4 * i + 4]); } var k: usize = 0; @@ -356,7 +358,8 @@ fn Blake2b(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 64]; for (rr) |s, j| { - mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceLittle(u64, out[8 * j .. 8 * j + 8], s); } } @@ -367,7 +370,7 @@ fn Blake2b(comptime out_len: usize) type { var v: [16]u64 = undefined; for (m) |*r, i| { - r.* = mem.readIntLE(u64, b[8 * i .. 8 * i + 8]); + r.* = mem.readIntSliceLittle(u64, b[8 * i .. 8 * i + 8]); } var k: usize = 0; diff --git a/std/crypto/chacha20.zig b/std/crypto/chacha20.zig index 059bc82088..d964f4c03d 100644 --- a/std/crypto/chacha20.zig +++ b/std/crypto/chacha20.zig @@ -4,6 +4,7 @@ const std = @import("../index.zig"); const mem = std.mem; const endian = std.endian; const assert = std.debug.assert; +const testing = std.testing; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -59,7 +60,8 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { } for (x) |_, i| { - mem.writeInt(out[4 * i .. 4 * i + 4], x[i] +% input[i], builtin.Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceLittle(u32, out[4 * i .. 4 * i + 4], x[i] +% input[i]); } } @@ -70,10 +72,10 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo const c = "expand 32-byte k"; const constant_le = []u32{ - mem.readIntLE(u32, c[0..4]), - mem.readIntLE(u32, c[4..8]), - mem.readIntLE(u32, c[8..12]), - mem.readIntLE(u32, c[12..16]), + mem.readIntSliceLittle(u32, c[0..4]), + mem.readIntSliceLittle(u32, c[4..8]), + mem.readIntSliceLittle(u32, c[8..12]), + mem.readIntSliceLittle(u32, c[12..16]), }; mem.copy(u32, ctx[0..], constant_le[0..4]); @@ -117,19 +119,19 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntLE(u32, key[0..4]); - k[1] = mem.readIntLE(u32, key[4..8]); - k[2] = mem.readIntLE(u32, key[8..12]); - k[3] = mem.readIntLE(u32, key[12..16]); - k[4] = mem.readIntLE(u32, key[16..20]); - k[5] = mem.readIntLE(u32, key[20..24]); - k[6] = mem.readIntLE(u32, key[24..28]); - k[7] = mem.readIntLE(u32, key[28..32]); + k[0] = mem.readIntSliceLittle(u32, key[0..4]); + k[1] = mem.readIntSliceLittle(u32, key[4..8]); + k[2] = mem.readIntSliceLittle(u32, key[8..12]); + k[3] = mem.readIntSliceLittle(u32, key[12..16]); + k[4] = mem.readIntSliceLittle(u32, key[16..20]); + k[5] = mem.readIntSliceLittle(u32, key[20..24]); + k[6] = mem.readIntSliceLittle(u32, key[24..28]); + k[7] = mem.readIntSliceLittle(u32, key[28..32]); c[0] = counter; - c[1] = mem.readIntLE(u32, nonce[0..4]); - c[2] = mem.readIntLE(u32, nonce[4..8]); - c[3] = mem.readIntLE(u32, nonce[8..12]); + c[1] = mem.readIntSliceLittle(u32, nonce[0..4]); + c[2] = mem.readIntSliceLittle(u32, nonce[4..8]); + c[3] = mem.readIntSliceLittle(u32, nonce[8..12]); chaCha20_internal(out, in, k, c); } @@ -144,19 +146,19 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32] var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntLE(u32, key[0..4]); - k[1] = mem.readIntLE(u32, key[4..8]); - k[2] = mem.readIntLE(u32, key[8..12]); - k[3] = mem.readIntLE(u32, key[12..16]); - k[4] = mem.readIntLE(u32, key[16..20]); - k[5] = mem.readIntLE(u32, key[20..24]); - k[6] = mem.readIntLE(u32, key[24..28]); - k[7] = mem.readIntLE(u32, key[28..32]); + k[0] = mem.readIntSliceLittle(u32, key[0..4]); + k[1] = mem.readIntSliceLittle(u32, key[4..8]); + k[2] = mem.readIntSliceLittle(u32, key[8..12]); + k[3] = mem.readIntSliceLittle(u32, key[12..16]); + k[4] = mem.readIntSliceLittle(u32, key[16..20]); + k[5] = mem.readIntSliceLittle(u32, key[20..24]); + k[6] = mem.readIntSliceLittle(u32, key[24..28]); + k[7] = mem.readIntSliceLittle(u32, key[28..32]); c[0] = @truncate(u32, counter); c[1] = @truncate(u32, counter >> 32); - c[2] = mem.readIntLE(u32, nonce[0..4]); - c[3] = mem.readIntLE(u32, nonce[4..8]); + c[2] = mem.readIntSliceLittle(u32, nonce[0..4]); + c[3] = mem.readIntSliceLittle(u32, nonce[4..8]); const block_size = (1 << 6); const big_block = (block_size << 32); @@ -215,12 +217,12 @@ test "crypto.chacha20 test vector sunscreen" { }; chaCha20IETF(result[0..], input[0..], 1, key, nonce); - assert(mem.eql(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); - assert(mem.compare(u8, input, plaintext) == mem.Compare.Equal); + testing.expect(mem.compare(u8, input, plaintext) == mem.Compare.Equal); } // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 @@ -255,7 +257,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); - assert(mem.eql(u8, expected_result, result)); + testing.expectEqualSlices(u8, expected_result, result); } test "crypto.chacha20 test vector 2" { @@ -289,7 +291,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); - assert(mem.eql(u8, expected_result, result)); + testing.expectEqualSlices(u8, expected_result, result); } test "crypto.chacha20 test vector 3" { @@ -323,7 +325,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); - assert(mem.eql(u8, expected_result, result)); + testing.expectEqualSlices(u8, expected_result, result); } test "crypto.chacha20 test vector 4" { @@ -357,7 +359,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); - assert(mem.eql(u8, expected_result, result)); + testing.expectEqualSlices(u8, expected_result, result); } test "crypto.chacha20 test vector 5" { @@ -429,5 +431,5 @@ test "crypto.chacha20 test vector 5" { }; chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); - assert(mem.eql(u8, expected_result, result)); + testing.expectEqualSlices(u8, expected_result, result); } diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index 8663fa751f..994a7fa25c 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -112,7 +112,8 @@ pub const Md5 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); } } diff --git a/std/crypto/poly1305.zig b/std/crypto/poly1305.zig index a5d9fcdf57..64adb17c45 100644 --- a/std/crypto/poly1305.zig +++ b/std/crypto/poly1305.zig @@ -6,8 +6,8 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const Endian = builtin.Endian; -const readInt = std.mem.readInt; -const writeInt = std.mem.writeInt; +const readIntSliceLittle = std.mem.readIntSliceLittle; +const writeIntSliceLittle = std.mem.writeIntSliceLittle; pub const Poly1305 = struct { const Self = @This(); @@ -59,19 +59,19 @@ pub const Poly1305 = struct { { var i: usize = 0; while (i < 1) : (i += 1) { - ctx.r[0] = readInt(key[0..4], u32, Endian.Little) & 0x0fffffff; + ctx.r[0] = readIntSliceLittle(u32, key[0..4]) & 0x0fffffff; } } { var i: usize = 1; while (i < 4) : (i += 1) { - ctx.r[i] = readInt(key[i * 4 .. i * 4 + 4], u32, Endian.Little) & 0x0ffffffc; + ctx.r[i] = readIntSliceLittle(u32, key[i * 4 .. i * 4 + 4]) & 0x0ffffffc; } } { var i: usize = 0; while (i < 4) : (i += 1) { - ctx.pad[i] = readInt(key[i * 4 + 16 .. i * 4 + 16 + 4], u32, Endian.Little); + ctx.pad[i] = readIntSliceLittle(u32, key[i * 4 + 16 .. i * 4 + 16 + 4]); } } @@ -168,10 +168,10 @@ pub const Poly1305 = struct { const nb_blocks = nmsg.len >> 4; var i: usize = 0; while (i < nb_blocks) : (i += 1) { - ctx.c[0] = readInt(nmsg[0..4], u32, Endian.Little); - ctx.c[1] = readInt(nmsg[4..8], u32, Endian.Little); - ctx.c[2] = readInt(nmsg[8..12], u32, Endian.Little); - ctx.c[3] = readInt(nmsg[12..16], u32, Endian.Little); + ctx.c[0] = readIntSliceLittle(u32, nmsg[0..4]); + ctx.c[1] = readIntSliceLittle(u32, nmsg[4..8]); + ctx.c[2] = readIntSliceLittle(u32, nmsg[8..12]); + ctx.c[3] = readIntSliceLittle(u32, nmsg[12..16]); polyBlock(ctx); nmsg = nmsg[16..]; } @@ -210,10 +210,11 @@ pub const Poly1305 = struct { const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000 - writeInt(out[0..], @truncate(u32, uu0), Endian.Little); - writeInt(out[4..], @truncate(u32, uu1), Endian.Little); - writeInt(out[8..], @truncate(u32, uu2), Endian.Little); - writeInt(out[12..], @truncate(u32, uu3), Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + writeIntSliceLittle(u32, out[0..], @truncate(u32, uu0)); + writeIntSliceLittle(u32, out[4..], @truncate(u32, uu1)); + writeIntSliceLittle(u32, out[8..], @truncate(u32, uu2)); + writeIntSliceLittle(u32, out[12..], @truncate(u32, uu3)); ctx.secureZero(); } @@ -229,5 +230,5 @@ test "poly1305 rfc7439 vector1" { var mac: [16]u8 = undefined; Poly1305.create(mac[0..], msg, key); - std.debug.assert(std.mem.eql(u8, mac, expected_mac)); + std.testing.expectEqualSlices(u8, expected_mac, mac); } diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 1cb0b17434..d5aab8f33f 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -109,7 +109,8 @@ pub const Sha1 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); } } diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index 7e9749364b..0476a3a25e 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -167,7 +167,8 @@ fn Sha2_32(comptime params: Sha2Params32) type { const rr = d.s[0 .. params.out_len / 32]; for (rr) |s, j| { - mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); } } @@ -508,7 +509,8 @@ fn Sha2_64(comptime params: Sha2Params64) type { const rr = d.s[0 .. params.out_len / 64]; for (rr) |s, j| { - mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Big); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceBig(u64, out[8 * j .. 8 * j + 8], s); } } diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index 881370e686..e686e1337c 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -120,7 +120,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { var c = []const u64{0} ** 5; for (s) |*r, i| { - r.* = mem.readIntLE(u64, d[8 * i .. 8 * i + 8]); + r.* = mem.readIntSliceLittle(u64, d[8 * i .. 8 * i + 8]); } comptime var x: usize = 0; @@ -167,7 +167,8 @@ fn keccak_f(comptime F: usize, d: []u8) void { } for (s) |r, i| { - mem.writeInt(d[8 * i .. 8 * i + 8], r, builtin.Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + mem.writeIntSliceLittle(u64, d[8 * i .. 8 * i + 8], r); } } diff --git a/std/crypto/test.zig b/std/crypto/test.zig index 3fa24272e5..258cdf7320 100644 --- a/std/crypto/test.zig +++ b/std/crypto/test.zig @@ -1,6 +1,7 @@ -const debug = @import("../debug/index.zig"); -const mem = @import("../mem.zig"); -const fmt = @import("../fmt/index.zig"); +const std = @import("../index.zig"); +const testing = std.testing; +const mem = std.mem; +const fmt = std.fmt; // Hash using the specified hasher `H` asserting `expected == H(input)`. pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) void { @@ -17,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; } - debug.assert(mem.eql(u8, expected_bytes, input)); + testing.expectEqualSlices(u8, expected_bytes, input); } diff --git a/std/crypto/x25519.zig b/std/crypto/x25519.zig index 281813b457..9349569f97 100644 --- a/std/crypto/x25519.zig +++ b/std/crypto/x25519.zig @@ -7,8 +7,8 @@ const builtin = @import("builtin"); const fmt = std.fmt; const Endian = builtin.Endian; -const readInt = std.mem.readInt; -const writeInt = std.mem.writeInt; +const readIntSliceLittle = std.mem.readIntSliceLittle; +const writeIntSliceLittle = std.mem.writeIntSliceLittle; // Based on Supercop's ref10 implementation. pub const X25519 = struct { @@ -255,16 +255,16 @@ const Fe = struct { var t: [10]i64 = undefined; - t[0] = readInt(s[0..4], u32, Endian.Little); - t[1] = readInt(s[4..7], u32, Endian.Little) << 6; - t[2] = readInt(s[7..10], u32, Endian.Little) << 5; - t[3] = readInt(s[10..13], u32, Endian.Little) << 3; - t[4] = readInt(s[13..16], u32, Endian.Little) << 2; - t[5] = readInt(s[16..20], u32, Endian.Little); - t[6] = readInt(s[20..23], u32, Endian.Little) << 7; - t[7] = readInt(s[23..26], u32, Endian.Little) << 5; - t[8] = readInt(s[26..29], u32, Endian.Little) << 4; - t[9] = (readInt(s[29..32], u32, Endian.Little) & 0x7fffff) << 2; + 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[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; carry1(h, t[0..]); } @@ -544,14 +544,15 @@ const Fe = struct { ut[i] = @bitCast(u32, @intCast(i32, t[i])); } - writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little); - writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little); - writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little); - writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little); - writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little); - writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little); - writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little); - writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little); + // TODO https://github.com/ziglang/zig/issues/863 + writeIntSliceLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); + writeIntSliceLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); + writeIntSliceLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); + writeIntSliceLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); + writeIntSliceLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); + writeIntSliceLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); + writeIntSliceLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); + writeIntSliceLittle(u32, s[28..], (ut[8] >> 20) | (ut[9] << 6)); std.mem.secureZero(i64, t[0..]); } @@ -580,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.debug.assert(X25519.createPublicKey(pk_calculated[0..], sk)); - std.debug.assert(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" { @@ -592,8 +593,8 @@ test "x25519 rfc7748 vector1" { var output: [32]u8 = undefined; - std.debug.assert(X25519.create(output[0..], secret_key, public_key)); - std.debug.assert(std.mem.eql(u8, output, expected_output)); + std.testing.expect(X25519.create(output[0..], secret_key, public_key)); + std.testing.expect(std.mem.eql(u8, output, expected_output)); } test "x25519 rfc7748 vector2" { @@ -604,8 +605,8 @@ test "x25519 rfc7748 vector2" { var output: [32]u8 = undefined; - std.debug.assert(X25519.create(output[0..], secret_key, public_key)); - std.debug.assert(std.mem.eql(u8, output, expected_output)); + std.testing.expect(X25519.create(output[0..], secret_key, public_key)); + std.testing.expect(std.mem.eql(u8, output, expected_output)); } test "x25519 rfc7748 one iteration" { @@ -618,13 +619,13 @@ test "x25519 rfc7748 one iteration" { var i: usize = 0; while (i < 1) : (i += 1) { var output: [32]u8 = undefined; - std.debug.assert(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..]); } - std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); + std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); } test "x25519 rfc7748 1,000 iterations" { @@ -642,13 +643,13 @@ test "x25519 rfc7748 1,000 iterations" { var i: usize = 0; while (i < 1000) : (i += 1) { var output: [32]u8 = undefined; - std.debug.assert(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..]); } - std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); + std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); } test "x25519 rfc7748 1,000,000 iterations" { @@ -665,11 +666,11 @@ test "x25519 rfc7748 1,000,000 iterations" { var i: usize = 0; while (i < 1000000) : (i += 1) { var output: [32]u8 = undefined; - std.debug.assert(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..]); } - std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); + std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); } diff --git a/std/cstr.zig b/std/cstr.zig index a8aaf21279..abd2eac48f 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -2,7 +2,7 @@ const std = @import("index.zig"); const builtin = @import("builtin"); const debug = std.debug; const mem = std.mem; -const assert = debug.assert; +const testing = std.testing; pub const line_sep = switch (builtin.os) { builtin.Os.windows => "\r\n", @@ -42,8 +42,8 @@ test "cstr fns" { } fn testCStrFnsImpl() void { - assert(cmp(c"aoeu", c"aoez") == -1); - assert(len(c"123456789") == 9); + testing.expect(cmp(c"aoeu", c"aoez") == -1); + testing.expect(len(c"123456789") == 9); } /// Returns a mutable slice with 1 more byte of length which is a null byte. diff --git a/std/debug/index.zig b/std/debug/index.zig index 4a96e9d259..7e5be9acef 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -37,7 +37,6 @@ const Module = struct { var stderr_file: os.File = undefined; var stderr_file_out_stream: os.File.OutStream = undefined; -/// TODO multithreaded awareness var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; var stderr_mutex = std.Mutex.init(); pub fn warn(comptime fmt: []const u8, args: ...) void { @@ -108,37 +107,15 @@ pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { /// This function invokes undefined behavior when `ok` is `false`. /// In Debug and ReleaseSafe modes, calls to this function are always /// generated, and the `unreachable` statement triggers a panic. -/// In ReleaseFast and ReleaseSmall modes, calls to this function can be -/// optimized away. +/// In ReleaseFast and ReleaseSmall modes, calls to this function are +/// optimized away, and in fact the optimizer is able to use the assertion +/// in its heuristics. +/// Inside a test block, it is best to use the `std.testing` module rather +/// than this function, because this function may not detect a test failure +/// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert +/// function is the correct function to use. pub fn assert(ok: bool) void { - if (!ok) { - // In ReleaseFast test mode, we still want assert(false) to crash, so - // we insert an explicit call to @panic instead of unreachable. - // TODO we should use `assertOrPanic` in tests and remove this logic. - if (builtin.is_test) { - @panic("assertion failure"); - } else { - unreachable; // assertion failure - } - } -} - -/// TODO: add `==` operator for `error_union == error_set`, and then -/// remove this function -pub fn assertError(value: var, expected_error: anyerror) void { - if (value) { - @panic("expected error"); - } else |actual_error| { - assert(actual_error == expected_error); - } -} - -/// Call this function when you want to panic if the condition is not true. -/// If `ok` is `false`, this function will panic in every release mode. -pub fn assertOrPanic(ok: bool) void { - if (!ok) { - @panic("assertion failure"); - } + if (!ok) unreachable; // assertion failure } pub fn panic(comptime format: []const u8, args: ...) noreturn { @@ -247,8 +224,7 @@ pub fn writeCurrentStackTraceWindows( start_addr: ?usize, ) !void { var addr_buf: [1024]usize = undefined; - const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast - const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null); + const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); const addrs = addr_buf[0..n]; var start_i: usize = if (start_addr) |saddr| blk: { for (addrs) |addr, i| { @@ -264,7 +240,7 @@ pub fn writeCurrentStackTraceWindows( pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), - builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), + builtin.Os.linux, builtin.Os.freebsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), else => return error.UnsupportedOperatingSystem, } @@ -338,50 +314,74 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres switch (subsect_hdr.Kind) { pdb.DebugSubsectionKind.Lines => { - var line_index: usize = sect_offset; + var line_index = sect_offset; const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; line_index += @sizeOf(pdb.LineFragmentHeader); - - const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineBlockFragmentHeader); - - const has_column = line_hdr.Flags.LF_HaveColumns; - const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; - if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { - var line_i: usize = 0; + + // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) + // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, + // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. + const subsection_end_index = sect_offset + subsect_hdr.Length; + while (line_index < subsection_end_index) { + const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineBlockFragmentHeader); const start_line_index = line_index; - while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineNumberEntry); - const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); - const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; - if (relative_address >= vaddr_start and relative_address < vaddr_end) { + + const has_column = line_hdr.Flags.LF_HaveColumns; + + if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { + // All line entries are stored inside their line block by ascending start address. + // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. + // This is done with a simple linear search. + var line_i: u32 = 0; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineNumberEntry); + + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + if (relative_address <= vaddr_start) { + break; + } + } + + // line_i == 0 would mean that no matching LineNumberEntry was found. + if (line_i > 0) { const subsect_index = checksum_offset + block_hdr.NameIndex; const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; try di.pdb.string_table.seekTo(strtab_offset); const source_file_name = try di.pdb.string_table.readNullTermString(allocator); - const line = flags.Start; + + const line_entry_idx = line_i - 1; + const column = if (has_column) blk: { - line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; - line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i; - const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]); + const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; + const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; + const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); break :blk col_num_entry.StartColumn; } else 0; + + const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); + const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); + break :subsections LineInfo{ .allocator = allocator, .file_name = source_file_name, - .line = line, + .line = flags.Start, .column = column, }; } } - break :subsections null; + } + + // Checking that we are not reading garbage after the (possibly) multiple block fragments. + if (line_index != subsection_end_index) { + return error.InvalidDebugInfo; } }, else => {}, @@ -523,7 +523,7 @@ fn populateModule(di: *DebugInfo, mod: *Module) !void { const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; - const signature = try modi.stream.readIntLe(u32); + const signature = try modi.stream.readIntLittle(u32); if (signature != 4) return error.InvalidDebugInfo; @@ -717,7 +717,7 @@ pub const OpenSelfDebugInfoError = error{ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { switch (builtin.os) { - builtin.Os.linux => return openSelfDebugInfoLinux(allocator), + builtin.Os.linux, builtin.Os.freebsd => return openSelfDebugInfoLinux(allocator), builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), builtin.Os.windows => return openSelfDebugInfoWindows(allocator), else => return error.UnsupportedOperatingSystem, @@ -728,7 +728,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const self_file = try os.openSelfExe(); defer self_file.close(); - const coff_obj = try allocator.createOne(coff.Coff); + const coff_obj = try allocator.create(coff.Coff); coff_obj.* = coff.Coff{ .in_file = self_file, .allocator = allocator, @@ -752,14 +752,14 @@ 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 os.path.resolve(allocator, raw_path); + const path = try os.path.resolve(allocator, [][]const u8{raw_path}); try di.pdb.openFile(di.coff, path); var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; - const version = try pdb_stream.stream.readIntLe(u32); - const signature = try pdb_stream.stream.readIntLe(u32); - const age = try pdb_stream.stream.readIntLe(u32); + const version = try pdb_stream.stream.readIntLittle(u32); + 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..]); if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) @@ -767,7 +767,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { // We validated the executable and pdb match. const string_table_index = str_tab_index: { - const name_bytes_len = try pdb_stream.stream.readIntLe(u32); + const name_bytes_len = try pdb_stream.stream.readIntLittle(u32); const name_bytes = try allocator.alloc(u8, name_bytes_len); try pdb_stream.stream.readNoEof(name_bytes); @@ -797,8 +797,8 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { }; const bucket_list = try allocator.alloc(Bucket, present.len); for (present) |_| { - const name_offset = try pdb_stream.stream.readIntLe(u32); - const name_index = try pdb_stream.stream.readIntLe(u32); + 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); if (mem.eql(u8, name, "/names")) { break :str_tab_index name_index; @@ -859,7 +859,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); var sect_cont_offset: usize = 0; if (section_contrib_size != 0) { - const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32)); + const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32)); if (ver != pdb.SectionContrSubstreamVersion.Ver60) return error.InvalidDebugInfo; sect_cont_offset += @sizeOf(u32); @@ -879,11 +879,11 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { } fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { - const num_words = try stream.readIntLe(u32); + const num_words = try stream.readIntLittle(u32); var word_i: usize = 0; var list = ArrayList(usize).init(allocator); while (word_i != num_words) : (word_i += 1) { - const word = try stream.readIntLe(u32); + const word = try stream.readIntLittle(u32); var bit_i: u5 = 0; while (true) : (bit_i += 1) { if (word & (u32(1) << bit_i) != 0) { @@ -1013,7 +1013,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } } } - const sentinel = try allocator.createOne(macho.nlist_64); + const sentinel = try allocator.create(macho.nlist_64); sentinel.* = macho.nlist_64{ .n_strx = 0, .n_type = 36, @@ -1135,14 +1135,13 @@ pub const DebugInfo = switch (builtin.os) { return self.ofiles.allocator; } }, - builtin.Os.windows => struct { + builtin.Os.uefi, builtin.Os.windows => struct { pdb: pdb.Pdb, coff: *coff.Coff, sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - builtin.Os.linux => DwarfInfo, - builtin.Os.freebsd => struct {}, + builtin.Os.linux, builtin.Os.freebsd => DwarfInfo, else => @compileError("Unsupported OS"), }; @@ -1200,7 +1199,7 @@ const Constant = struct { fn asUnsignedLe(self: *const Constant) !u64 { if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; if (self.signed) return error.InvalidDebugInfo; - return mem.readInt(self.payload, u64, builtin.Endian.Little); + return mem.readVarInt(u64, self.payload, builtin.Endian.Little); } }; @@ -1331,7 +1330,7 @@ const LineNumberProgram = struct { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); + const file_name = try os.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(usize, self.prev_line) else 0, @@ -1381,7 +1380,7 @@ fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize } fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { - const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size); + const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size); return parseFormValueBlockLen(allocator, in_stream, block_len); } @@ -1395,11 +1394,11 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo } fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try in_stream.readIntLe(u64) else u64(try in_stream.readIntLe(u32)); + return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32)); } fn parseFormValueTargetAddrSize(in_stream: var) !u64 { - return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; + return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable; } fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { @@ -1408,7 +1407,7 @@ fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) } fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue { - const block_len = try in_stream.readIntLe(T); + const block_len = try in_stream.readIntLittle(T); return parseFormValueRefLen(allocator, in_stream, block_len); } @@ -1450,7 +1449,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 }, DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLe(u64) }, + DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLittle(u64) }, DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, @@ -1747,11 +1746,11 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr continue; } - const version = try di.dwarf_in_stream.readInt(di.endian, u16); + 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(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); + 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(); @@ -1820,7 +1819,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr return error.MissingDebugInfo; }, DW.LNE_set_address => { - const addr = try di.dwarf_in_stream.readInt(di.endian, usize); + const addr = try di.dwarf_in_stream.readInt(usize, di.endian); prog.address = addr; }, DW.LNE_define_file => { @@ -1882,7 +1881,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr prog.address += inc_addr; }, DW.LNS_fixed_advance_pc => { - const arg = try di.dwarf_in_stream.readInt(di.endian, u16); + const arg = try di.dwarf_in_stream.readInt(u16, di.endian); prog.address += arg; }, DW.LNS_set_prologue_end => {}, @@ -1914,10 +1913,10 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void { 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(di.endian, u16); + 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(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); + 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; @@ -1927,7 +1926,8 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void { try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64)); + const compile_unit_die = try di.allocator().create(Die); + compile_unit_die.* = try parseDie(di, abbrev_table, is_64); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; @@ -1978,8 +1978,8 @@ fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { 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.readIntLe(usize); - const end_addr = try di.dwarf_in_stream.readIntLe(usize); + 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; } @@ -2001,7 +2001,8 @@ fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { } fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { - const result = mem.readInt(ptr.*[0..@sizeOf(T)], T, endian); + // TODO https://github.com/ziglang/zig/issues/863 + const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian); ptr.* += @sizeOf(T); return result; } @@ -2017,11 +2018,12 @@ fn readByteSignedMem(ptr: *[*]const u8) i8 { } fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { - const first_32_bits = mem.readIntLE(u32, ptr.*[0..4]); + // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 + const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); is_64.* = (first_32_bits == 0xffffffff); if (is_64.*) { ptr.* += 4; - const result = mem.readIntLE(u64, ptr.*[0..8]); + const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); ptr.* += 8; return result; } else { @@ -2084,10 +2086,10 @@ fn readILeb128Mem(ptr: *[*]const u8) !i64 { } fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { - const first_32_bits = try in_stream.readIntLe(u32); + const first_32_bits = try in_stream.readIntLittle(u32); is_64.* = (first_32_bits == 0xffffffff); if (is_64.*) { - return in_stream.readIntLe(u64); + return in_stream.readIntLittle(u64); } else { if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; return u64(first_32_bits); diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 49f217bc8e..66026b1f29 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -6,6 +6,7 @@ const mem = std.mem; const cstr = std.cstr; const os = std.os; const assert = std.debug.assert; +const testing = std.testing; const elf = std.elf; const linux = os.linux; const windows = os.windows; @@ -19,7 +20,6 @@ pub const DynLib = switch (builtin.os) { }; pub const LinuxDynLib = struct { - allocator: *mem.Allocator, elf_lib: ElfLib, fd: i32, map_addr: usize, @@ -27,7 +27,7 @@ pub const LinuxDynLib = struct { /// Trusts the file pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { - const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY | linux.O_CLOEXEC); + const fd = try std.os.posixOpen(path, 0, linux.O_RDONLY | linux.O_CLOEXEC); errdefer std.os.close(fd); const size = @intCast(usize, (try std.os.posixFStat(fd)).size); @@ -45,7 +45,6 @@ pub const LinuxDynLib = struct { const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; return DynLib{ - .allocator = allocator, .elf_lib = try ElfLib.init(bytes), .fd = fd, .map_addr = addr, @@ -208,7 +207,7 @@ test "dynamic_library" { }; const dynlib = DynLib.open(std.debug.global_allocator, libname) catch |err| { - assert(err == error.FileNotFound); + testing.expect(err == error.FileNotFound); return; }; @panic("Expected error from function"); diff --git a/std/elf.zig b/std/elf.zig index cf7c29b1e5..6a564c3283 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -412,7 +412,7 @@ pub const Elf = struct { // skip over padding try seekable_stream.seekForward(9); - elf.file_type = switch (try in.readInt(elf.endian, u16)) { + elf.file_type = switch (try in.readInt(u16, elf.endian)) { 1 => FileType.Relocatable, 2 => FileType.Executable, 3 => FileType.Shared, @@ -420,7 +420,7 @@ pub const Elf = struct { else => return error.InvalidFormat, }; - elf.arch = switch (try in.readInt(elf.endian, u16)) { + elf.arch = switch (try in.readInt(u16, elf.endian)) { 0x02 => Arch.Sparc, 0x03 => Arch.x86, 0x08 => Arch.Mips, @@ -433,32 +433,32 @@ pub const Elf = struct { else => return error.InvalidFormat, }; - const elf_version = try in.readInt(elf.endian, u32); + const elf_version = try in.readInt(u32, elf.endian); if (elf_version != 1) return error.InvalidFormat; if (elf.is_64) { - elf.entry_addr = try in.readInt(elf.endian, u64); - elf.program_header_offset = try in.readInt(elf.endian, u64); - elf.section_header_offset = try in.readInt(elf.endian, u64); + elf.entry_addr = try in.readInt(u64, elf.endian); + 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(elf.endian, u32)); - elf.program_header_offset = u64(try in.readInt(elf.endian, u32)); - elf.section_header_offset = u64(try in.readInt(elf.endian, u32)); + 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)); } // skip over flags try seekable_stream.seekForward(4); - const header_size = try in.readInt(elf.endian, u16); + const header_size = try in.readInt(u16, elf.endian); if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { return error.InvalidFormat; } - const ph_entry_size = try in.readInt(elf.endian, u16); - const ph_entry_count = try in.readInt(elf.endian, u16); - const sh_entry_size = try in.readInt(elf.endian, u16); - const sh_entry_count = try in.readInt(elf.endian, u16); - elf.string_section_index = u64(try in.readInt(elf.endian, u16)); + const ph_entry_size = try in.readInt(u16, elf.endian); + 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 = u64(try in.readInt(u16, elf.endian)); if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; @@ -481,32 +481,32 @@ pub const Elf = struct { if (sh_entry_size != 64) return error.InvalidFormat; for (elf.section_headers) |*elf_section| { - elf_section.name = try in.readInt(elf.endian, u32); - elf_section.sh_type = try in.readInt(elf.endian, u32); - elf_section.flags = try in.readInt(elf.endian, u64); - elf_section.addr = try in.readInt(elf.endian, u64); - elf_section.offset = try in.readInt(elf.endian, u64); - elf_section.size = try in.readInt(elf.endian, u64); - elf_section.link = try in.readInt(elf.endian, u32); - elf_section.info = try in.readInt(elf.endian, u32); - elf_section.addr_align = try in.readInt(elf.endian, u64); - elf_section.ent_size = try in.readInt(elf.endian, u64); + elf_section.name = try in.readInt(u32, elf.endian); + elf_section.sh_type = try in.readInt(u32, elf.endian); + elf_section.flags = try in.readInt(u64, elf.endian); + elf_section.addr = try in.readInt(u64, elf.endian); + elf_section.offset = try in.readInt(u64, elf.endian); + elf_section.size = try in.readInt(u64, elf.endian); + elf_section.link = try in.readInt(u32, elf.endian); + elf_section.info = try in.readInt(u32, elf.endian); + elf_section.addr_align = try in.readInt(u64, elf.endian); + elf_section.ent_size = try in.readInt(u64, elf.endian); } } else { if (sh_entry_size != 40) return error.InvalidFormat; for (elf.section_headers) |*elf_section| { // TODO (multiple occurrences) allow implicit cast from %u32 -> %u64 ? - elf_section.name = try in.readInt(elf.endian, u32); - elf_section.sh_type = try in.readInt(elf.endian, u32); - elf_section.flags = u64(try in.readInt(elf.endian, u32)); - elf_section.addr = u64(try in.readInt(elf.endian, u32)); - elf_section.offset = u64(try in.readInt(elf.endian, u32)); - elf_section.size = u64(try in.readInt(elf.endian, u32)); - elf_section.link = try in.readInt(elf.endian, u32); - elf_section.info = try in.readInt(elf.endian, u32); - elf_section.addr_align = u64(try in.readInt(elf.endian, u32)); - elf_section.ent_size = u64(try in.readInt(elf.endian, u32)); + 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.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)); } } diff --git a/std/event/channel.zig b/std/event/channel.zig index 133ce1c69c..4af9bf612b 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -1,6 +1,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const testing = std.testing; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; const Loop = std.event.Loop; @@ -54,7 +55,8 @@ pub fn Channel(comptime T: type) type { const buffer_nodes = try loop.allocator.alloc(T, capacity); errdefer loop.allocator.free(buffer_nodes); - const self = try loop.allocator.create(SelfChannel{ + const self = try loop.allocator.create(SelfChannel); + self.* = SelfChannel{ .loop = loop, .buffer_len = 0, .buffer_nodes = buffer_nodes, @@ -66,7 +68,7 @@ pub fn Channel(comptime T: type) type { .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(), .get_count = 0, .put_count = 0, - }); + }; errdefer loop.allocator.destroy(self); return self; @@ -319,6 +321,9 @@ 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; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -346,19 +351,19 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { const value1_promise = try async channel.get(); const value1 = await value1_promise; - assert(value1 == 1234); + testing.expect(value1 == 1234); const value2_promise = try async channel.get(); const value2 = await value2_promise; - assert(value2 == 4567); + testing.expect(value2 == 4567); const value3_promise = try async channel.getOrNull(); const value3 = await value3_promise; - assert(value3 == null); + testing.expect(value3 == null); const last_put = try async testPut(channel, 4444); const value4 = await try async channel.getOrNull(); - assert(value4.? == 4444); + testing.expect(value4.? == 4444); await last_put; } diff --git a/std/event/fs.zig b/std/event/fs.zig index 1b8e1aa5dc..0e7482ec60 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const event = std.event; const assert = std.debug.assert; +const testing = std.testing; const os = std.os; const mem = std.mem; const posix = os.posix; @@ -495,7 +496,7 @@ pub const CloseOperation = struct { }; pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) { - const self = try loop.allocator.createOne(CloseOperation); + const self = try loop.allocator.create(CloseOperation); self.* = CloseOperation{ .loop = loop, .os_data = switch (builtin.os) { @@ -787,7 +788,7 @@ pub fn Watch(comptime V: type) type { }, builtin.Os.windows => { - const self = try loop.allocator.createOne(Self); + const self = try loop.allocator.create(Self); errdefer loop.allocator.destroy(self); self.* = Self{ .channel = channel, @@ -802,7 +803,7 @@ pub fn Watch(comptime V: type) type { }, builtin.Os.macosx, builtin.Os.freebsd => { - const self = try loop.allocator.createOne(Self); + const self = try loop.allocator.create(Self); errdefer loop.allocator.destroy(self); self.* = Self{ @@ -871,7 +872,7 @@ pub fn Watch(comptime V: type) type { } async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { - const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path); + const resolved_path = try os.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); @@ -1068,7 +1069,7 @@ pub fn Watch(comptime V: type) type { } } else { errdefer _ = self.os_data.dir_table.remove(dirname); - const dir = try self.channel.loop.allocator.createOne(OsData.Dir); + const dir = try self.channel.loop.allocator.create(OsData.Dir); errdefer self.channel.loop.allocator.destroy(dir); dir.* = OsData.Dir{ @@ -1307,39 +1308,36 @@ pub fn Watch(comptime V: type) type { const test_tmp_dir = "std_event_fs_test"; -test "write a file, watch it, write it again" { - if (builtin.os == builtin.Os.windows) { - // TODO this test is disabled on windows until the coroutine rewrite is finished. - // https://github.com/ziglang/zig/issues/1363 - return error.SkipZigTest; - } - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const allocator = &da.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; -} +// TODO this test is disabled until the coroutine rewrite is finished. +//test "write a file, watch it, write it again" { +// return error.SkipZigTest; +// var da = std.heap.DirectAllocator.init(); +// defer da.deinit(); +// +// const allocator = &da.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; +//} async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { result.* = await (async testFsWatch(loop) catch unreachable); } async fn testFsWatch(loop: *Loop) !void { - const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt"); + const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); defer loop.allocator.free(file_path); const contents = @@ -1352,13 +1350,13 @@ async fn testFsWatch(loop: *Loop) !void { try await try async writeFile(loop, file_path, contents); const read_contents = try await try async readFile(loop, file_path, 1024 * 1024); - assert(mem.eql(u8, read_contents, contents)); + testing.expectEqualSlices(u8, contents, read_contents); // now watch the file var watch = try Watch(void).create(loop, 0); defer watch.destroy(); - assert((try await try async watch.addFile(file_path, {})) == null); + testing.expect((try await try async watch.addFile(file_path, {})) == null); const ev = try async watch.channel.get(); var ev_consumed = false; @@ -1378,10 +1376,10 @@ async fn testFsWatch(loop: *Loop) !void { WatchEventId.Delete => @panic("wrong event"), } const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024); - assert(mem.eql(u8, contents_updated, + testing.expectEqualSlices(u8, \\line 1 \\lorem ipsum - )); + , contents_updated); // TODO test deleting the file and then re-adding it. we should get events for both } diff --git a/std/event/future.zig b/std/event/future.zig index d61768b198..66acac5ad7 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -1,5 +1,6 @@ const std = @import("../index.zig"); const assert = std.debug.assert; +const testing = std.testing; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -84,6 +85,9 @@ pub fn Future(comptime T: type) type { } test "std.event.Future" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -111,7 +115,7 @@ async fn testFuture(loop: *Loop) void { const result = (await a) + (await b); cancel c; - assert(result == 12); + testing.expect(result == 12); } async fn waitOnFuture(future: *Future(i32)) i32 { diff --git a/std/event/group.zig b/std/event/group.zig index 9f2687a5b3..25e79640cb 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -4,7 +4,7 @@ const Lock = std.event.Lock; const Loop = std.event.Loop; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; -const assert = std.debug.assert; +const testing = std.testing; /// ReturnType must be `void` or `E!void` pub fn Group(comptime ReturnType: type) type { @@ -42,10 +42,11 @@ pub fn Group(comptime ReturnType: type) type { /// Add a promise to the group. Thread-safe. pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) { - const node = try self.lock.loop.allocator.create(Stack.Node{ + const node = try self.lock.loop.allocator.create(Stack.Node); + node.* = Stack.Node{ .next = undefined, .data = handle, - }); + }; self.alloc_stack.push(node); } @@ -121,6 +122,9 @@ pub fn Group(comptime ReturnType: type) type { } test "std.event.Group" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -142,12 +146,12 @@ async fn testGroup(loop: *Loop) void { group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory"); group.call(increaseByTen, &count) catch @panic("memory"); await (async group.wait() catch @panic("memory")); - assert(count == 11); + testing.expect(count == 11); var another = Group(anyerror!void).init(loop); another.add(async somethingElse() catch @panic("memory")) catch @panic("memory"); another.call(doSomethingThatFails) catch @panic("memory"); - std.debug.assertError(await (async another.wait() catch @panic("memory")), error.ItBroke); + testing.expectError(error.ItBroke, await (async another.wait() catch @panic("memory"))); } async fn sleepALittle(count: *usize) void { diff --git a/std/event/io.zig b/std/event/io.zig index bb377a3b68..b11550f7aa 100644 --- a/std/event/io.zig +++ b/std/event/io.zig @@ -39,18 +39,22 @@ pub fn InStream(comptime ReadError: type) type { if (amt_read < buf.len) return error.EndOfStream; } - pub async fn readIntLe(self: *Self, comptime T: type) !T { - return await (async self.readInt(builtin.Endian.Little, T) catch unreachable); + pub async fn readIntLittle(self: *Self, comptime T: type) !T { + var bytes: [@sizeOf(T)]u8 = undefined; + try await (async self.readNoEof(bytes[0..]) catch unreachable); + return mem.readIntLittle(T, &bytes); } pub async fn readIntBe(self: *Self, comptime T: type) !T { - return await (async self.readInt(builtin.Endian.Big, T) catch unreachable); - } - - pub async fn readInt(self: *Self, endian: builtin.Endian, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try await (async self.readNoEof(bytes[0..]) catch unreachable); - return mem.readInt(bytes, T, endian); + return mem.readIntBig(T, &bytes); + } + + pub async fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T { + var bytes: [@sizeOf(T)]u8 = undefined; + try await (async self.readNoEof(bytes[0..]) catch unreachable); + return mem.readInt(T, &bytes, endian); } pub async fn readStruct(self: *Self, comptime T: type) !T { diff --git a/std/event/lock.zig b/std/event/lock.zig index 46e0d13468..d6a246cee5 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -1,6 +1,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -122,6 +123,9 @@ pub const Lock = struct { }; test "std.event.Lock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -138,7 +142,7 @@ test "std.event.Lock" { defer cancel handle; loop.run(); - assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len)); + 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 { diff --git a/std/event/loop.zig b/std/event/loop.zig index 43965e8293..b92bf41982 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -1,6 +1,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -97,6 +98,7 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); const core_count = try os.cpuCount(allocator); return self.initInternal(allocator, core_count); } @@ -201,6 +203,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { // writing 8 bytes to an eventfd cannot fail @@ -301,6 +308,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable; @@ -338,6 +350,11 @@ pub const Loop = struct { self.available_eventfd_resume_nodes.push(eventfd_node); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { var i: usize = 0; @@ -845,6 +862,9 @@ pub const Loop = struct { }; test "std.event.Loop - basic" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -858,6 +878,9 @@ test "std.event.Loop - basic" { } test "std.event.Loop - call" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -874,7 +897,7 @@ test "std.event.Loop - call" { loop.run(); - assert(did_it); + testing.expect(did_it); } async fn testEventLoop() i32 { @@ -883,6 +906,6 @@ async fn testEventLoop() i32 { async fn testEventLoop2(h: promise->i32, did_it: *bool) void { const value = await h; - assert(value == 1234); + testing.expect(value == 1234); did_it.* = true; } diff --git a/std/event/net.zig b/std/event/net.zig index 6838704084..48461c3d81 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const testing = std.testing; const event = std.event; const mem = std.mem; const os = std.os; @@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File { } test "listen on a port, send bytes, receive bytes" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + if (builtin.os != builtin.Os.linux) { // TODO build abstractions for other operating systems return error.SkipZigTest; @@ -323,7 +326,7 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv var buf: [512]u8 = undefined; const amt_read = try socket_file.read(buf[0..]); const msg = buf[0..amt_read]; - assert(mem.eql(u8, msg, "hello from server\n")); + testing.expect(mem.eql(u8, msg, "hello from server\n")); server.close(); } diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 5d48ea893e..26ccd12b73 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -1,6 +1,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -211,6 +212,9 @@ pub const RwLock = struct { }; test "std.event.RwLock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -228,7 +232,7 @@ test "std.event.RwLock" { loop.run(); const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len; - assert(mem.eql(i32, shared_test_data, expected_result)); + testing.expectEqualSlices(i32, expected_result, shared_test_data); } async fn testLock(loop: *Loop, lock: *RwLock) void { @@ -290,7 +294,7 @@ async fn readRunner(lock: *RwLock) void { const handle = await lock_promise; defer handle.release(); - assert(shared_test_index == 0); - assert(shared_test_data[i] == @intCast(i32, shared_count)); + testing.expect(shared_test_index == 0); + testing.expect(shared_test_data[i] == @intCast(i32, shared_count)); } } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index eda0bfae03..f006dfdbdc 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -2,7 +2,7 @@ const std = @import("../index.zig"); const math = std.math; const debug = std.debug; const assert = debug.assert; -const assertError = debug.assertError; +const testing = std.testing; const mem = std.mem; const builtin = @import("builtin"); const errol = @import("errol/index.zig"); @@ -117,7 +117,7 @@ pub fn formatType( return output(context, @errorName(value)); } switch (@typeInfo(T)) { - builtin.TypeId.Int, builtin.TypeId.Float => { + builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { return formatValue(value, fmt, context, Errors, output); }, builtin.TypeId.Void => { @@ -236,6 +236,9 @@ pub fn formatType( const casted_value = ([]const u8)(value); return output(context, casted_value); }, + builtin.TypeInfo.Pointer.Size.C => { + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, }, builtin.TypeId.Array => |info| { if (info.child == u8) { @@ -243,6 +246,9 @@ pub fn formatType( } return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); }, + builtin.TypeId.Fn => { + return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); + }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } @@ -268,11 +274,15 @@ fn formatValue( } } - comptime var T = @typeOf(value); + const T = @typeOf(value); switch (@typeId(T)) { builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), - else => unreachable, + builtin.TypeId.ComptimeInt => { + const Int = math.IntFittingRange(value, value); + return formatIntValue(Int(value), fmt, context, Errors, output); + }, + else => comptime unreachable, } } @@ -289,9 +299,10 @@ pub fn formatIntValue( if (fmt.len > 0) { switch (fmt[0]) { 'c' => { - if (@typeOf(value) == u8) { - if (fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]}); - return formatAsciiChar(value, context, Errors, output); + if (@typeOf(value).bit_count <= 8) { + if (fmt.len > 1) + @compileError("Unknown format character: " ++ []u8{fmt[1]}); + return formatAsciiChar(u8(value), context, Errors, output); } }, 'b' => { @@ -580,7 +591,7 @@ pub fn formatFloatDecimal( } // Remaining fractional portion, zero-padding if insufficient. - debug.assert(precision >= printed); + assert(precision >= printed); if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); return; @@ -790,13 +801,13 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { } test "fmt.parseInt" { - assert((parseInt(i32, "-10", 10) catch unreachable) == -10); - assert((parseInt(i32, "+10", 10) catch unreachable) == 10); - assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); - assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); - assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); - assert((parseInt(u8, "255", 10) catch unreachable) == 255); - assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); + 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); } const ParseUnsignedError = error{ @@ -820,31 +831,37 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned return x; } -test "parseUnsigned" { - assert((try parseUnsigned(u16, "050124", 10)) == 50124); - assert((try parseUnsigned(u16, "65535", 10)) == 65535); - assertError(parseUnsigned(u16, "65536", 10), error.Overflow); +test "fmt.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)); - assert((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); - assertError(parseUnsigned(u64, "10000000000000000", 16), error.Overflow); + testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); + testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); - assert((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); + testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); - assert((try parseUnsigned(u7, "1", 10)) == 1); - assert((try parseUnsigned(u7, "1000", 2)) == 8); + testing.expect((try parseUnsigned(u7, "1", 10)) == 1); + testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); - assertError(parseUnsigned(u32, "f", 10), error.InvalidCharacter); - assertError(parseUnsigned(u8, "109", 8), error.InvalidCharacter); + testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); + testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); - assert((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); + testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); // these numbers should fit even though the radix itself doesn't fit in the destination type - assert((try parseUnsigned(u1, "0", 10)) == 0); - assert((try parseUnsigned(u1, "1", 10)) == 1); - assertError(parseUnsigned(u1, "2", 10), error.Overflow); - assert((try parseUnsigned(u1, "001", 16)) == 1); - assert((try parseUnsigned(u2, "3", 16)) == 3); - assertError(parseUnsigned(u2, "4", 16), error.Overflow); + 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)); +} + +pub const parseFloat = @import("parse_float.zig").parseFloat; + +test "fmt.parseFloat" { + _ = @import("parse_float.zig"); } pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { @@ -902,19 +919,19 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { test "buf print int" { var buffer: [max_int_digits]u8 = undefined; const buf = buffer[0..]; - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); } fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { @@ -931,7 +948,7 @@ test "parse u64 digit too big" { test "parse unsigned comptime" { comptime { - assert((try parseUnsigned(usize, "2", 10)) == 2); + testing.expect((try parseUnsigned(usize, "2", 10)) == 2); } } @@ -964,6 +981,23 @@ test "fmt.format" { const value: u8 = 0b1100; try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); } + { + var buf1: [32]u8 = undefined; + var context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite); + var res = buf1[0 .. buf1.len - context.remaining.len]; + testing.expect(mem.eql(u8, res, "1234")); + + context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); + res = buf1[0 .. buf1.len - context.remaining.len]; + testing.expect(mem.eql(u8, res, "a")); + + context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); + res = buf1[0 .. buf1.len - context.remaining.len]; + testing.expect(mem.eql(u8, res, "1100")); + } { const value: [3]u8 = "abc"; try testFmt("array: abc\n", "array: {}\n", value); @@ -985,6 +1019,14 @@ test "fmt.format" { 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); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + } try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); @@ -1020,19 +1062,19 @@ test "fmt.format" { var buf1: [32]u8 = undefined; const value: f32 = 1.34; const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); + testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); } { var buf1: [32]u8 = undefined; const value: f32 = 12.34; const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); + testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); } { var buf1: [32]u8 = undefined; const value: f64 = -12.34e10; const result = try bufPrint(buf1[0..], "f64: {e}\n", value); - assert(mem.eql(u8, result, "f64: -1.234e+11\n")); + testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); } { // This fails on release due to a minor rounding difference. @@ -1042,26 +1084,26 @@ test "fmt.format" { var buf1: [32]u8 = undefined; const value: f64 = 9.999960e-40; const result = try bufPrint(buf1[0..], "f64: {e}\n", value); - assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); + testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); } } { var buf1: [32]u8 = undefined; const value: f64 = 1.409706e-42; const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); + testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); } { var buf1: [32]u8 = undefined; const value: f64 = @bitCast(f32, u32(814313563)); const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); + testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); } { var buf1: [32]u8 = undefined; const value: f64 = @bitCast(f32, u32(1006632960)); const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); + testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); } { // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. @@ -1069,47 +1111,47 @@ test "fmt.format" { var buf1: [32]u8 = undefined; const value: f64 = @bitCast(f32, u32(1203982400)); const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); + testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); - assert(mem.eql(u8, result, "f64: nan\n")); + testing.expect(mem.eql(u8, result, "f64: nan\n")); } if (builtin.arch != builtin.Arch.armv8) { // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); - assert(mem.eql(u8, result, "f64: -nan\n")); + testing.expect(mem.eql(u8, result, "f64: -nan\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); - assert(mem.eql(u8, result, "f64: inf\n")); + testing.expect(mem.eql(u8, result, "f64: inf\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); - assert(mem.eql(u8, result, "f64: -inf\n")); + testing.expect(mem.eql(u8, result, "f64: -inf\n")); } { var buf1: [64]u8 = undefined; const value: f64 = 1.52314e+29; const result = try bufPrint(buf1[0..], "f64: {.}\n", value); - assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); + testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); } { var buf1: [32]u8 = undefined; const value: f32 = 1.1234; const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); - assert(mem.eql(u8, result, "f32: 1.1\n")); + testing.expect(mem.eql(u8, result, "f32: 1.1\n")); } { var buf1: [32]u8 = undefined; const value: f32 = 1234.567; const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); - assert(mem.eql(u8, result, "f32: 1234.57\n")); + testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); } { var buf1: [32]u8 = undefined; @@ -1117,92 +1159,92 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). // -11.12339... is rounded back up to -11.1234 - assert(mem.eql(u8, result, "f32: -11.1234\n")); + testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); } { var buf1: [32]u8 = undefined; const value: f32 = 91.12345; const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); - assert(mem.eql(u8, result, "f32: 91.12345\n")); + testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 91.12345678901235; const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); - assert(mem.eql(u8, result, "f64: 91.1234567890\n")); + testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 0.0; const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 5.700; const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); - assert(mem.eql(u8, result, "f64: 6\n")); + testing.expect(mem.eql(u8, result, "f64: 6\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 9.999; const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); - assert(mem.eql(u8, result, "f64: 10.0\n")); + testing.expect(mem.eql(u8, result, "f64: 10.0\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 1.0; const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); - assert(mem.eql(u8, result, "f64: 1.000\n")); + testing.expect(mem.eql(u8, result, "f64: 1.000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 0.0003; const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); - assert(mem.eql(u8, result, "f64: 0.00030000\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 1.40130e-45; const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = 9.999960e-40; const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); } // libc checks { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(916964781))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00001\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); } { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(925353389))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00001\n")); + testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); } { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(1036831278))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.10000\n")); + testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(1065353133))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); } { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(1092616192))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 10.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); } // libc differences { @@ -1212,7 +1254,7 @@ test "fmt.format" { // floats of the form x.yyyy25 on a precision point. const value: f64 = f64(@bitCast(f32, u32(1015021568))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.01563\n")); + testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); } // std-windows-x86_64-Debug-bare test case fails { @@ -1222,7 +1264,7 @@ test "fmt.format" { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(1518338049))); const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); + testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); } //custom type format { @@ -1303,10 +1345,10 @@ test "fmt.format" { var buf: [100]u8 = undefined; const uu_result = try bufPrint(buf[0..], "{}", uu_inst); - debug.assert(mem.eql(u8, uu_result[0..3], "UU@")); + testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); const eu_result = try bufPrint(buf[0..], "{}", eu_inst); - debug.assert(mem.eql(u8, uu_result[0..3], "EU@")); + testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); } //enum format { @@ -1365,11 +1407,11 @@ pub fn trim(buf: []const u8) []const u8 { } test "fmt.trim" { - assert(mem.eql(u8, "abc", trim("\n abc \t"))); - assert(mem.eql(u8, "", trim(" "))); - assert(mem.eql(u8, "", trim(""))); - assert(mem.eql(u8, "abc", trim(" abc"))); - assert(mem.eql(u8, "abc", trim("abc "))); + 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 "))); } pub fn isWhiteSpace(byte: u8) bool { diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig new file mode 100644 index 0000000000..de9619efe2 --- /dev/null +++ b/std/fmt/parse_float.zig @@ -0,0 +1,420 @@ +// Adapted from https://github.com/grzegorz-kraszewski/stringtofloat. + +// MIT License +// +// Copyright (c) 2016 Grzegorz Kraszewski +// +// 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. +// + +// Be aware that this implementation has the following limitations: +// +// - Is not round-trip accurate for all values +// - Only supports round-to-zero +// - Does not handle denormals + +const std = @import("../index.zig"); + +const max_digits = 25; + +const f64_plus_zero: u64 = 0x0000000000000000; +const f64_minus_zero: u64 = 0x8000000000000000; +const f64_plus_infinity: u64 = 0x7FF0000000000000; +const f64_minus_infinity: u64 = 0xFFF0000000000000; + +const Z96 = struct { + d0: u32, + d1: u32, + d2: u32, + + // d = s >> 1 + inline fn shiftRight1(d: *Z96, s: Z96) void { + d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); + d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); + d.d2 = s.d2 >> 1; + } + + // d = s << 1 + inline fn shiftLeft1(d: *Z96, s: Z96) void { + d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); + d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); + d.d0 = s.d0 << 1; + } + + // d += s + inline fn add(d: *Z96, s: Z96) void { + var w = u64(d.d0) + u64(s.d0); + d.d0 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d1) + u64(s.d1); + d.d1 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d2) + 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); + d.d0 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d1) -% u64(s.d1); + d.d1 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d2) -% u64(s.d2); + d.d2 = @truncate(u32, w); + } +}; + +const FloatRepr = struct { + negative: bool, + exponent: i32, + mantissa: u64, +}; + +fn convertRepr(comptime T: type, n: FloatRepr) T { + const mask28: u32 = 0xf << 28; + + var s: Z96 = undefined; + var q: Z96 = undefined; + var r: Z96 = undefined; + + s.d0 = @truncate(u32, n.mantissa); + s.d1 = @truncate(u32, n.mantissa >> 32); + s.d2 = 0; + + var binary_exponent: u64 = 92; + var exp = n.exponent; + + while (exp > 0) : (exp -= 1) { + q.shiftLeft1(s); // q = p << 1 + r.shiftLeft1(q); // r = p << 2 + s.shiftLeft1(r); // p = p << 3 + q.add(s); // p = (p << 3) + (p << 1) + + exp -= 1; + + while (s.d2 & mask28 != 0) { + q.shiftRight1(s); + binary_exponent += 1; + s = q; + } + } + + while (exp < 0) { + while (s.d2 & (1 << 31) == 0) { + q.shiftLeft1(s); + binary_exponent -= 1; + s = q; + } + + q.d2 = s.d2 / 10; + r.d1 = s.d2 % 10; + r.d2 = (s.d1 >> 8) | (r.d1 << 24); + q.d1 = r.d2 / 10; + r.d1 = r.d2 % 10; + r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24); + r.d0 = r.d2 / 10; + r.d1 = r.d2 % 10; + q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16); + q.d0 = r.d0 << 16; + r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16); + q.d0 |= r.d2 / 10; + s = q; + + exp += 1; + } + + if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) { + while (s.d2 & mask28 == 0) { + q.shiftLeft1(s); + binary_exponent -= 1; + s = q; + } + } + + binary_exponent += 1023; + + const repr: u64 = blk: { + if (binary_exponent > 2046) { + break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity; + } else if (binary_exponent < 1) { + break :blk if (n.negative) f64_minus_zero else f64_plus_zero; + } else if (s.d2 != 0) { + const binexs2 = u64(binary_exponent) << 52; + const rr = (u64(s.d2 & ~mask28) << 24) | ((u64(s.d1) + 128) >> 8) | binexs2; + break :blk if (n.negative) rr | (1 << 63) else rr; + } else { + break :blk 0; + } + }; + + const f = @bitCast(f64, repr); + return @floatCast(T, f); +} + +const State = enum { + MaybeSign, + LeadingMantissaZeros, + LeadingFractionalZeros, + MantissaIntegral, + MantissaFractional, + ExponentSign, + LeadingExponentZeros, + Exponent, +}; + +const ParseResult = enum { + Ok, + PlusZero, + MinusZero, + PlusInf, + MinusInf, +}; + +inline fn isDigit(c: u8) bool { + return c >= '0' and c <= '9'; +} + +inline fn isSpace(c: u8) bool { + return (c >= 0x09 and c <= 0x13) or c == 0x20; +} + +fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { + var digit_index: usize = 0; + var negative = false; + var negative_exp = false; + var exponent: i32 = 0; + + var state = State.MaybeSign; + + var i: usize = 0; + loop: while (i < s.len) { + const c = s[i]; + + switch (state) { + State.MaybeSign => { + state = State.LeadingMantissaZeros; + + if (c == '+') { + i += 1; + } else if (c == '-') { + n.negative = true; + i += 1; + } else if (isDigit(c) or c == '.') { + // continue + } else { + return error.InvalidCharacter; + } + }, + + State.LeadingMantissaZeros => { + if (c == '0') { + i += 1; + } else if (c == '.') { + i += 1; + state = State.LeadingFractionalZeros; + } else { + state = State.MantissaIntegral; + } + }, + + State.LeadingFractionalZeros => { + if (c == '0') { + i += 1; + if (n.exponent > std.math.minInt(i32)) { + n.exponent -= 1; + } + } else { + state = State.MantissaFractional; + } + }, + + State.MantissaIntegral => { + if (isDigit(c)) { + if (digit_index < max_digits) { + n.mantissa *%= 10; + n.mantissa += s[i] - '0'; + digit_index += 1; + } else if (n.exponent < std.math.maxInt(i32)) { + n.exponent += 1; + } + + i += 1; + } else if (c == '.') { + i += 1; + state = State.MantissaFractional; + } else { + state = State.MantissaFractional; + } + }, + + State.MantissaFractional => { + if (isDigit(c)) { + if (digit_index < max_digits) { + n.mantissa *%= 10; + n.mantissa += c - '0'; + n.exponent -%= 1; + digit_index += 1; + } + + i += 1; + } else if (c == 'e' or c == 'E') { + i += 1; + state = State.ExponentSign; + } else { + state = State.ExponentSign; + } + }, + + State.ExponentSign => { + if (c == '+') { + i += 1; + } else if (c == '-') { + negative_exp = true; + i += 1; + } + + state = State.LeadingExponentZeros; + }, + + State.LeadingExponentZeros => { + if (c == '0') { + i += 1; + } else { + state = State.Exponent; + } + }, + + State.Exponent => { + if (isDigit(c)) { + if (exponent < std.math.maxInt(i32)) { + exponent *= 10; + exponent += @intCast(i32, c - '0'); + } + + i += 1; + } else { + return error.InvalidCharacter; + } + }, + } + } + + if (negative_exp) exponent = -exponent; + n.exponent += exponent; + + if (n.mantissa == 0) { + return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; + } else if (n.exponent > 309) { + return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf; + } else if (n.exponent < -328) { + return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; + } + + return ParseResult.Ok; +} + +inline fn isLower(c: u8) bool { + return c -% 'a' < 26; +} + +inline fn toUpper(c: u8) u8 { + return if (isLower(c)) (c & 0x5f) else c; +} + +fn caseInEql(a: []const u8, b: []const u8) bool { + if (a.len != b.len) return false; + + for (a) |_, i| { + if (toUpper(a[i]) != toUpper(b[i])) { + return false; + } + } + + return true; +} + +pub fn parseFloat(comptime T: type, s: []const u8) !T { + if (s.len == 0) { + return error.InvalidCharacter; + } + + if (caseInEql(s, "nan")) { + return std.math.nan(T); + } else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) { + return std.math.inf(T); + } else if (caseInEql(s, "-inf")) { + return -std.math.inf(T); + } + + var r = FloatRepr{ + .negative = false, + .exponent = 0, + .mantissa = 0, + }; + + return switch (try parseRepr(s, &r)) { + ParseResult.Ok => convertRepr(T, r), + ParseResult.PlusZero => 0.0, + ParseResult.MinusZero => -T(0.0), + ParseResult.PlusInf => std.math.inf(T), + ParseResult.MinusInf => -std.math.inf(T), + }; +} + +test "fmt.parseFloat" { + const testing = std.testing; + const expect = testing.expect; + const expectEqual = testing.expectEqual; + const approxEq = std.math.approxEq; + const epsilon = 1e-7; + + inline for ([]type{ f16, f32, f64, f128 }) |T| { + const Z = @IntType(false, T.bit_count); + + testing.expectError(error.InvalidCharacter, parseFloat(T, "")); + testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); + + expectEqual(try parseFloat(T, "0"), 0.0); + expectEqual((try parseFloat(T, "0")), 0.0); + expectEqual((try parseFloat(T, "+0")), 0.0); + expectEqual((try parseFloat(T, "-0")), 0.0); + + expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon)); + expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); + + expectEqual((try parseFloat(T, "1e-700")), 0); + expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T)); + + expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); + expectEqual((try parseFloat(T, "inF")), std.math.inf(T)); + expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T)); + + if (T != f16) { + expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); + expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); + } + } +} diff --git a/std/hash/adler.zig b/std/hash/adler.zig index 9c5966f89b..78f960367a 100644 --- a/std/hash/adler.zig +++ b/std/hash/adler.zig @@ -4,7 +4,7 @@ // https://github.com/madler/zlib/blob/master/adler32.c const std = @import("../index.zig"); -const debug = std.debug; +const testing = std.testing; pub const Adler32 = struct { const base = 65521; @@ -89,19 +89,19 @@ pub const Adler32 = struct { }; test "adler32 sanity" { - debug.assert(Adler32.hash("a") == 0x620062); - debug.assert(Adler32.hash("example") == 0xbc002ed); + testing.expect(Adler32.hash("a") == 0x620062); + testing.expect(Adler32.hash("example") == 0xbc002ed); } test "adler32 long" { const long1 = []u8{1} ** 1024; - debug.assert(Adler32.hash(long1[0..]) == 0x06780401); + testing.expect(Adler32.hash(long1[0..]) == 0x06780401); const long2 = []u8{1} ** 1025; - debug.assert(Adler32.hash(long2[0..]) == 0x0a7a0402); + testing.expect(Adler32.hash(long2[0..]) == 0x0a7a0402); } test "adler32 very long" { const long = []u8{1} ** 5553; - debug.assert(Adler32.hash(long[0..]) == 0x707f15b2); + testing.expect(Adler32.hash(long[0..]) == 0x707f15b2); } diff --git a/std/hash/crc.zig b/std/hash/crc.zig index c4bd92884a..9bea358bf1 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -7,6 +7,7 @@ const std = @import("../index.zig"); const debug = std.debug; +const testing = std.testing; pub const Polynomial = struct { const IEEE = 0xedb88320; @@ -101,17 +102,17 @@ pub fn Crc32WithPoly(comptime poly: u32) type { test "crc32 ieee" { const Crc32Ieee = Crc32WithPoly(Polynomial.IEEE); - debug.assert(Crc32Ieee.hash("") == 0x00000000); - debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43); - debug.assert(Crc32Ieee.hash("abc") == 0x352441c2); + testing.expect(Crc32Ieee.hash("") == 0x00000000); + testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); + testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); } test "crc32 castagnoli" { const Crc32Castagnoli = Crc32WithPoly(Polynomial.Castagnoli); - debug.assert(Crc32Castagnoli.hash("") == 0x00000000); - debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330); - debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7); + testing.expect(Crc32Castagnoli.hash("") == 0x00000000); + testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); + testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); } // half-byte lookup table implementation. @@ -165,15 +166,15 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { test "small crc32 ieee" { const Crc32Ieee = Crc32SmallWithPoly(Polynomial.IEEE); - debug.assert(Crc32Ieee.hash("") == 0x00000000); - debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43); - debug.assert(Crc32Ieee.hash("abc") == 0x352441c2); + testing.expect(Crc32Ieee.hash("") == 0x00000000); + testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); + testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); } test "small crc32 castagnoli" { const Crc32Castagnoli = Crc32SmallWithPoly(Polynomial.Castagnoli); - debug.assert(Crc32Castagnoli.hash("") == 0x00000000); - debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330); - debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7); + testing.expect(Crc32Castagnoli.hash("") == 0x00000000); + testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); + testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); } diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig index 9bb18f14b3..6876b636f6 100644 --- a/std/hash/fnv.zig +++ b/std/hash/fnv.zig @@ -5,7 +5,7 @@ // https://tools.ietf.org/html/draft-eastlake-fnv-14 const std = @import("../index.zig"); -const debug = std.debug; +const testing = std.testing; pub const Fnv1a_32 = Fnv1a(u32, 0x01000193, 0x811c9dc5); pub const Fnv1a_64 = Fnv1a(u64, 0x100000001b3, 0xcbf29ce484222325); @@ -41,18 +41,18 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type { } test "fnv1a-32" { - debug.assert(Fnv1a_32.hash("") == 0x811c9dc5); - debug.assert(Fnv1a_32.hash("a") == 0xe40c292c); - debug.assert(Fnv1a_32.hash("foobar") == 0xbf9cf968); + testing.expect(Fnv1a_32.hash("") == 0x811c9dc5); + testing.expect(Fnv1a_32.hash("a") == 0xe40c292c); + testing.expect(Fnv1a_32.hash("foobar") == 0xbf9cf968); } test "fnv1a-64" { - debug.assert(Fnv1a_64.hash("") == 0xcbf29ce484222325); - debug.assert(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c); - debug.assert(Fnv1a_64.hash("foobar") == 0x85944171f73967e8); + testing.expect(Fnv1a_64.hash("") == 0xcbf29ce484222325); + testing.expect(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c); + testing.expect(Fnv1a_64.hash("foobar") == 0x85944171f73967e8); } test "fnv1a-128" { - debug.assert(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d); - debug.assert(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964); + testing.expect(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d); + testing.expect(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964); } diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 0fe958c38b..c9a6128d35 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -6,7 +6,8 @@ // https://131002.net/siphash/ const std = @import("../index.zig"); -const debug = std.debug; +const assert = std.debug.assert; +const testing = std.testing; const math = std.math; const mem = std.mem; @@ -21,8 +22,8 @@ pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type { } fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type { - debug.assert(T == u64 or T == u128); - debug.assert(c_rounds > 0 and d_rounds > 0); + assert(T == u64 or T == u128); + assert(c_rounds > 0 and d_rounds > 0); return struct { const Self = @This(); @@ -40,10 +41,10 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) msg_len: u8, pub fn init(key: []const u8) Self { - debug.assert(key.len >= 16); + assert(key.len >= 16); - const k0 = mem.readInt(key[0..8], u64, Endian.Little); - const k1 = mem.readInt(key[8..16], u64, Endian.Little); + const k0 = mem.readIntSliceLittle(u64, key[0..8]); + const k1 = mem.readIntSliceLittle(u64, key[8..16]); var d = Self{ .v0 = k0 ^ 0x736f6d6570736575, @@ -119,9 +120,9 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) } fn round(d: *Self, b: []const u8) void { - debug.assert(b.len == 8); + assert(b.len == 8); - const m = mem.readInt(b[0..], u64, Endian.Little); + const m = mem.readIntSliceLittle(u64, b[0..]); d.v3 ^= m; comptime var i: usize = 0; @@ -162,7 +163,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"; test "siphash64-2-4 sanity" { - const vectors = [][]const u8{ + 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 @@ -235,13 +236,13 @@ test "siphash64-2-4 sanity" { for (vectors) |vector, i| { buffer[i] = @intCast(u8, i); - const expected = mem.readInt(vector, u64, Endian.Little); - debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); + const expected = mem.readIntLittle(u64, &vector); + testing.expect(siphash.hash(test_key, buffer[0..i]) == expected); } } test "siphash128-2-4 sanity" { - const vectors = [][]const u8{ + 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", @@ -314,7 +315,7 @@ test "siphash128-2-4 sanity" { for (vectors) |vector, i| { buffer[i] = @intCast(u8, i); - const expected = mem.readInt(vector, u128, Endian.Little); - debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); + const expected = mem.readIntLittle(u128, &vector); + testing.expect(siphash.hash(test_key, buffer[0..i]) == expected); } } diff --git a/std/hash_map.zig b/std/hash_map.zig index 99237047e0..4519890bb7 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -1,6 +1,7 @@ const std = @import("index.zig"); const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const math = std.math; const mem = std.mem; const Allocator = mem.Allocator; @@ -342,37 +343,37 @@ test "basic hash map usage" { var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator); defer map.deinit(); - assert((try map.put(1, 11)) == null); - assert((try map.put(2, 22)) == null); - assert((try map.put(3, 33)) == null); - assert((try map.put(4, 44)) == null); - assert((try map.put(5, 55)) == null); + testing.expect((try map.put(1, 11)) == null); + testing.expect((try map.put(2, 22)) == null); + testing.expect((try map.put(3, 33)) == null); + testing.expect((try map.put(4, 44)) == null); + testing.expect((try map.put(5, 55)) == null); - assert((try map.put(5, 66)).?.value == 55); - assert((try map.put(5, 55)).?.value == 66); + testing.expect((try map.put(5, 66)).?.value == 55); + testing.expect((try map.put(5, 55)).?.value == 66); const gop1 = try map.getOrPut(5); - assert(gop1.found_existing == true); - assert(gop1.kv.value == 55); + testing.expect(gop1.found_existing == true); + testing.expect(gop1.kv.value == 55); gop1.kv.value = 77; - assert(map.get(5).?.value == 77); + testing.expect(map.get(5).?.value == 77); const gop2 = try map.getOrPut(99); - assert(gop2.found_existing == false); + testing.expect(gop2.found_existing == false); gop2.kv.value = 42; - assert(map.get(99).?.value == 42); + testing.expect(map.get(99).?.value == 42); const gop3 = try map.getOrPutValue(5, 5); - assert(gop3.value == 77); + testing.expect(gop3.value == 77); const gop4 = try map.getOrPutValue(100, 41); - assert(gop4.value == 41); + testing.expect(gop4.value == 41); - assert(map.contains(2)); - assert(map.get(2).?.value == 22); + testing.expect(map.contains(2)); + testing.expect(map.get(2).?.value == 22); _ = map.remove(2); - assert(map.remove(2) == null); - assert(map.get(2) == null); + testing.expect(map.remove(2) == null); + testing.expect(map.get(2) == null); } test "iterator hash map" { @@ -382,9 +383,9 @@ test "iterator hash map" { var reset_map = AutoHashMap(i32, i32).init(&direct_allocator.allocator); defer reset_map.deinit(); - assert((try reset_map.put(1, 11)) == null); - assert((try reset_map.put(2, 22)) == null); - assert((try reset_map.put(3, 33)) == null); + testing.expect((try reset_map.put(1, 11)) == null); + testing.expect((try reset_map.put(2, 22)) == null); + testing.expect((try reset_map.put(3, 33)) == null); var keys = []i32{ 3, @@ -400,26 +401,26 @@ test "iterator hash map" { var it = reset_map.iterator(); var count: usize = 0; while (it.next()) |next| { - assert(next.key == keys[count]); - assert(next.value == values[count]); + testing.expect(next.key == keys[count]); + testing.expect(next.value == values[count]); count += 1; } - assert(count == 3); - assert(it.next() == null); + testing.expect(count == 3); + testing.expect(it.next() == null); it.reset(); count = 0; while (it.next()) |next| { - assert(next.key == keys[count]); - assert(next.value == values[count]); + testing.expect(next.key == keys[count]); + testing.expect(next.value == values[count]); count += 1; if (count == 2) break; } it.reset(); var entry = it.next().?; - assert(entry.key == keys[0]); - assert(entry.value == values[0]); + testing.expect(entry.key == keys[0]); + testing.expect(entry.value == values[0]); } pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) { @@ -495,6 +496,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type builtin.TypeId.Pointer => |info| switch (info.size) { builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"), builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"), + builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto hash C pointers"), builtin.TypeInfo.Pointer.Size.Slice => { const interval = std.math.max(1, key.len / 256); var i: usize = 0; @@ -508,6 +510,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"), builtin.TypeId.Array => @compileError("TODO auto hash for arrays"), + builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"), builtin.TypeId.Struct => @compileError("TODO auto hash for structs"), builtin.TypeId.Union => @compileError("TODO auto hash for unions"), builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"), @@ -541,6 +544,7 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool { builtin.TypeId.Pointer => |info| switch (info.size) { builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"), builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"), + builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto eql for C pointers"), builtin.TypeInfo.Pointer.Size.Slice => { if (a.len != b.len) return false; for (a) |a_item, i| { @@ -555,5 +559,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool { builtin.TypeId.Struct => @compileError("TODO auto eql for structs"), builtin.TypeId.Union => @compileError("TODO auto eql for unions"), builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"), + builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"), } } diff --git a/std/heap.zig b/std/heap.zig index 5c31d412cf..e7088150a8 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -1,6 +1,7 @@ const std = @import("index.zig"); const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const mem = std.mem; const os = std.os; const builtin = @import("builtin"); @@ -66,7 +67,7 @@ pub const DirectAllocator = struct { } } - fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -106,9 +107,7 @@ pub const DirectAllocator = struct { }; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); - const rem = @rem(root_addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); - const adjusted_addr = root_addr + march_forward_bytes; + const adjusted_addr = mem.alignForward(root_addr, alignment); const record_addr = adjusted_addr + n; @intToPtr(*align(1) usize, record_addr).* = root_addr; return @intToPtr([*]u8, adjusted_addr)[0..n]; @@ -126,8 +125,7 @@ pub const DirectAllocator = struct { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; const new_addr_end = base_addr + new_size; - const rem = @rem(new_addr_end, os.page_size); - const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); + const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); if (old_addr_end > new_addr_end_rounded) { _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); } @@ -324,51 +322,57 @@ pub const FixedBufferAllocator = struct { fn free(allocator: *Allocator, bytes: []u8) void {} }; -/// lock free -pub const ThreadSafeFixedBufferAllocator = struct { - allocator: Allocator, - end_index: usize, - buffer: []u8, +pub const ThreadSafeFixedBufferAllocator = blk: { + if (builtin.single_threaded) { + break :blk FixedBufferAllocator; + } else { + /// lock free + break :blk struct { + allocator: Allocator, + end_index: usize, + buffer: []u8, - pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { - return ThreadSafeFixedBufferAllocator{ - .allocator = Allocator{ - .allocFn = alloc, - .reallocFn = realloc, - .freeFn = free, - }, - .buffer = buffer, - .end_index = 0, + pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { + return ThreadSafeFixedBufferAllocator{ + .allocator = Allocator{ + .allocFn = alloc, + .reallocFn = realloc, + .freeFn = free, + }, + .buffer = buffer, + .end_index = 0, + }; + } + + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); + var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); + while (true) { + const addr = @ptrToInt(self.buffer.ptr) + end_index; + const rem = @rem(addr, alignment); + const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + const adjusted_index = end_index + march_forward_bytes; + const new_end_index = adjusted_index + n; + if (new_end_index > self.buffer.len) { + return error.OutOfMemory; + } + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index]; + } + } + + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + if (new_size <= old_mem.len) { + return old_mem[0..new_size]; + } else { + const result = try alloc(allocator, new_size, alignment); + mem.copy(u8, result, old_mem); + return result; + } + } + + fn free(allocator: *Allocator, bytes: []u8) void {} }; } - - fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { - const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); - var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); - while (true) { - const addr = @ptrToInt(self.buffer.ptr) + end_index; - const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); - const adjusted_index = end_index + march_forward_bytes; - const new_end_index = adjusted_index + n; - if (new_end_index > self.buffer.len) { - return error.OutOfMemory; - } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index]; - } - } - - fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - if (new_size <= old_mem.len) { - return old_mem[0..new_size]; - } else { - const result = try alloc(allocator, new_size, alignment); - mem.copy(u8, result, old_mem); - return result; - } - } - - fn free(allocator: *Allocator, bytes: []u8) void {} }; pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) { @@ -484,11 +488,11 @@ test "FixedBufferAllocator Reuse memory on realloc" { var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]); var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5); - assert(slice0.len == 5); + testing.expect(slice0.len == 5); var slice1 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 10); - assert(slice1.ptr == slice0.ptr); - assert(slice1.len == 10); - debug.assertError(fixed_buffer_allocator.allocator.realloc(u8, slice1, 11), error.OutOfMemory); + testing.expect(slice1.ptr == slice0.ptr); + testing.expect(slice1.len == 10); + testing.expectError(error.OutOfMemory, fixed_buffer_allocator.allocator.realloc(u8, slice1, 11)); } // check that we don't re-use the memory if it's not the most recent block { @@ -499,10 +503,10 @@ test "FixedBufferAllocator Reuse memory on realloc" { slice0[1] = 2; var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2); var slice2 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 4); - assert(slice0.ptr != slice2.ptr); - assert(slice1.ptr != slice2.ptr); - assert(slice2[0] == 1); - assert(slice2[1] == 2); + testing.expect(slice0.ptr != slice2.ptr); + testing.expect(slice1.ptr != slice2.ptr); + testing.expect(slice2[0] == 1); + testing.expect(slice2[1] == 2); } } @@ -516,27 +520,28 @@ test "ThreadSafeFixedBufferAllocator" { fn testAllocator(allocator: *mem.Allocator) !void { var slice = try allocator.alloc(*i32, 100); - assert(slice.len == 100); + testing.expect(slice.len == 100); for (slice) |*item, i| { - item.* = try allocator.create(@intCast(i32, i)); + item.* = try allocator.create(i32); + item.*.* = @intCast(i32, i); } slice = try allocator.realloc(*i32, slice, 20000); - assert(slice.len == 20000); + testing.expect(slice.len == 20000); for (slice[0..100]) |item, i| { - assert(item.* == @intCast(i32, i)); + testing.expect(item.* == @intCast(i32, i)); allocator.destroy(item); } slice = try allocator.realloc(*i32, slice, 50); - assert(slice.len == 50); + testing.expect(slice.len == 50); slice = try allocator.realloc(*i32, slice, 25); - assert(slice.len == 25); + testing.expect(slice.len == 25); slice = try allocator.realloc(*i32, slice, 0); - assert(slice.len == 0); + testing.expect(slice.len == 0); slice = try allocator.realloc(*i32, slice, 10); - assert(slice.len == 10); + testing.expect(slice.len == 10); allocator.free(slice); } @@ -544,25 +549,25 @@ fn testAllocator(allocator: *mem.Allocator) !void { fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void { // initial var slice = try allocator.alignedAlloc(u8, alignment, 10); - assert(slice.len == 10); + testing.expect(slice.len == 10); // grow slice = try allocator.alignedRealloc(u8, alignment, slice, 100); - assert(slice.len == 100); + testing.expect(slice.len == 100); // shrink slice = try allocator.alignedRealloc(u8, alignment, slice, 10); - assert(slice.len == 10); + testing.expect(slice.len == 10); // go to zero slice = try allocator.alignedRealloc(u8, alignment, slice, 0); - assert(slice.len == 0); + testing.expect(slice.len == 0); // realloc from zero slice = try allocator.alignedRealloc(u8, alignment, slice, 100); - assert(slice.len == 100); + testing.expect(slice.len == 100); // shrink with shrink slice = allocator.alignedShrink(u8, alignment, slice, 10); - assert(slice.len == 10); + testing.expect(slice.len == 10); // shrink to zero slice = allocator.alignedShrink(u8, alignment, slice, 0); - assert(slice.len == 0); + testing.expect(slice.len == 0); } fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { @@ -577,19 +582,19 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); var slice = try allocator.allocFn(allocator, 500, large_align); - debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); slice = try allocator.reallocFn(allocator, slice, 100, large_align); - debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); slice = try allocator.reallocFn(allocator, slice, 5000, large_align); - debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); slice = try allocator.reallocFn(allocator, slice, 10, large_align); - debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); slice = try allocator.reallocFn(allocator, slice, 20000, large_align); - debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); allocator.free(slice); } diff --git a/std/index.zig b/std/index.zig index 55ad016bb1..9d9b7ee8d6 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const Mutex = @import("mutex.zig").Mutex; +pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -30,21 +31,21 @@ pub const hash_map = @import("hash_map.zig"); pub const heap = @import("heap.zig"); pub const io = @import("io.zig"); pub const json = @import("json.zig"); +pub const lazyInit = @import("lazy_init.zig").lazyInit; pub const macho = @import("macho.zig"); pub const math = @import("math/index.zig"); -pub const meta = @import("meta/index.zig"); pub const mem = @import("mem.zig"); +pub const meta = @import("meta/index.zig"); pub const net = @import("net.zig"); pub const os = @import("os/index.zig"); pub const pdb = @import("pdb.zig"); pub const rand = @import("rand/index.zig"); pub const rb = @import("rb.zig"); pub const sort = @import("sort.zig"); +pub const testing = @import("testing.zig"); pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); -pub const lazyInit = @import("lazy_init.zig").lazyInit; - test "std" { // run tests from these _ = @import("array_list.zig"); @@ -55,9 +56,10 @@ test "std" { _ = @import("hash_map.zig"); _ = @import("linked_list.zig"); _ = @import("mutex.zig"); + _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); - + _ = @import("base64.zig"); _ = @import("build.zig"); _ = @import("c/index.zig"); @@ -66,24 +68,26 @@ test "std" { _ = @import("cstr.zig"); _ = @import("debug/index.zig"); _ = @import("dwarf.zig"); + _ = @import("dynamic_library.zig"); _ = @import("elf.zig"); _ = @import("empty.zig"); _ = @import("event.zig"); _ = @import("fmt/index.zig"); _ = @import("hash/index.zig"); + _ = @import("heap.zig"); _ = @import("io.zig"); _ = @import("json.zig"); + _ = @import("lazy_init.zig"); _ = @import("macho.zig"); _ = @import("math/index.zig"); - _ = @import("meta/index.zig"); _ = @import("mem.zig"); + _ = @import("meta/index.zig"); _ = @import("net.zig"); - _ = @import("heap.zig"); _ = @import("os/index.zig"); - _ = @import("rand/index.zig"); _ = @import("pdb.zig"); + _ = @import("rand/index.zig"); _ = @import("sort.zig"); + _ = @import("testing.zig"); _ = @import("unicode.zig"); _ = @import("zig/index.zig"); - _ = @import("lazy_init.zig"); } diff --git a/std/io.zig b/std/io.zig index bdca2b03e8..f6e3790af6 100644 --- a/std/io.zig +++ b/std/io.zig @@ -8,9 +8,12 @@ const debug = std.debug; const assert = debug.assert; const os = std.os; const mem = std.mem; +const meta = std.meta; +const trait = meta.trait; const Buffer = std.Buffer; const fmt = std.fmt; const File = std.os.File; +const testing = std.testing; const is_posix = builtin.os != builtin.Os.windows; const is_windows = builtin.os == builtin.Os.windows; @@ -152,35 +155,43 @@ pub fn InStream(comptime ReadError: type) type { } /// Reads a native-endian integer - pub fn readIntNe(self: *Self, comptime T: type) !T { - return self.readInt(builtin.endian, T); - } - - pub fn readIntLe(self: *Self, comptime T: type) !T { + pub fn readIntNative(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntLE(T, bytes); + return mem.readIntNative(T, &bytes); } - pub fn readIntBe(self: *Self, comptime T: type) !T { + /// Reads a foreign-endian integer + pub fn readIntForeign(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntBE(T, bytes); + return mem.readIntForeign(T, &bytes); } - pub fn readInt(self: *Self, endian: builtin.Endian, comptime T: type) !T { + pub fn readIntLittle(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readInt(bytes, T, endian); + return mem.readIntLittle(T, &bytes); } - pub fn readVarInt(self: *Self, endian: builtin.Endian, comptime T: type, size: usize) !T { - assert(size <= @sizeOf(T)); - assert(size <= 8); - var input_buf: [8]u8 = undefined; - const input_slice = input_buf[0..size]; - try self.readNoEof(input_slice); - return mem.readInt(input_slice, T, endian); + pub fn readIntBig(self: *Self, comptime T: type) !T { + var bytes: [@sizeOf(T)]u8 = undefined; + try self.readNoEof(bytes[0..]); + return mem.readIntBig(T, &bytes); + } + + pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T { + var bytes: [@sizeOf(T)]u8 = undefined; + try self.readNoEof(bytes[0..]); + return mem.readInt(T, &bytes, endian); + } + + pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType { + assert(size <= @sizeOf(ReturnType)); + var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined; + const bytes = bytes_buf[0..size]; + try self.readNoEof(bytes); + return mem.readVarInt(ReturnType, bytes, endian); } pub fn skipBytes(self: *Self, num_bytes: usize) !void { @@ -229,25 +240,34 @@ pub fn OutStream(comptime WriteError: type) type { } /// Write a native-endian integer. - pub fn writeIntNe(self: *Self, comptime T: type, value: T) Error!void { - return self.writeInt(builtin.endian, T, value); - } - - pub fn writeIntLe(self: *Self, comptime T: type, value: T) Error!void { + pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void { var bytes: [@sizeOf(T)]u8 = undefined; - mem.writeIntLE(T, &bytes, value); + mem.writeIntNative(T, &bytes, value); return self.writeFn(self, bytes); } - pub fn writeIntBe(self: *Self, comptime T: type, value: T) Error!void { + /// Write a foreign-endian integer. + pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void { var bytes: [@sizeOf(T)]u8 = undefined; - mem.writeIntBE(T, &bytes, value); + mem.writeIntForeign(T, &bytes, value); return self.writeFn(self, bytes); } - pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) Error!void { + pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void { var bytes: [@sizeOf(T)]u8 = undefined; - mem.writeInt(bytes[0..], value, endian); + mem.writeIntLittle(T, &bytes, value); + return self.writeFn(self, bytes); + } + + pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void { + var bytes: [@sizeOf(T)]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: [@sizeOf(T)]u8 = undefined; + mem.writeInt(T, &bytes, value, endian); return self.writeFn(self, bytes); } }; @@ -323,23 +343,24 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) const amt_buffered = self.end_index - self.start_index; if (amt_buffered == 0) { assert(self.end_index <= buffer_size); - if (self.end_index == buffer_size) { - // we can read more data from the unbuffered stream - if (dest_space < buffer_size) { - self.start_index = 0; - self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]); - } else { - // asking for so much data that buffering is actually less efficient. - // forward the request directly to the unbuffered stream - const amt_read = try self.unbuffered_in_stream.read(dest[dest_index..]); - return dest_index + amt_read; - } - } else { - // reading from the unbuffered stream returned less than we asked for - // so we cannot read any more data. + // Make sure the last read actually gave us some data + if (self.end_index == 0) { + // reading from the unbuffered stream returned nothing + // so we have nothing left to read. return dest_index; } + // we can read more data from the unbuffered stream + if (dest_space < buffer_size) { + self.start_index = 0; + self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]); + } else { + // asking for so much data that buffering is actually less efficient. + // forward the request directly to the unbuffered stream + const amt_read = try self.unbuffered_in_stream.read(dest[dest_index..]); + return dest_index + amt_read; + } } + const copy_amount = math.min(dest_space, amt_buffered); const copy_end_index = self.start_index + copy_amount; mem.copy(u8, dest[dest_index..], self.buffer[self.start_index..copy_end_index]); @@ -350,6 +371,46 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) }; } +test "io.BufferedInStream" { + const OneByteReadInStream = struct { + const Error = error{NoError}; + const Stream = InStream(Error); + + stream: Stream, + str: []const u8, + curr: usize, + + fn init(str: []const u8) @This() { + return @This(){ + .stream = Stream{ .readFn = readFn }, + .str = str, + .curr = 0, + }; + } + + fn readFn(in_stream: *Stream, dest: []u8) Error!usize { + const self = @fieldParentPtr(@This(), "stream", in_stream); + if (self.str.len <= self.curr or dest.len == 0) + return 0; + + dest[0] = self.str[self.curr]; + self.curr += 1; + return 1; + } + }; + + var buf: [100]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + + const str = "This is a test"; + var one_byte_stream = OneByteReadInStream.init(str); + var buf_in_stream = BufferedInStream(OneByteReadInStream.Error).init(&one_byte_stream.stream); + const stream = &buf_in_stream.stream; + + const res = try stream.readAllAlloc(allocator, str.len + 1); + testing.expectEqualSlices(u8, str, res); +} + /// Creates a stream which supports 'un-reading' data, so that it can be read again. /// This makes look-ahead style parsing much easier. pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type { @@ -446,6 +507,153 @@ pub const SliceInStream = struct { } }; +/// Creates a stream which allows for reading bit fields from another stream +pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { + return struct { + const Self = @This(); + + in_stream: *Stream, + bit_buffer: u7, + bit_count: u3, + stream: Stream, + + pub const Stream = InStream(Error); + const u8_bit_count = comptime meta.bitCount(u8); + const u7_bit_count = comptime meta.bitCount(u7); + const u4_bit_count = comptime meta.bitCount(u4); + + pub fn init(in_stream: *Stream) Self { + return Self{ + .in_stream = in_stream, + .bit_buffer = 0, + .bit_count = 0, + .stream = Stream{ .readFn = read }, + }; + } + + /// Reads `bits` bits from the stream and returns a specified unsigned int type + /// containing them in the least significant end, returning an error if the + /// specified number of bits could not be read. + pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U { + var n: usize = undefined; + const result = try self.readBits(U, bits, &n); + if (n < bits) return error.EndOfStream; + return result; + } + + /// Reads `bits` bits from the stream and returns a specified unsigned int type + /// containing them in the least significant end. The number of bits successfully + /// read is placed in `out_bits`, as reaching the end of the stream is not an error. + pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { + comptime assert(trait.isUnsignedInt(U)); + + //by extending the buffer to a minimum of u8 we can cover a number of edge cases + // related to shifting and casting. + const u_bit_count = comptime meta.bitCount(U); + const buf_bit_count = bc: { + assert(u_bit_count >= bits); + break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; + }; + const Buf = @IntType(false, buf_bit_count); + const BufShift = math.Log2Int(Buf); + + out_bits.* = usize(0); + if (U == u0 or bits == 0) return 0; + var out_buffer = 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); + self.bit_buffer <<= n; + }, + builtin.Endian.Little => { + const value = (self.bit_buffer << shift) >> shift; + out_buffer = Buf(value); + self.bit_buffer >>= n; + }, + } + self.bit_count -= n; + out_bits.* = n; + } + //at this point we know bit_buffer is empty + + //copy bytes until we have enough bits, then leave the rest in bit_buffer + while (out_bits.* < bits) { + const n = bits - out_bits.*; + const next_byte = self.in_stream.readByte() catch |err| { + if (err == error.EndOfStream) { + return @intCast(U, out_buffer); + } + //@BUG: See #1810. Not sure if the bug is that I have to do this for some + // streams, or that I don't for streams with emtpy errorsets. + return @errSetCast(Error, err); + }; + + switch (endian) { + builtin.Endian.Big => { + if (n >= u8_bit_count) { + out_buffer <<= @intCast(u3, u8_bit_count - 1); + out_buffer <<= 1; + out_buffer |= 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_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_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_bits.* += n; + self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); + self.bit_count = shift; + }, + } + } + + return @intCast(U, out_buffer); + } + + pub fn alignToByte(self: *Self) void { + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn read(self_stream: *Stream, buffer: []u8) Error!usize { + var self = @fieldParentPtr(Self, "stream", self_stream); + + var out_bits: usize = undefined; + var out_bits_total = 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| { + b.* = try self.readBits(u8, u8_bit_count, &out_bits); + out_bits_total += out_bits; + } + const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0); + return (out_bits_total / u8_bit_count) + incomplete_byte; + } + + return self.in_stream.read(buffer); + } + }; +} + /// This is a simple OutStream that writes to a slice, and returns an error /// when it runs out of space. pub const SliceOutStream = struct { @@ -498,7 +706,7 @@ test "io.SliceOutStream" { const stream = &slice_stream.stream; try stream.print("{}{}!", "Hello", "World"); - debug.assert(mem.eql(u8, "HelloWorld!", slice_stream.getWritten())); + testing.expectEqualSlices(u8, "HelloWorld!", slice_stream.getWritten()); } var null_out_stream_state = NullOutStream.init(); @@ -560,7 +768,7 @@ test "io.CountingOutStream" { const bytes = "yay" ** 10000; stream.write(bytes) catch unreachable; - debug.assert(counting_stream.bytes_written == bytes.len); + testing.expect(counting_stream.bytes_written == bytes.len); } pub fn BufferedOutStream(comptime Error: type) type { @@ -639,6 +847,135 @@ pub const BufferOutStream = struct { } }; +/// Creates a stream which allows for writing bit fields to another stream +pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { + return struct { + const Self = @This(); + + out_stream: *Stream, + bit_buffer: u8, + bit_count: u4, + stream: Stream, + + pub const Stream = OutStream(Error); + const u8_bit_count = comptime meta.bitCount(u8); + const u4_bit_count = comptime meta.bitCount(u4); + + pub fn init(out_stream: *Stream) Self { + return Self{ + .out_stream = out_stream, + .bit_buffer = 0, + .bit_count = 0, + .stream = Stream{ .writeFn = write }, + }; + } + + /// Write the specified number of bits to the stream from the least significant bits of + /// the specified unsigned int value. Bits will only be written to the stream when there + /// are enough to fill a byte. + pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { + if (bits == 0) return; + + 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 + // related to shifting and casting. + const u_bit_count = comptime meta.bitCount(U); + const buf_bit_count = bc: { + assert(u_bit_count >= bits); + break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; + }; + const Buf = @IntType(false, buf_bit_count); + const BufShift = math.Log2Int(Buf); + + const buf_value = @intCast(Buf, value); + + const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); + var in_buffer = switch (endian) { + builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits), + builtin.Endian.Little => buf_value, + }; + var in_bits = bits; + + if (self.bit_count > 0) { + const bits_remaining = u8_bit_count - self.bit_count; + const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); + switch (endian) { + builtin.Endian.Big => { + const shift = @intCast(BufShift, high_byte_shift + self.bit_count); + const v = @intCast(u8, in_buffer >> shift); + self.bit_buffer |= v; + in_buffer <<= n; + }, + builtin.Endian.Little => { + const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); + self.bit_buffer |= v; + in_buffer >>= n; + }, + } + self.bit_count += n; + in_bits -= n; + + //if we didn't fill the buffer, it's because bits < bits_remaining; + if (self.bit_count != u8_bit_count) return; + try self.out_stream.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + //at this point we know bit_buffer is empty + + //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer + while (in_bits >= u8_bit_count) { + switch (endian) { + builtin.Endian.Big => { + const v = @intCast(u8, in_buffer >> high_byte_shift); + try self.out_stream.writeByte(v); + in_buffer <<= @intCast(u3, u8_bit_count - 1); + in_buffer <<= 1; + }, + builtin.Endian.Little => { + const v = @truncate(u8, in_buffer); + try self.out_stream.writeByte(v); + in_buffer >>= @intCast(u3, u8_bit_count - 1); + in_buffer >>= 1; + }, + } + in_bits -= u8_bit_count; + } + + if (in_bits > 0) { + self.bit_count = @intCast(u4, in_bits); + self.bit_buffer = switch (endian) { + builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift), + builtin.Endian.Little => @truncate(u8, in_buffer), + }; + } + } + + /// Flush any remaining bits to the stream. + pub fn flushBits(self: *Self) Error!void { + if (self.bit_count == 0) return; + try self.out_stream.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn write(self_stream: *Stream, buffer: []const u8) Error!void { + var self = @fieldParentPtr(Self, "stream", self_stream); + + //@NOTE: I'm not sure this is a good idea, maybe flushBits should be forced + if (self.bit_count > 0) { + for (buffer) |b, i| + try self.writeBits(b, u8_bit_count); + return; + } + + return self.out_stream.write(buffer); + } + }; +} + pub const BufferedAtomicFile = struct { atomic_file: os.AtomicFile, file_stream: os.File.OutStream, @@ -647,12 +984,13 @@ pub const BufferedAtomicFile = struct { pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { // TODO with well defined copy elision we don't need this allocation - var self = try allocator.create(BufferedAtomicFile{ + var self = try allocator.create(BufferedAtomicFile); + self.* = BufferedAtomicFile{ .atomic_file = undefined, .file_stream = undefined, .buffered_stream = undefined, .allocator = allocator, - }); + }; errdefer allocator.destroy(self); self.atomic_file = try os.AtomicFile.init(dest_path, os.File.default_mode); @@ -679,12 +1017,6 @@ pub const BufferedAtomicFile = struct { } }; -test "import io tests" { - comptime { - _ = @import("io_test.zig"); - } -} - pub fn readLine(buf: *std.Buffer) ![]u8 { var stdin = try getStdIn(); var stdin_stream = stdin.inStream(); @@ -721,10 +1053,10 @@ test "io.readLineFrom" { ); const stream = &mem_stream.stream; - debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf))); - debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf))); - debug.assertError(readLineFrom(stream, &buf), error.EndOfStream); - debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333")); + testing.expectEqualSlices(u8, "Line 1", try readLineFrom(stream, &buf)); + testing.expectEqualSlices(u8, "Line 22", try readLineFrom(stream, &buf)); + testing.expectError(error.EndOfStream, readLineFrom(stream, &buf)); + testing.expectEqualSlices(u8, "Line 1Line 22Line 333", buf.toSlice()); } pub fn readLineSlice(slice: []u8) ![]u8 { @@ -752,6 +1084,372 @@ test "io.readLineSliceFrom" { ); const stream = &mem_stream.stream; - debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); - debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory); + testing.expectEqualSlices(u8, "Line 1", try readLineSliceFrom(stream, buf[0..])); + testing.expectError(error.OutOfMemory, readLineSliceFrom(stream, buf[0..])); } + +/// Creates a deserializer that deserializes types from any stream. +/// If `is_packed` is true, the data stream is treated as bit-packed, +/// otherwise data is expected to be packed to the smallest byte. +/// Types may implement a custom deserialization routine with a +/// function named `deserialize` in the form of: +/// pub fn deserialize(self: *Self, deserializer: var) !void +/// which will be called when the deserializer is used to deserialize +/// that type. It will pass a pointer to the type instance to deserialize +/// into and a pointer to the deserializer struct. +pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type { + return struct { + const Self = @This(); + + in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream, + + pub const Stream = InStream(Error); + + pub fn init(in_stream: *Stream) Self { + return Self{ .in_stream = switch (is_packed) { + true => BitInStream(endian, Stream.Error).init(in_stream), + else => in_stream, + } }; + } + + pub fn alignToByte(self: *Self) void { + if (!is_packed) return; + self.in_stream.alignToByte(); + } + + //@BUG: inferred error issue. See: #1386 + fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T { + comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); + + const u8_bit_count = 8; + const t_bit_count = comptime meta.bitCount(T); + + const U = @IntType(false, t_bit_count); + const Log2U = math.Log2Int(U); + const int_size = (U.bit_count + 7) / 8; + + if (is_packed) { + const result = try self.in_stream.readBitsNoEof(U, t_bit_count); + return @bitCast(T, result); + } + + var buffer: [int_size]u8 = undefined; + const read_size = try self.in_stream.read(buffer[0..]); + if (read_size < int_size) return error.EndOfStream; + + if (int_size == 1) { + if (t_bit_count == 8) return @bitCast(T, buffer[0]); + const PossiblySignedByte = @IntType(T.is_signed, 8); + return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); + } + + var result = 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); + }, + } + } + + 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; + try self.deserializeInto(&value); + return value; + } + + /// Deserializes data into the type pointed to by `ptr` + pub fn deserializeInto(self: *Self, ptr: var) !void { + 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)) { + for (ptr) |*v| + try self.deserializeInto(v); + return; + } + + comptime assert(trait.isSingleItemPtr(T)); + + const C = comptime meta.Child(T); + const child_type_id = @typeId(C); + + //custom deserializer: fn(self: *Self, deserializer: var) !void + if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); + + if (comptime trait.isPacked(C) and !is_packed) { + var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream); + return packed_deserializer.deserializeInto(ptr); + } + + switch (child_type_id) { + builtin.TypeId.Void => return, + builtin.TypeId.Bool => ptr.* = (try self.deserializeInt(u1)) > 0, + builtin.TypeId.Float, builtin.TypeId.Int => ptr.* = try self.deserializeInt(C), + builtin.TypeId.Struct => { + const info = @typeInfo(C).Struct; + + inline for (info.fields) |*field_info| { + const name = field_info.name; + const FieldType = field_info.field_type; + + if (FieldType == void or FieldType == u0) continue; + + //it doesn't make any sense to read pointers + if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) { + @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++ + @typeName(C) ++ " because it " ++ "is of pointer-type " ++ + @typeName(FieldType) ++ "."); + } + + try self.deserializeInto(&@field(ptr, name)); + } + }, + builtin.TypeId.Union => { + const info = @typeInfo(C).Union; + if (info.tag_type) |TagType| { + //we avoid duplicate iteration over the enum tags + // by getting the int directly and casting it without + // safety. If it is bad, it will be caught anyway. + 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); + try self.deserializeInto(&@field(ptr, name)); + return; + } + } + //This is reachable if the enum data is bad + return error.InvalidEnumTag; + } + @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++ + " because it is an untagged union Use a custom deserialize()."); + }, + builtin.TypeId.Optional => { + const OC = comptime meta.Child(C); + const exists = (try self.deserializeInt(u1)) > 0; + if (!exists) { + ptr.* = null; + return; + } + + //The way non-pointer optionals are implemented ensures a pointer to them + // will point to the value. The flag is stored at the end of that data. + var val_ptr = @ptrCast(*OC, ptr); + try self.deserializeInto(val_ptr); + //This bit ensures the null flag isn't set. Any actual copying should be + // optimized out... I hope. + ptr.* = val_ptr.*; + }, + builtin.TypeId.Enum => { + var value = try self.deserializeInt(@TagType(C)); + ptr.* = try meta.intToEnum(C, value); + }, + else => { + @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented)."); + }, + } + } + }; +} + +/// Creates a serializer that serializes types to any stream. +/// If `is_packed` is true, the data will be bit-packed into the stream. +/// Note that the you must call `serializer.flush()` when you are done +/// writing bit-packed data in order ensure any unwritten bits are committed. +/// If `is_packed` is false, data is packed to the smallest byte. In the case +/// of packed structs, the struct will written bit-packed and with the specified +/// endianess, after which data will resume being written at the next byte boundary. +/// Types may implement a custom serialization routine with a +/// function named `serialize` in the form of: +/// pub fn serialize(self: Self, serializer: var) !void +/// which will be called when the serializer is used to serialize that type. It will +/// pass a const pointer to the type instance to be serialized and a pointer +/// to the serializer struct. +pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type { + return struct { + const Self = @This(); + + out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream, + + pub const Stream = OutStream(Error); + + pub fn init(out_stream: *Stream) Self { + return Self{ .out_stream = switch (is_packed) { + true => BitOutStream(endian, Stream.Error).init(out_stream), + else => out_stream, + } }; + } + + /// Flushes any unwritten bits to the stream + pub fn flush(self: *Self) Error!void { + if (is_packed) return self.out_stream.flushBits(); + } + + fn serializeInt(self: *Self, value: var) Error!void { + 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); + const u8_bit_count = comptime meta.bitCount(u8); + + const U = @IntType(false, t_bit_count); + const Log2U = math.Log2Int(U); + const int_size = (U.bit_count + 7) / 8; + + const u_value = @bitCast(U, value); + + if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count); + + var buffer: [int_size]u8 = undefined; + if (int_size == 1) buffer[0] = u_value; + + for (buffer) |*byte, i| { + const idx = switch (endian) { + builtin.Endian.Big => int_size - i - 1, + builtin.Endian.Little => i, + }; + const shift = @intCast(Log2U, idx * u8_bit_count); + const v = u_value >> shift; + byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); + } + + 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); + + if (comptime trait.isIndexable(T)) { + for (value) |v| + try self.serialize(v); + return; + } + + //custom serializer: fn(self: Self, serializer: var) !void + if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); + + if (comptime trait.isPacked(T) and !is_packed) { + var packed_serializer = Serializer(endian, true, Error).init(self.out_stream); + try packed_serializer.serialize(value); + try packed_serializer.flush(); + return; + } + + switch (@typeId(T)) { + builtin.TypeId.Void => return, + builtin.TypeId.Bool => try self.serializeInt(u1(@boolToInt(value))), + builtin.TypeId.Float, builtin.TypeId.Int => try self.serializeInt(value), + builtin.TypeId.Struct => { + const info = @typeInfo(T); + + inline for (info.Struct.fields) |*field_info| { + const name = field_info.name; + const FieldType = field_info.field_type; + + if (FieldType == void or FieldType == u0) continue; + + //It doesn't make sense to write pointers + if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) { + @compileError("Will not " ++ "serialize field " ++ name ++ + " of struct " ++ @typeName(T) ++ " because it " ++ + "is of pointer-type " ++ @typeName(FieldType) ++ "."); + } + try self.serialize(@field(value, name)); + } + }, + builtin.TypeId.Union => { + const info = @typeInfo(T).Union; + if (info.tag_type) |TagType| { + const active_tag = meta.activeTag(value); + try self.serialize(active_tag); + //This inline loop is necessary because active_tag is a runtime + // value, but @field requires a comptime value. Our alternative + // is to check each field for a match + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == @enumToInt(active_tag)) { + const name = field_info.name; + const FieldType = field_info.field_type; + try self.serialize(@field(value, name)); + return; + } + } + unreachable; + } + @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++ + " because it is an untagged union Use a custom serialize()."); + }, + builtin.TypeId.Optional => { + if (value == null) { + try self.serializeInt(u1(@boolToInt(false))); + return; + } + try self.serializeInt(u1(@boolToInt(true))); + + const OC = comptime meta.Child(T); + + //The way non-pointer optionals are implemented ensures a pointer to them + // will point to the value. The flag is stored at the end of that data. + var val_ptr = @ptrCast(*const OC, &value); + try self.serialize(val_ptr.*); + }, + builtin.TypeId.Enum => { + try self.serializeInt(@enumToInt(value)); + }, + else => @compileError("Cannot serialize " ++ @tagName(@typeId(T)) ++ " types (unimplemented)."), + } + } + }; +} + +test "import io tests" { + comptime { + _ = @import("io_test.zig"); + } +} + diff --git a/std/io_test.zig b/std/io_test.zig index 5224199527..fb6e0ae7e9 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -1,8 +1,10 @@ const std = @import("index.zig"); const io = std.io; +const meta = std.meta; +const trait = std.trait; const DefaultPrng = std.rand.DefaultPrng; -const assert = std.debug.assert; -const assertError = std.debug.assertError; +const expect = std.testing.expect; +const expectError = std.testing.expectError; const mem = std.mem; const os = std.os; const builtin = @import("builtin"); @@ -33,7 +35,7 @@ test "write a file, read it, then delete it" { const file_size = try file.getEndPos(); const expected_file_size = "begin".len + data.len + "end".len; - assert(file_size == expected_file_size); + expect(file_size == expected_file_size); var file_in_stream = file.inStream(); var buf_stream = io.BufferedInStream(os.File.ReadError).init(&file_in_stream.stream); @@ -41,9 +43,9 @@ test "write a file, read it, then delete it" { const contents = try st.readAllAlloc(allocator, 2 * 1024); defer allocator.free(contents); - assert(mem.eql(u8, contents[0.."begin".len], "begin")); - assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); - assert(mem.eql(u8, contents[contents.len - "end".len ..], "end")); + 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[contents.len - "end".len ..], "end")); } try os.deleteFile(tmp_file_name); } @@ -59,7 +61,7 @@ test "BufferOutStream" { const y: i32 = 1234; try buf_stream.print("x: {}\ny: {}\n", x, y); - assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); + expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); } test "SliceInStream" { @@ -69,15 +71,15 @@ test "SliceInStream" { var dest: [4]u8 = undefined; var read = try ss.stream.read(dest[0..4]); - assert(read == 4); - assert(mem.eql(u8, dest[0..4], bytes[0..4])); + expect(read == 4); + expect(mem.eql(u8, dest[0..4], bytes[0..4])); read = try ss.stream.read(dest[0..4]); - assert(read == 3); - assert(mem.eql(u8, dest[0..3], bytes[4..7])); + expect(read == 3); + expect(mem.eql(u8, dest[0..3], bytes[4..7])); read = try ss.stream.read(dest[0..4]); - assert(read == 0); + expect(read == 0); } test "PeekStream" { @@ -91,26 +93,26 @@ test "PeekStream" { ps.putBackByte(10); var read = try ps.stream.read(dest[0..4]); - assert(read == 4); - assert(dest[0] == 10); - assert(dest[1] == 9); - assert(mem.eql(u8, dest[2..4], bytes[0..2])); + expect(read == 4); + expect(dest[0] == 10); + expect(dest[1] == 9); + expect(mem.eql(u8, dest[2..4], bytes[0..2])); read = try ps.stream.read(dest[0..4]); - assert(read == 4); - assert(mem.eql(u8, dest[0..4], bytes[2..6])); + expect(read == 4); + expect(mem.eql(u8, dest[0..4], bytes[2..6])); read = try ps.stream.read(dest[0..4]); - assert(read == 2); - assert(mem.eql(u8, dest[0..2], bytes[6..8])); + expect(read == 2); + expect(mem.eql(u8, dest[0..2], bytes[6..8])); ps.putBackByte(11); ps.putBackByte(12); read = try ps.stream.read(dest[0..4]); - assert(read == 2); - assert(dest[0] == 12); - assert(dest[1] == 11); + expect(read == 2); + expect(dest[0] == 12); + expect(dest[1] == 11); } test "SliceOutStream" { @@ -118,17 +120,461 @@ test "SliceOutStream" { var ss = io.SliceOutStream.init(buffer[0..]); try ss.stream.write("Hello"); - assert(mem.eql(u8, ss.getWritten(), "Hello")); + expect(mem.eql(u8, ss.getWritten(), "Hello")); try ss.stream.write("world"); - assert(mem.eql(u8, ss.getWritten(), "Helloworld")); + expect(mem.eql(u8, ss.getWritten(), "Helloworld")); - assertError(ss.stream.write("!"), error.OutOfSpace); - assert(mem.eql(u8, ss.getWritten(), "Helloworld")); + expectError(error.OutOfSpace, ss.stream.write("!")); + expect(mem.eql(u8, ss.getWritten(), "Helloworld")); ss.reset(); - assert(ss.getWritten().len == 0); + expect(ss.getWritten().len == 0); - assertError(ss.stream.write("Hello world!"), error.OutOfSpace); - assert(mem.eql(u8, ss.getWritten(), "Hello worl")); + expectError(error.OutOfSpace, ss.stream.write("Hello world!")); + expect(mem.eql(u8, ss.getWritten(), "Hello worl")); +} + +test "BitInStream" { + const mem_be = []u8{ 0b11001101, 0b00001011 }; + const mem_le = []u8{ 0b00011101, 0b10010101 }; + + var mem_in_be = io.SliceInStream.init(mem_be[0..]); + const InError = io.SliceInStream.Error; + var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream); + + var out_bits: usize = undefined; + + expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); + expect(out_bits == 15); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); + expect(out_bits == 16); + + _ = try bit_stream_be.readBits(u0, 0, &out_bits); + + expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); + expect(out_bits == 0); + expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); + + var mem_in_le = io.SliceInStream.init(mem_le[0..]); + var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); + + expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); + expect(out_bits == 15); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); + expect(out_bits == 16); + + _ = try bit_stream_le.readBits(u0, 0, &out_bits); + + expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); + expect(out_bits == 0); + expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); +} + +test "BitOutStream" { + var mem_be = []u8{0} ** 2; + var mem_le = []u8{0} ** 2; + + var mem_out_be = io.SliceOutStream.init(mem_be[0..]); + 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); + + 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.flushBits(); + expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); + + mem_out_be.pos = 0; + try bit_stream_be.writeBits(u32(0b110011010000101), 16); + expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); + + try bit_stream_be.writeBits(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); + + 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.flushBits(); + expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(u32(0b1100110100001011), 16); + expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); + + try bit_stream_le.writeBits(u0(0), 0); +} + +test "BitStreams with File Stream" { + const tmp_file_name = "temp_test_file.txt"; + { + var file = try os.File.openWrite(tmp_file_name); + defer file.close(); + + var file_out = file.outStream(); + var file_out_stream = &file_out.stream; + const OutError = os.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.flushBits(); + } + { + var file = try os.File.openRead(tmp_file_name); + defer file.close(); + + var file_in = file.inStream(); + var file_in_stream = &file_in.stream; + const InError = os.File.ReadError; + var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); + + var out_bits: usize = undefined; + + expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); + } + try os.deleteFile(tmp_file_name); +} + +fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + //@NOTE: if this test is taking too long, reduce the maximum tested bitsize + const max_test_bitsize = 128; + + const total_bytes = comptime blk: { + var bytes = 0; + comptime var i = 0; + while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); + break :blk bytes * 2; + }; + + var data_mem: [total_bytes]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + comptime var i = 0; + 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.flush(); + + i = 0; + inline while (i <= max_test_bitsize) : (i += 1) { + const U = @IntType(false, i); + 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); + } + + const u8_bit_count = comptime meta.bitCount(u8); + //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 + //and we have each for unsigned and signed, so * 2 + const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); + const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); + const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; + + expect(in.pos == if (is_packed) total_packed_bytes else total_bytes); + + //Verify that empty error set works with serializer. + //deserializer is covered by SliceInStream + const NullError = io.NullOutStream.Error; + var null_out = io.NullOutStream.init(); + var null_out_stream = &null_out.stream; + var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream); + try null_serializer.serialize(data_mem[0..]); + try null_serializer.flush(); +} + +test "Serializer/Deserializer Int" { + try testIntSerializerDeserializer(builtin.Endian.Big, false); + try testIntSerializerDeserializer(builtin.Endian.Little, false); + try testIntSerializerDeserializer(builtin.Endian.Big, true); + try testIntSerializerDeserializer(builtin.Endian.Little, true); +} + +fn testIntSerializerDeserializerInfNaN(comptime endian: builtin.Endian, + comptime is_packed: bool) !void +{ + const mem_size = (16*2 + 32*2 + 64*2 + 128*2) / comptime meta.bitCount(u8); + var data_mem: [mem_size]u8 = undefined; + + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + //@TODO: isInf/isNan not currently implemented for f128. + try serializer.serialize(std.math.nan(f16)); + try serializer.serialize(std.math.inf(f16)); + try serializer.serialize(std.math.nan(f32)); + try serializer.serialize(std.math.inf(f32)); + try serializer.serialize(std.math.nan(f64)); + try serializer.serialize(std.math.inf(f64)); + //try serializer.serialize(std.math.nan(f128)); + //try serializer.serialize(std.math.inf(f128)); + const nan_check_f16 = try deserializer.deserialize(f16); + const inf_check_f16 = try deserializer.deserialize(f16); + const nan_check_f32 = try deserializer.deserialize(f32); + const inf_check_f32 = try deserializer.deserialize(f32); + const nan_check_f64 = try deserializer.deserialize(f64); + const inf_check_f64 = try deserializer.deserialize(f64); + //const nan_check_f128 = try deserializer.deserialize(f128); + //const inf_check_f128 = try deserializer.deserialize(f128); + expect(std.math.isNan(nan_check_f16)); + expect(std.math.isInf(inf_check_f16)); + expect(std.math.isNan(nan_check_f32)); + expect(std.math.isInf(inf_check_f32)); + expect(std.math.isNan(nan_check_f64)); + expect(std.math.isInf(inf_check_f64)); + //expect(std.math.isNan(nan_check_f128)); + //expect(std.math.isInf(inf_check_f128)); +} + +test "Serializer/Deserializer Int: Inf/NaN" { + try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, false); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, false); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, true); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true); +} + +fn testAlternateSerializer(self: var, serializer: var) !void { + try serializer.serialize(self.f_f16); +} + +fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + const ColorType = enum(u4) { + RGB8 = 1, + RA16 = 2, + R32 = 3, + }; + + const TagAlign = union(enum(u32)) { + A: u8, + B: u8, + C: u8, + }; + + const Color = union(ColorType) { + RGB8: struct { + r: u8, + g: u8, + b: u8, + a: u8, + }, + RA16: struct { + r: u16, + a: u16, + }, + R32: u32, + }; + + const PackedStruct = packed struct { + f_i3: i3, + f_u2: u2, + }; + + + + //to test custom serialization + const Custom = struct { + f_f16: f16, + f_unused_u32: u32, + + pub fn deserialize(self: *@This(), deserializer: var) !void { + try deserializer.deserializeInto(&self.f_f16); + self.f_unused_u32 = 47; + } + + pub const serialize = testAlternateSerializer; + }; + + const MyStruct = struct { + f_i3: i3, + f_u8: u8, + f_tag_align: TagAlign, + f_u24: u24, + f_i19: i19, + f_void: void, + f_f32: f32, + f_f128: f128, + f_packed_0: PackedStruct, + f_i7arr: [10]i7, + f_of64n: ?f64, + f_of64v: ?f64, + f_color_type: ColorType, + f_packed_1: PackedStruct, + f_custom: Custom, + f_color: Color, + }; + + const my_inst = MyStruct{ + .f_i3 = -1, + .f_u8 = 8, + .f_tag_align = TagAlign{ .B = 148 }, + .f_u24 = 24, + .f_i19 = 19, + .f_void = {}, + .f_f32 = 32.32, + .f_f128 = 128.128, + .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, + .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + .f_of64n = null, + .f_of64v = 64.64, + .f_color_type = ColorType.R32, + .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, + .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, + .f_color = Color{ .R32 = 123822 }, + }; + + var data_mem: [@sizeOf(MyStruct)]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + try serializer.serialize(my_inst); + + const my_copy = try deserializer.deserialize(MyStruct); + expect(meta.eql(my_copy, my_inst)); +} + +test "Serializer/Deserializer generic" { + try testSerializerDeserializer(builtin.Endian.Big, false); + try testSerializerDeserializer(builtin.Endian.Little, false); + try testSerializerDeserializer(builtin.Endian.Big, true); + try testSerializerDeserializer(builtin.Endian.Little, true); +} + +fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + const E = enum(u14) { + One = 1, + Two = 2, + }; + + const A = struct { + e: E, + }; + + const C = union(E) { + One: u14, + Two: f16, + }; + + var data_mem: [4]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + try serializer.serialize(u14(3)); + expectError(error.InvalidEnumTag, deserializer.deserialize(A)); + out.pos = 0; + try serializer.serialize(u14(3)); + try serializer.serialize(u14(88)); + expectError(error.InvalidEnumTag, deserializer.deserialize(C)); +} + +test "Deserializer bad data" { + try testBadData(builtin.Endian.Big, false); + try testBadData(builtin.Endian.Little, false); + try testBadData(builtin.Endian.Big, true); + try testBadData(builtin.Endian.Little, true); } diff --git a/std/json.zig b/std/json.zig index 4d07d7b89d..dd053e7635 100644 --- a/std/json.zig +++ b/std/json.zig @@ -4,6 +4,7 @@ const std = @import("index.zig"); const debug = std.debug; +const testing = std.testing; const mem = std.mem; const maxInt = std.math.maxInt; @@ -960,7 +961,7 @@ test "json.token" { checkNext(&p, Token.Id.ObjectEnd); checkNext(&p, Token.Id.ObjectEnd); - debug.assert((try p.next()) == null); + testing.expect((try p.next()) == null); } // Validate a JSON string. This does not limit number precision so a decoder may not necessarily @@ -981,7 +982,7 @@ pub fn validate(s: []const u8) bool { } test "json.validate" { - debug.assert(validate("{}")); + testing.expect(validate("{}")); } const Allocator = std.mem.Allocator; @@ -1344,7 +1345,7 @@ pub const Parser = struct { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else - @panic("TODO: fmt.parseFloat not yet implemented"); + Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) }; } }; @@ -1365,7 +1366,8 @@ test "json.parser.dynamic" { \\ }, \\ "Animated" : false, \\ "IDs": [116, 943, 234, 38793], - \\ "ArrayOfObject": [{"n": "m"}] + \\ "ArrayOfObject": [{"n": "m"}], + \\ "double": 1.3412 \\ } \\} ; @@ -1378,20 +1380,23 @@ test "json.parser.dynamic" { var image = root.Object.get("Image").?.value; const width = image.Object.get("Width").?.value; - debug.assert(width.Integer == 800); + testing.expect(width.Integer == 800); const height = image.Object.get("Height").?.value; - debug.assert(height.Integer == 600); + testing.expect(height.Integer == 600); const title = image.Object.get("Title").?.value; - debug.assert(mem.eql(u8, title.String, "View from 15th Floor")); + testing.expect(mem.eql(u8, title.String, "View from 15th Floor")); const animated = image.Object.get("Animated").?.value; - debug.assert(animated.Bool == false); + testing.expect(animated.Bool == false); const array_of_object = image.Object.get("ArrayOfObject").?.value; - debug.assert(array_of_object.Array.len == 1); + testing.expect(array_of_object.Array.len == 1); const obj0 = array_of_object.Array.at(0).Object.get("n").?.value; - debug.assert(mem.eql(u8, obj0.String, "m")); + testing.expect(mem.eql(u8, obj0.String, "m")); + + const double = image.Object.get("double").?.value; + testing.expect(double.Float == 1.3412); } diff --git a/std/json_test.zig b/std/json_test.zig index 9e19ec592a..edc50be8cb 100644 --- a/std/json_test.zig +++ b/std/json_test.zig @@ -6,15 +6,15 @@ const std = @import("index.zig"); fn ok(comptime s: []const u8) void { - std.debug.assert(std.json.validate(s)); + std.testing.expect(std.json.validate(s)); } fn err(comptime s: []const u8) void { - std.debug.assert(!std.json.validate(s)); + std.testing.expect(!std.json.validate(s)); } fn any(comptime s: []const u8) void { - std.debug.assert(true); + std.testing.expect(true); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/std/lazy_init.zig b/std/lazy_init.zig index f08c01e874..a09168786b 100644 --- a/std/lazy_init.zig +++ b/std/lazy_init.zig @@ -1,6 +1,7 @@ const std = @import("index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const testing = std.testing; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -63,12 +64,12 @@ test "std.lazyInit" { global_number.resolve(); } if (global_number.get()) |x| { - assert(x.* == 1234); + testing.expect(x.* == 1234); } else { @panic("bad"); } if (global_number.get()) |x| { - assert(x.* == 1234); + testing.expect(x.* == 1234); } else { @panic("bad"); } @@ -80,6 +81,6 @@ test "std.lazyInit(void)" { if (global_void.get()) |_| @panic("bad") else { global_void.resolve(); } - assert(global_void.get() != null); - assert(global_void.get() != null); + testing.expect(global_void.get() != null); + testing.expect(global_void.get() != null); } diff --git a/std/linked_list.zig b/std/linked_list.zig index c3db55b5a6..86e5cd056e 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -1,6 +1,7 @@ const std = @import("index.zig"); const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const mem = std.mem; const Allocator = mem.Allocator; @@ -190,7 +191,7 @@ pub fn LinkedList(comptime T: type) type { /// Returns: /// A pointer to the new node. pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { - return allocator.create(Node(undefined)); + return allocator.create(Node); } /// Deallocate a node. @@ -246,7 +247,7 @@ test "basic linked list test" { var it = list.first; var index: u32 = 1; while (it) |node| : (it = node.next) { - assert(node.data == index); + testing.expect(node.data == index); index += 1; } } @@ -256,7 +257,7 @@ test "basic linked list test" { var it = list.last; var index: u32 = 1; while (it) |node| : (it = node.prev) { - assert(node.data == (6 - index)); + testing.expect(node.data == (6 - index)); index += 1; } } @@ -265,9 +266,9 @@ test "basic linked list test" { var last = list.pop(); // {2, 3, 4} list.remove(three); // {2, 4} - assert(list.first.?.data == 2); - assert(list.last.?.data == 4); - assert(list.len == 2); + testing.expect(list.first.?.data == 2); + testing.expect(list.last.?.data == 4); + testing.expect(list.len == 2); } test "linked list concatenation" { @@ -294,18 +295,18 @@ test "linked list concatenation" { list1.concatByMoving(&list2); - assert(list1.last == five); - assert(list1.len == 5); - assert(list2.first == null); - assert(list2.last == null); - assert(list2.len == 0); + testing.expect(list1.last == five); + testing.expect(list1.len == 5); + testing.expect(list2.first == null); + testing.expect(list2.last == null); + testing.expect(list2.len == 0); // Traverse forwards. { var it = list1.first; var index: u32 = 1; while (it) |node| : (it = node.next) { - assert(node.data == index); + testing.expect(node.data == index); index += 1; } } @@ -315,7 +316,7 @@ test "linked list concatenation" { var it = list1.last; var index: u32 = 1; while (it) |node| : (it = node.prev) { - assert(node.data == (6 - index)); + testing.expect(node.data == (6 - index)); index += 1; } } @@ -328,7 +329,7 @@ test "linked list concatenation" { var it = list2.first; var index: u32 = 1; while (it) |node| : (it = node.next) { - assert(node.data == index); + testing.expect(node.data == index); index += 1; } } @@ -338,7 +339,7 @@ test "linked list concatenation" { var it = list2.last; var index: u32 = 1; while (it) |node| : (it = node.prev) { - assert(node.data == (6 - index)); + testing.expect(node.data == (6 - index)); index += 1; } } diff --git a/std/math/acos.zig b/std/math/acos.zig index 54844e8f6e..734f7a8651 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -4,7 +4,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn acos(x: var) @typeOf(x) { const T = @typeOf(x); @@ -143,38 +143,38 @@ fn acos64(x: f64) f64 { } test "math.acos" { - assert(acos(f32(0.0)) == acos32(0.0)); - assert(acos(f64(0.0)) == acos64(0.0)); + expect(acos(f32(0.0)) == acos32(0.0)); + expect(acos(f64(0.0)) == acos64(0.0)); } test "math.acos32" { const epsilon = 0.000001; - assert(math.approxEq(f32, acos32(0.0), 1.570796, epsilon)); - assert(math.approxEq(f32, acos32(0.2), 1.369438, epsilon)); - assert(math.approxEq(f32, acos32(0.3434), 1.220262, epsilon)); - assert(math.approxEq(f32, acos32(0.5), 1.047198, epsilon)); - assert(math.approxEq(f32, acos32(0.8923), 0.468382, epsilon)); - assert(math.approxEq(f32, acos32(-0.2), 1.772154, epsilon)); + expect(math.approxEq(f32, acos32(0.0), 1.570796, epsilon)); + expect(math.approxEq(f32, acos32(0.2), 1.369438, epsilon)); + expect(math.approxEq(f32, acos32(0.3434), 1.220262, epsilon)); + expect(math.approxEq(f32, acos32(0.5), 1.047198, epsilon)); + expect(math.approxEq(f32, acos32(0.8923), 0.468382, epsilon)); + expect(math.approxEq(f32, acos32(-0.2), 1.772154, epsilon)); } test "math.acos64" { const epsilon = 0.000001; - assert(math.approxEq(f64, acos64(0.0), 1.570796, epsilon)); - assert(math.approxEq(f64, acos64(0.2), 1.369438, epsilon)); - assert(math.approxEq(f64, acos64(0.3434), 1.220262, epsilon)); - assert(math.approxEq(f64, acos64(0.5), 1.047198, epsilon)); - assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon)); - assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon)); + expect(math.approxEq(f64, acos64(0.0), 1.570796, epsilon)); + expect(math.approxEq(f64, acos64(0.2), 1.369438, epsilon)); + expect(math.approxEq(f64, acos64(0.3434), 1.220262, epsilon)); + expect(math.approxEq(f64, acos64(0.5), 1.047198, epsilon)); + expect(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon)); + expect(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon)); } test "math.acos32.special" { - assert(math.isNan(acos32(-2))); - assert(math.isNan(acos32(1.5))); + expect(math.isNan(acos32(-2))); + expect(math.isNan(acos32(1.5))); } test "math.acos64.special" { - assert(math.isNan(acos64(-2))); - assert(math.isNan(acos64(1.5))); + expect(math.isNan(acos64(-2))); + expect(math.isNan(acos64(1.5))); } diff --git a/std/math/acosh.zig b/std/math/acosh.zig index 9be323e1f6..a9c4a908ef 100644 --- a/std/math/acosh.zig +++ b/std/math/acosh.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn acosh(x: var) @typeOf(x) { const T = @typeOf(x); @@ -55,34 +55,34 @@ fn acosh64(x: f64) f64 { } test "math.acosh" { - assert(acosh(f32(1.5)) == acosh32(1.5)); - assert(acosh(f64(1.5)) == acosh64(1.5)); + expect(acosh(f32(1.5)) == acosh32(1.5)); + expect(acosh(f64(1.5)) == acosh64(1.5)); } test "math.acosh32" { const epsilon = 0.000001; - assert(math.approxEq(f32, acosh32(1.5), 0.962424, epsilon)); - assert(math.approxEq(f32, acosh32(37.45), 4.315976, epsilon)); - assert(math.approxEq(f32, acosh32(89.123), 5.183133, epsilon)); - assert(math.approxEq(f32, acosh32(123123.234375), 12.414088, epsilon)); + expect(math.approxEq(f32, acosh32(1.5), 0.962424, epsilon)); + expect(math.approxEq(f32, acosh32(37.45), 4.315976, epsilon)); + expect(math.approxEq(f32, acosh32(89.123), 5.183133, epsilon)); + expect(math.approxEq(f32, acosh32(123123.234375), 12.414088, epsilon)); } test "math.acosh64" { const epsilon = 0.000001; - assert(math.approxEq(f64, acosh64(1.5), 0.962424, epsilon)); - assert(math.approxEq(f64, acosh64(37.45), 4.315976, epsilon)); - assert(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon)); - assert(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon)); + expect(math.approxEq(f64, acosh64(1.5), 0.962424, epsilon)); + expect(math.approxEq(f64, acosh64(37.45), 4.315976, epsilon)); + expect(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon)); + expect(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon)); } test "math.acosh32.special" { - assert(math.isNan(acosh32(math.nan(f32)))); - assert(math.isSignalNan(acosh32(0.5))); + expect(math.isNan(acosh32(math.nan(f32)))); + expect(math.isSignalNan(acosh32(0.5))); } test "math.acosh64.special" { - assert(math.isNan(acosh64(math.nan(f64)))); - assert(math.isSignalNan(acosh64(0.5))); + expect(math.isNan(acosh64(math.nan(f64)))); + expect(math.isSignalNan(acosh64(0.5))); } diff --git a/std/math/asin.zig b/std/math/asin.zig index 30b3a57e32..c9dbdf704f 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -5,7 +5,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn asin(x: var) @typeOf(x) { const T = @typeOf(x); @@ -136,42 +136,42 @@ fn asin64(x: f64) f64 { } test "math.asin" { - assert(asin(f32(0.0)) == asin32(0.0)); - assert(asin(f64(0.0)) == asin64(0.0)); + expect(asin(f32(0.0)) == asin32(0.0)); + expect(asin(f64(0.0)) == asin64(0.0)); } test "math.asin32" { const epsilon = 0.000001; - assert(math.approxEq(f32, asin32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, asin32(0.2), 0.201358, epsilon)); - assert(math.approxEq(f32, asin32(-0.2), -0.201358, epsilon)); - assert(math.approxEq(f32, asin32(0.3434), 0.350535, epsilon)); - assert(math.approxEq(f32, asin32(0.5), 0.523599, epsilon)); - assert(math.approxEq(f32, asin32(0.8923), 1.102415, epsilon)); + expect(math.approxEq(f32, asin32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, asin32(0.2), 0.201358, epsilon)); + expect(math.approxEq(f32, asin32(-0.2), -0.201358, epsilon)); + expect(math.approxEq(f32, asin32(0.3434), 0.350535, epsilon)); + expect(math.approxEq(f32, asin32(0.5), 0.523599, epsilon)); + expect(math.approxEq(f32, asin32(0.8923), 1.102415, epsilon)); } test "math.asin64" { const epsilon = 0.000001; - assert(math.approxEq(f64, asin64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, asin64(0.2), 0.201358, epsilon)); - assert(math.approxEq(f64, asin64(-0.2), -0.201358, epsilon)); - assert(math.approxEq(f64, asin64(0.3434), 0.350535, epsilon)); - assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon)); - assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon)); + expect(math.approxEq(f64, asin64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, asin64(0.2), 0.201358, epsilon)); + expect(math.approxEq(f64, asin64(-0.2), -0.201358, epsilon)); + expect(math.approxEq(f64, asin64(0.3434), 0.350535, epsilon)); + expect(math.approxEq(f64, asin64(0.5), 0.523599, epsilon)); + expect(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon)); } test "math.asin32.special" { - assert(asin32(0.0) == 0.0); - assert(asin32(-0.0) == -0.0); - assert(math.isNan(asin32(-2))); - assert(math.isNan(asin32(1.5))); + expect(asin32(0.0) == 0.0); + expect(asin32(-0.0) == -0.0); + expect(math.isNan(asin32(-2))); + expect(math.isNan(asin32(1.5))); } test "math.asin64.special" { - assert(asin64(0.0) == 0.0); - assert(asin64(-0.0) == -0.0); - assert(math.isNan(asin64(-2))); - assert(math.isNan(asin64(1.5))); + expect(asin64(0.0) == 0.0); + expect(asin64(-0.0) == -0.0); + expect(math.isNan(asin64(-2))); + expect(math.isNan(asin64(1.5))); } diff --git a/std/math/asinh.zig b/std/math/asinh.zig index 98892bcbcb..05bd8d008e 100644 --- a/std/math/asinh.zig +++ b/std/math/asinh.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn asinh(x: var) @typeOf(x) { @@ -83,46 +83,46 @@ fn asinh64(x: f64) f64 { } test "math.asinh" { - assert(asinh(f32(0.0)) == asinh32(0.0)); - assert(asinh(f64(0.0)) == asinh64(0.0)); + expect(asinh(f32(0.0)) == asinh32(0.0)); + expect(asinh(f64(0.0)) == asinh64(0.0)); } test "math.asinh32" { const epsilon = 0.000001; - assert(math.approxEq(f32, asinh32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, asinh32(0.2), 0.198690, epsilon)); - assert(math.approxEq(f32, asinh32(0.8923), 0.803133, epsilon)); - assert(math.approxEq(f32, asinh32(1.5), 1.194763, epsilon)); - assert(math.approxEq(f32, asinh32(37.45), 4.316332, epsilon)); - assert(math.approxEq(f32, asinh32(89.123), 5.183196, epsilon)); - assert(math.approxEq(f32, asinh32(123123.234375), 12.414088, epsilon)); + expect(math.approxEq(f32, asinh32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, asinh32(0.2), 0.198690, epsilon)); + expect(math.approxEq(f32, asinh32(0.8923), 0.803133, epsilon)); + expect(math.approxEq(f32, asinh32(1.5), 1.194763, epsilon)); + expect(math.approxEq(f32, asinh32(37.45), 4.316332, epsilon)); + expect(math.approxEq(f32, asinh32(89.123), 5.183196, epsilon)); + expect(math.approxEq(f32, asinh32(123123.234375), 12.414088, epsilon)); } test "math.asinh64" { const epsilon = 0.000001; - assert(math.approxEq(f64, asinh64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, asinh64(0.2), 0.198690, epsilon)); - assert(math.approxEq(f64, asinh64(0.8923), 0.803133, epsilon)); - assert(math.approxEq(f64, asinh64(1.5), 1.194763, epsilon)); - assert(math.approxEq(f64, asinh64(37.45), 4.316332, epsilon)); - assert(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon)); - assert(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon)); + expect(math.approxEq(f64, asinh64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, asinh64(0.2), 0.198690, epsilon)); + expect(math.approxEq(f64, asinh64(0.8923), 0.803133, epsilon)); + expect(math.approxEq(f64, asinh64(1.5), 1.194763, epsilon)); + expect(math.approxEq(f64, asinh64(37.45), 4.316332, epsilon)); + expect(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon)); + expect(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon)); } test "math.asinh32.special" { - assert(asinh32(0.0) == 0.0); - assert(asinh32(-0.0) == -0.0); - assert(math.isPositiveInf(asinh32(math.inf(f32)))); - assert(math.isNegativeInf(asinh32(-math.inf(f32)))); - assert(math.isNan(asinh32(math.nan(f32)))); + expect(asinh32(0.0) == 0.0); + expect(asinh32(-0.0) == -0.0); + expect(math.isPositiveInf(asinh32(math.inf(f32)))); + expect(math.isNegativeInf(asinh32(-math.inf(f32)))); + expect(math.isNan(asinh32(math.nan(f32)))); } test "math.asinh64.special" { - assert(asinh64(0.0) == 0.0); - assert(asinh64(-0.0) == -0.0); - assert(math.isPositiveInf(asinh64(math.inf(f64)))); - assert(math.isNegativeInf(asinh64(-math.inf(f64)))); - assert(math.isNan(asinh64(math.nan(f64)))); + expect(asinh64(0.0) == 0.0); + expect(asinh64(-0.0) == -0.0); + expect(math.isPositiveInf(asinh64(math.inf(f64)))); + expect(math.isNegativeInf(asinh64(-math.inf(f64)))); + expect(math.isNan(asinh64(math.nan(f64)))); } diff --git a/std/math/atan.zig b/std/math/atan.zig index 6ca94dd84a..72d17b4db2 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -5,7 +5,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn atan(x: var) @typeOf(x) { const T = @typeOf(x); @@ -206,44 +206,44 @@ fn atan64(x_: f64) f64 { } test "math.atan" { - assert(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2))); - assert(atan(f64(0.2)) == atan64(0.2)); + expect(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2))); + expect(atan(f64(0.2)) == atan64(0.2)); } test "math.atan32" { const epsilon = 0.000001; - assert(math.approxEq(f32, atan32(0.2), 0.197396, epsilon)); - assert(math.approxEq(f32, atan32(-0.2), -0.197396, epsilon)); - assert(math.approxEq(f32, atan32(0.3434), 0.330783, epsilon)); - assert(math.approxEq(f32, atan32(0.8923), 0.728545, epsilon)); - assert(math.approxEq(f32, atan32(1.5), 0.982794, epsilon)); + expect(math.approxEq(f32, atan32(0.2), 0.197396, epsilon)); + expect(math.approxEq(f32, atan32(-0.2), -0.197396, epsilon)); + expect(math.approxEq(f32, atan32(0.3434), 0.330783, epsilon)); + expect(math.approxEq(f32, atan32(0.8923), 0.728545, epsilon)); + expect(math.approxEq(f32, atan32(1.5), 0.982794, epsilon)); } test "math.atan64" { const epsilon = 0.000001; - assert(math.approxEq(f64, atan64(0.2), 0.197396, epsilon)); - assert(math.approxEq(f64, atan64(-0.2), -0.197396, epsilon)); - assert(math.approxEq(f64, atan64(0.3434), 0.330783, epsilon)); - assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon)); - assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon)); + expect(math.approxEq(f64, atan64(0.2), 0.197396, epsilon)); + expect(math.approxEq(f64, atan64(-0.2), -0.197396, epsilon)); + expect(math.approxEq(f64, atan64(0.3434), 0.330783, epsilon)); + expect(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon)); + expect(math.approxEq(f64, atan64(1.5), 0.982794, epsilon)); } test "math.atan32.special" { const epsilon = 0.000001; - assert(atan32(0.0) == 0.0); - assert(atan32(-0.0) == -0.0); - assert(math.approxEq(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon)); + expect(atan32(0.0) == 0.0); + expect(atan32(-0.0) == -0.0); + expect(math.approxEq(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon)); } test "math.atan64.special" { const epsilon = 0.000001; - assert(atan64(0.0) == 0.0); - assert(atan64(-0.0) == -0.0); - assert(math.approxEq(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon)); + expect(atan64(0.0) == 0.0); + expect(atan64(-0.0) == -0.0); + expect(math.approxEq(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon)); } diff --git a/std/math/atan2.zig b/std/math/atan2.zig index b3e45ba045..6e1f67cfbb 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -20,12 +20,12 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; -pub fn atan2(comptime T: type, x: T, y: T) T { +pub fn atan2(comptime T: type, y: T, x: T) T { return switch (T) { - f32 => atan2_32(x, y), - f64 => atan2_64(x, y), + f32 => atan2_32(y, x), + f64 => atan2_64(y, x), else => @compileError("atan2 not implemented for " ++ @typeName(T)), }; } @@ -206,78 +206,78 @@ fn atan2_64(y: f64, x: f64) f64 { } test "math.atan2" { - assert(atan2(f32, 0.2, 0.21) == atan2_32(0.2, 0.21)); - assert(atan2(f64, 0.2, 0.21) == atan2_64(0.2, 0.21)); + expect(atan2(f32, 0.2, 0.21) == atan2_32(0.2, 0.21)); + expect(atan2(f64, 0.2, 0.21) == atan2_64(0.2, 0.21)); } test "math.atan2_32" { const epsilon = 0.000001; - assert(math.approxEq(f32, atan2_32(0.0, 0.0), 0.0, epsilon)); - assert(math.approxEq(f32, atan2_32(0.2, 0.2), 0.785398, epsilon)); - assert(math.approxEq(f32, atan2_32(-0.2, 0.2), -0.785398, epsilon)); - assert(math.approxEq(f32, atan2_32(0.2, -0.2), 2.356194, epsilon)); - assert(math.approxEq(f32, atan2_32(-0.2, -0.2), -2.356194, epsilon)); - assert(math.approxEq(f32, atan2_32(0.34, -0.4), 2.437099, epsilon)); - assert(math.approxEq(f32, atan2_32(0.34, 1.243), 0.267001, epsilon)); + expect(math.approxEq(f32, atan2_32(0.0, 0.0), 0.0, epsilon)); + expect(math.approxEq(f32, atan2_32(0.2, 0.2), 0.785398, epsilon)); + expect(math.approxEq(f32, atan2_32(-0.2, 0.2), -0.785398, epsilon)); + expect(math.approxEq(f32, atan2_32(0.2, -0.2), 2.356194, epsilon)); + expect(math.approxEq(f32, atan2_32(-0.2, -0.2), -2.356194, epsilon)); + expect(math.approxEq(f32, atan2_32(0.34, -0.4), 2.437099, epsilon)); + expect(math.approxEq(f32, atan2_32(0.34, 1.243), 0.267001, epsilon)); } test "math.atan2_64" { const epsilon = 0.000001; - assert(math.approxEq(f64, atan2_64(0.0, 0.0), 0.0, epsilon)); - assert(math.approxEq(f64, atan2_64(0.2, 0.2), 0.785398, epsilon)); - assert(math.approxEq(f64, atan2_64(-0.2, 0.2), -0.785398, epsilon)); - assert(math.approxEq(f64, atan2_64(0.2, -0.2), 2.356194, epsilon)); - assert(math.approxEq(f64, atan2_64(-0.2, -0.2), -2.356194, epsilon)); - assert(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon)); - assert(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon)); + expect(math.approxEq(f64, atan2_64(0.0, 0.0), 0.0, epsilon)); + expect(math.approxEq(f64, atan2_64(0.2, 0.2), 0.785398, epsilon)); + expect(math.approxEq(f64, atan2_64(-0.2, 0.2), -0.785398, epsilon)); + expect(math.approxEq(f64, atan2_64(0.2, -0.2), 2.356194, epsilon)); + expect(math.approxEq(f64, atan2_64(-0.2, -0.2), -2.356194, epsilon)); + expect(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon)); + expect(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon)); } test "math.atan2_32.special" { const epsilon = 0.000001; - assert(math.isNan(atan2_32(1.0, math.nan(f32)))); - assert(math.isNan(atan2_32(math.nan(f32), 1.0))); - assert(atan2_32(0.0, 5.0) == 0.0); - assert(atan2_32(-0.0, 5.0) == -0.0); - assert(math.approxEq(f32, atan2_32(0.0, -5.0), math.pi, epsilon)); - //assert(math.approxEq(f32, atan2_32(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero? - assert(math.approxEq(f32, atan2_32(1.0, 0.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan2_32(1.0, -0.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan2_32(-1.0, 0.0), -math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan2_32(-1.0, -0.0), -math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi / 4.0, epsilon)); - assert(math.approxEq(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi / 4.0, epsilon)); - assert(math.approxEq(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi / 4.0, epsilon)); - assert(math.approxEq(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi / 4.0, epsilon)); - assert(atan2_32(1.0, math.inf(f32)) == 0.0); - assert(math.approxEq(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon)); - assert(math.approxEq(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon)); - assert(math.approxEq(f32, atan2_32(math.inf(f32), 1.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f32, atan2_32(-math.inf(f32), 1.0), -math.pi / 2.0, epsilon)); + expect(math.isNan(atan2_32(1.0, math.nan(f32)))); + expect(math.isNan(atan2_32(math.nan(f32), 1.0))); + expect(atan2_32(0.0, 5.0) == 0.0); + expect(atan2_32(-0.0, 5.0) == -0.0); + expect(math.approxEq(f32, atan2_32(0.0, -5.0), math.pi, epsilon)); + //expect(math.approxEq(f32, atan2_32(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero? + expect(math.approxEq(f32, atan2_32(1.0, 0.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan2_32(1.0, -0.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan2_32(-1.0, 0.0), -math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan2_32(-1.0, -0.0), -math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi / 4.0, epsilon)); + expect(math.approxEq(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi / 4.0, epsilon)); + expect(math.approxEq(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi / 4.0, epsilon)); + expect(math.approxEq(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi / 4.0, epsilon)); + expect(atan2_32(1.0, math.inf(f32)) == 0.0); + expect(math.approxEq(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon)); + expect(math.approxEq(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon)); + expect(math.approxEq(f32, atan2_32(math.inf(f32), 1.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f32, atan2_32(-math.inf(f32), 1.0), -math.pi / 2.0, epsilon)); } test "math.atan2_64.special" { const epsilon = 0.000001; - assert(math.isNan(atan2_64(1.0, math.nan(f64)))); - assert(math.isNan(atan2_64(math.nan(f64), 1.0))); - assert(atan2_64(0.0, 5.0) == 0.0); - assert(atan2_64(-0.0, 5.0) == -0.0); - assert(math.approxEq(f64, atan2_64(0.0, -5.0), math.pi, epsilon)); - //assert(math.approxEq(f64, atan2_64(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero? - assert(math.approxEq(f64, atan2_64(1.0, 0.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan2_64(1.0, -0.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan2_64(-1.0, 0.0), -math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan2_64(-1.0, -0.0), -math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi / 4.0, epsilon)); - assert(math.approxEq(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi / 4.0, epsilon)); - assert(math.approxEq(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi / 4.0, epsilon)); - assert(math.approxEq(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi / 4.0, epsilon)); - assert(atan2_64(1.0, math.inf(f64)) == 0.0); - assert(math.approxEq(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon)); - assert(math.approxEq(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon)); - assert(math.approxEq(f64, atan2_64(math.inf(f64), 1.0), math.pi / 2.0, epsilon)); - assert(math.approxEq(f64, atan2_64(-math.inf(f64), 1.0), -math.pi / 2.0, epsilon)); + expect(math.isNan(atan2_64(1.0, math.nan(f64)))); + expect(math.isNan(atan2_64(math.nan(f64), 1.0))); + expect(atan2_64(0.0, 5.0) == 0.0); + expect(atan2_64(-0.0, 5.0) == -0.0); + expect(math.approxEq(f64, atan2_64(0.0, -5.0), math.pi, epsilon)); + //expect(math.approxEq(f64, atan2_64(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero? + expect(math.approxEq(f64, atan2_64(1.0, 0.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan2_64(1.0, -0.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan2_64(-1.0, 0.0), -math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan2_64(-1.0, -0.0), -math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi / 4.0, epsilon)); + expect(math.approxEq(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi / 4.0, epsilon)); + expect(math.approxEq(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi / 4.0, epsilon)); + expect(math.approxEq(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi / 4.0, epsilon)); + expect(atan2_64(1.0, math.inf(f64)) == 0.0); + expect(math.approxEq(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon)); + expect(math.approxEq(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon)); + expect(math.approxEq(f64, atan2_64(math.inf(f64), 1.0), math.pi / 2.0, epsilon)); + expect(math.approxEq(f64, atan2_64(-math.inf(f64), 1.0), -math.pi / 2.0, epsilon)); } diff --git a/std/math/atanh.zig b/std/math/atanh.zig index c97855c234..f2feab0207 100644 --- a/std/math/atanh.zig +++ b/std/math/atanh.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn atanh(x: var) @typeOf(x) { @@ -78,38 +78,38 @@ fn atanh_64(x: f64) f64 { } test "math.atanh" { - assert(atanh(f32(0.0)) == atanh_32(0.0)); - assert(atanh(f64(0.0)) == atanh_64(0.0)); + expect(atanh(f32(0.0)) == atanh_32(0.0)); + expect(atanh(f64(0.0)) == atanh_64(0.0)); } test "math.atanh_32" { const epsilon = 0.000001; - assert(math.approxEq(f32, atanh_32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, atanh_32(0.2), 0.202733, epsilon)); - assert(math.approxEq(f32, atanh_32(0.8923), 1.433099, epsilon)); + expect(math.approxEq(f32, atanh_32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, atanh_32(0.2), 0.202733, epsilon)); + expect(math.approxEq(f32, atanh_32(0.8923), 1.433099, epsilon)); } test "math.atanh_64" { const epsilon = 0.000001; - assert(math.approxEq(f64, atanh_64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon)); - assert(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon)); + expect(math.approxEq(f64, atanh_64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon)); + expect(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon)); } test "math.atanh32.special" { - assert(math.isPositiveInf(atanh_32(1))); - assert(math.isNegativeInf(atanh_32(-1))); - assert(math.isSignalNan(atanh_32(1.5))); - assert(math.isSignalNan(atanh_32(-1.5))); - assert(math.isNan(atanh_32(math.nan(f32)))); + expect(math.isPositiveInf(atanh_32(1))); + expect(math.isNegativeInf(atanh_32(-1))); + expect(math.isSignalNan(atanh_32(1.5))); + expect(math.isSignalNan(atanh_32(-1.5))); + expect(math.isNan(atanh_32(math.nan(f32)))); } test "math.atanh64.special" { - assert(math.isPositiveInf(atanh_64(1))); - assert(math.isNegativeInf(atanh_64(-1))); - assert(math.isSignalNan(atanh_64(1.5))); - assert(math.isSignalNan(atanh_64(-1.5))); - assert(math.isNan(atanh_64(math.nan(f64)))); + expect(math.isPositiveInf(atanh_64(1))); + expect(math.isNegativeInf(atanh_64(-1))); + expect(math.isSignalNan(atanh_64(1.5))); + expect(math.isSignalNan(atanh_64(-1.5))); + expect(math.isNan(atanh_64(math.nan(f64)))); } diff --git a/std/math/big/int.zig b/std/math/big/int.zig index cda2e1419f..f21e5df8aa 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -1,6 +1,7 @@ const std = @import("../../index.zig"); const builtin = @import("builtin"); const debug = std.debug; +const testing = std.testing; const math = std.math; const mem = std.mem; const Allocator = mem.Allocator; @@ -1086,44 +1087,40 @@ test "big.int comptime_int set" { const result = Limb(s & maxInt(Limb)); s >>= Limb.bit_count / 2; s >>= Limb.bit_count / 2; - debug.assert(a.limbs[i] == result); + testing.expect(a.limbs[i] == result); } } test "big.int comptime_int set negative" { var a = try Int.initSet(al, -10); - debug.assert(a.limbs[0] == 10); - debug.assert(a.positive == false); + testing.expect(a.limbs[0] == 10); + testing.expect(a.positive == false); } test "big.int int set unaligned small" { var a = try Int.initSet(al, u7(45)); - debug.assert(a.limbs[0] == 45); - debug.assert(a.positive == true); + testing.expect(a.limbs[0] == 45); + testing.expect(a.positive == true); } test "big.int comptime_int to" { const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab); - debug.assert((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); + testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); } test "big.int sub-limb to" { const a = try Int.initSet(al, 10); - debug.assert((try a.to(u8)) == 10); + testing.expect((try a.to(u8)) == 10); } test "big.int to target too small error" { const a = try Int.initSet(al, 0xffffffff); - if (a.to(u8)) |_| { - unreachable; - } else |err| { - debug.assert(err == error.TargetTooSmall); - } + testing.expectError(error.TargetTooSmall, a.to(u8)); } test "big.int norm1" { @@ -1135,22 +1132,22 @@ test "big.int norm1" { a.limbs[2] = 3; a.limbs[3] = 0; a.norm1(4); - debug.assert(a.len == 3); + testing.expect(a.len == 3); a.limbs[0] = 1; a.limbs[1] = 2; a.limbs[2] = 3; a.norm1(3); - debug.assert(a.len == 3); + testing.expect(a.len == 3); a.limbs[0] = 0; a.limbs[1] = 0; a.norm1(2); - debug.assert(a.len == 1); + testing.expect(a.len == 1); a.limbs[0] = 0; a.norm1(1); - debug.assert(a.len == 1); + testing.expect(a.len == 1); } test "big.int normN" { @@ -1162,144 +1159,144 @@ test "big.int normN" { a.limbs[2] = 0; a.limbs[3] = 0; a.normN(4); - debug.assert(a.len == 2); + testing.expect(a.len == 2); a.limbs[0] = 1; a.limbs[1] = 2; a.limbs[2] = 3; a.normN(3); - debug.assert(a.len == 3); + testing.expect(a.len == 3); a.limbs[0] = 0; a.limbs[1] = 0; a.limbs[2] = 0; a.limbs[3] = 0; a.normN(4); - debug.assert(a.len == 1); + testing.expect(a.len == 1); a.limbs[0] = 0; a.normN(1); - debug.assert(a.len == 1); + testing.expect(a.len == 1); } test "big.int parity" { var a = try Int.init(al); try a.set(0); - debug.assert(a.isEven()); - debug.assert(!a.isOdd()); + testing.expect(a.isEven()); + testing.expect(!a.isOdd()); try a.set(7); - debug.assert(!a.isEven()); - debug.assert(a.isOdd()); + testing.expect(!a.isEven()); + testing.expect(a.isOdd()); } test "big.int bitcount + sizeInBase" { var a = try Int.init(al); try a.set(0b100); - debug.assert(a.bitCountAbs() == 3); - debug.assert(a.sizeInBase(2) >= 3); - debug.assert(a.sizeInBase(10) >= 1); + testing.expect(a.bitCountAbs() == 3); + testing.expect(a.sizeInBase(2) >= 3); + testing.expect(a.sizeInBase(10) >= 1); a.negate(); - debug.assert(a.bitCountAbs() == 3); - debug.assert(a.sizeInBase(2) >= 4); - debug.assert(a.sizeInBase(10) >= 2); + testing.expect(a.bitCountAbs() == 3); + testing.expect(a.sizeInBase(2) >= 4); + testing.expect(a.sizeInBase(10) >= 2); try a.set(0xffffffff); - debug.assert(a.bitCountAbs() == 32); - debug.assert(a.sizeInBase(2) >= 32); - debug.assert(a.sizeInBase(10) >= 10); + testing.expect(a.bitCountAbs() == 32); + testing.expect(a.sizeInBase(2) >= 32); + testing.expect(a.sizeInBase(10) >= 10); try a.shiftLeft(a, 5000); - debug.assert(a.bitCountAbs() == 5032); - debug.assert(a.sizeInBase(2) >= 5032); + testing.expect(a.bitCountAbs() == 5032); + testing.expect(a.sizeInBase(2) >= 5032); a.positive = false; - debug.assert(a.bitCountAbs() == 5032); - debug.assert(a.sizeInBase(2) >= 5033); + testing.expect(a.bitCountAbs() == 5032); + testing.expect(a.sizeInBase(2) >= 5033); } test "big.int bitcount/to" { var a = try Int.init(al); try a.set(0); - debug.assert(a.bitCountTwosComp() == 0); + testing.expect(a.bitCountTwosComp() == 0); // TODO: stack smashing - // debug.assert((try a.to(u0)) == 0); + // testing.expect((try a.to(u0)) == 0); // TODO: sigsegv - // debug.assert((try a.to(i0)) == 0); + // testing.expect((try a.to(i0)) == 0); try a.set(-1); - debug.assert(a.bitCountTwosComp() == 1); - debug.assert((try a.to(i1)) == -1); + testing.expect(a.bitCountTwosComp() == 1); + testing.expect((try a.to(i1)) == -1); try a.set(-8); - debug.assert(a.bitCountTwosComp() == 4); - debug.assert((try a.to(i4)) == -8); + testing.expect(a.bitCountTwosComp() == 4); + testing.expect((try a.to(i4)) == -8); try a.set(127); - debug.assert(a.bitCountTwosComp() == 7); - debug.assert((try a.to(u7)) == 127); + testing.expect(a.bitCountTwosComp() == 7); + testing.expect((try a.to(u7)) == 127); try a.set(-128); - debug.assert(a.bitCountTwosComp() == 8); - debug.assert((try a.to(i8)) == -128); + testing.expect(a.bitCountTwosComp() == 8); + testing.expect((try a.to(i8)) == -128); try a.set(-129); - debug.assert(a.bitCountTwosComp() == 9); - debug.assert((try a.to(i9)) == -129); + testing.expect(a.bitCountTwosComp() == 9); + testing.expect((try a.to(i9)) == -129); } test "big.int fits" { var a = try Int.init(al); try a.set(0); - debug.assert(a.fits(u0)); - debug.assert(a.fits(i0)); + testing.expect(a.fits(u0)); + testing.expect(a.fits(i0)); try a.set(255); - debug.assert(!a.fits(u0)); - debug.assert(!a.fits(u1)); - debug.assert(!a.fits(i8)); - debug.assert(a.fits(u8)); - debug.assert(a.fits(u9)); - debug.assert(a.fits(i9)); + testing.expect(!a.fits(u0)); + testing.expect(!a.fits(u1)); + testing.expect(!a.fits(i8)); + testing.expect(a.fits(u8)); + testing.expect(a.fits(u9)); + testing.expect(a.fits(i9)); try a.set(-128); - debug.assert(!a.fits(i7)); - debug.assert(a.fits(i8)); - debug.assert(a.fits(i9)); - debug.assert(!a.fits(u9)); + testing.expect(!a.fits(i7)); + testing.expect(a.fits(i8)); + testing.expect(a.fits(i9)); + testing.expect(!a.fits(u9)); try a.set(0x1ffffffffeeeeeeee); - debug.assert(!a.fits(u32)); - debug.assert(!a.fits(u64)); - debug.assert(a.fits(u65)); + testing.expect(!a.fits(u32)); + testing.expect(!a.fits(u64)); + testing.expect(a.fits(u65)); } test "big.int string set" { var a = try Int.init(al); try a.setString(10, "120317241209124781241290847124"); - debug.assert((try a.to(u128)) == 120317241209124781241290847124); + testing.expect((try a.to(u128)) == 120317241209124781241290847124); } test "big.int string negative" { var a = try Int.init(al); try a.setString(10, "-1023"); - debug.assert((try a.to(i32)) == -1023); + testing.expect((try a.to(i32)) == -1023); } test "big.int string set bad char error" { var a = try Int.init(al); - a.setString(10, "x") catch |err| debug.assert(err == error.InvalidCharForDigit); + testing.expectError(error.InvalidCharForDigit, a.setString(10, "x")); } test "big.int string set bad base error" { var a = try Int.init(al); - a.setString(45, "10") catch |err| debug.assert(err == error.InvalidBase); + testing.expectError(error.InvalidBase, a.setString(45, "10")); } test "big.int string to" { @@ -1308,17 +1305,13 @@ test "big.int string to" { const as = try a.toString(al, 10); const es = "120317241209124781241290847124"; - debug.assert(mem.eql(u8, as, es)); + testing.expect(mem.eql(u8, as, es)); } test "big.int string to base base error" { const a = try Int.initSet(al, 0xffffffff); - if (a.toString(al, 45)) |_| { - unreachable; - } else |err| { - debug.assert(err == error.InvalidBase); - } + testing.expectError(error.InvalidBase, a.toString(al, 45)); } test "big.int string to base 2" { @@ -1327,7 +1320,7 @@ test "big.int string to base 2" { const as = try a.toString(al, 2); const es = "-1011"; - debug.assert(mem.eql(u8, as, es)); + testing.expect(mem.eql(u8, as, es)); } test "big.int string to base 16" { @@ -1336,7 +1329,7 @@ test "big.int string to base 16" { const as = try a.toString(al, 16); const es = "efffffff00000001eeeeeeefaaaaaaab"; - debug.assert(mem.eql(u8, as, es)); + testing.expect(mem.eql(u8, as, es)); } test "big.int neg string to" { @@ -1345,7 +1338,7 @@ test "big.int neg string to" { const as = try a.toString(al, 10); const es = "-123907434"; - debug.assert(mem.eql(u8, as, es)); + testing.expect(mem.eql(u8, as, es)); } test "big.int zero string to" { @@ -1354,98 +1347,98 @@ test "big.int zero string to" { const as = try a.toString(al, 10); const es = "0"; - debug.assert(mem.eql(u8, as, es)); + testing.expect(mem.eql(u8, as, es)); } test "big.int clone" { var a = try Int.initSet(al, 1234); const b = try a.clone(); - debug.assert((try a.to(u32)) == 1234); - debug.assert((try b.to(u32)) == 1234); + testing.expect((try a.to(u32)) == 1234); + testing.expect((try b.to(u32)) == 1234); try a.set(77); - debug.assert((try a.to(u32)) == 77); - debug.assert((try b.to(u32)) == 1234); + testing.expect((try a.to(u32)) == 77); + testing.expect((try b.to(u32)) == 1234); } test "big.int swap" { var a = try Int.initSet(al, 1234); var b = try Int.initSet(al, 5678); - debug.assert((try a.to(u32)) == 1234); - debug.assert((try b.to(u32)) == 5678); + testing.expect((try a.to(u32)) == 1234); + testing.expect((try b.to(u32)) == 5678); a.swap(&b); - debug.assert((try a.to(u32)) == 5678); - debug.assert((try b.to(u32)) == 1234); + testing.expect((try a.to(u32)) == 5678); + testing.expect((try b.to(u32)) == 1234); } test "big.int to negative" { var a = try Int.initSet(al, -10); - debug.assert((try a.to(i32)) == -10); + testing.expect((try a.to(i32)) == -10); } test "big.int compare" { var a = try Int.initSet(al, -11); var b = try Int.initSet(al, 10); - debug.assert(a.cmpAbs(b) == 1); - debug.assert(a.cmp(b) == -1); + testing.expect(a.cmpAbs(b) == 1); + testing.expect(a.cmp(b) == -1); } test "big.int compare similar" { var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee); var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef); - debug.assert(a.cmpAbs(b) == -1); - debug.assert(b.cmpAbs(a) == 1); + testing.expect(a.cmpAbs(b) == -1); + testing.expect(b.cmpAbs(a) == 1); } test "big.int compare different limb size" { var a = try Int.initSet(al, maxInt(Limb) + 1); var b = try Int.initSet(al, 1); - debug.assert(a.cmpAbs(b) == 1); - debug.assert(b.cmpAbs(a) == -1); + testing.expect(a.cmpAbs(b) == 1); + testing.expect(b.cmpAbs(a) == -1); } test "big.int compare multi-limb" { var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); - debug.assert(a.cmpAbs(b) == 1); - debug.assert(a.cmp(b) == -1); + testing.expect(a.cmpAbs(b) == 1); + testing.expect(a.cmp(b) == -1); } test "big.int equality" { var a = try Int.initSet(al, 0xffffffff1); var b = try Int.initSet(al, -0xffffffff1); - debug.assert(a.eqAbs(b)); - debug.assert(!a.eq(b)); + testing.expect(a.eqAbs(b)); + testing.expect(!a.eq(b)); } test "big.int abs" { var a = try Int.initSet(al, -5); a.abs(); - debug.assert((try a.to(u32)) == 5); + testing.expect((try a.to(u32)) == 5); a.abs(); - debug.assert((try a.to(u32)) == 5); + testing.expect((try a.to(u32)) == 5); } test "big.int negate" { var a = try Int.initSet(al, 5); a.negate(); - debug.assert((try a.to(i32)) == -5); + testing.expect((try a.to(i32)) == -5); a.negate(); - debug.assert((try a.to(i32)) == 5); + testing.expect((try a.to(i32)) == 5); } test "big.int add single-single" { @@ -1455,7 +1448,7 @@ test "big.int add single-single" { var c = try Int.init(al); try c.add(a, b); - debug.assert((try c.to(u32)) == 55); + testing.expect((try c.to(u32)) == 55); } test "big.int add multi-single" { @@ -1465,10 +1458,10 @@ test "big.int add multi-single" { var c = try Int.init(al); try c.add(a, b); - debug.assert((try c.to(DoubleLimb)) == maxInt(Limb) + 2); + testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); try c.add(b, a); - debug.assert((try c.to(DoubleLimb)) == maxInt(Limb) + 2); + testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); } test "big.int add multi-multi" { @@ -1480,7 +1473,7 @@ test "big.int add multi-multi" { var c = try Int.init(al); try c.add(a, b); - debug.assert((try c.to(u128)) == op1 + op2); + testing.expect((try c.to(u128)) == op1 + op2); } test "big.int add zero-zero" { @@ -1490,7 +1483,7 @@ test "big.int add zero-zero" { var c = try Int.init(al); try c.add(a, b); - debug.assert((try c.to(u32)) == 0); + testing.expect((try c.to(u32)) == 0); } test "big.int add alias multi-limb nonzero-zero" { @@ -1500,7 +1493,7 @@ test "big.int add alias multi-limb nonzero-zero" { try a.add(a, b); - debug.assert((try a.to(u128)) == op1); + testing.expect((try a.to(u128)) == op1); } test "big.int add sign" { @@ -1512,16 +1505,16 @@ test "big.int add sign" { const neg_two = try Int.initSet(al, -2); try a.add(one, two); - debug.assert((try a.to(i32)) == 3); + testing.expect((try a.to(i32)) == 3); try a.add(neg_one, two); - debug.assert((try a.to(i32)) == 1); + testing.expect((try a.to(i32)) == 1); try a.add(one, neg_two); - debug.assert((try a.to(i32)) == -1); + testing.expect((try a.to(i32)) == -1); try a.add(neg_one, neg_two); - debug.assert((try a.to(i32)) == -3); + testing.expect((try a.to(i32)) == -3); } test "big.int sub single-single" { @@ -1531,7 +1524,7 @@ test "big.int sub single-single" { var c = try Int.init(al); try c.sub(a, b); - debug.assert((try c.to(u32)) == 45); + testing.expect((try c.to(u32)) == 45); } test "big.int sub multi-single" { @@ -1541,7 +1534,7 @@ test "big.int sub multi-single" { var c = try Int.init(al); try c.sub(a, b); - debug.assert((try c.to(Limb)) == maxInt(Limb)); + testing.expect((try c.to(Limb)) == maxInt(Limb)); } test "big.int sub multi-multi" { @@ -1554,7 +1547,7 @@ test "big.int sub multi-multi" { var c = try Int.init(al); try c.sub(a, b); - debug.assert((try c.to(u128)) == op1 - op2); + testing.expect((try c.to(u128)) == op1 - op2); } test "big.int sub equal" { @@ -1564,7 +1557,7 @@ test "big.int sub equal" { var c = try Int.init(al); try c.sub(a, b); - debug.assert((try c.to(u32)) == 0); + testing.expect((try c.to(u32)) == 0); } test "big.int sub sign" { @@ -1576,19 +1569,19 @@ test "big.int sub sign" { const neg_two = try Int.initSet(al, -2); try a.sub(one, two); - debug.assert((try a.to(i32)) == -1); + testing.expect((try a.to(i32)) == -1); try a.sub(neg_one, two); - debug.assert((try a.to(i32)) == -3); + testing.expect((try a.to(i32)) == -3); try a.sub(one, neg_two); - debug.assert((try a.to(i32)) == 3); + testing.expect((try a.to(i32)) == 3); try a.sub(neg_one, neg_two); - debug.assert((try a.to(i32)) == 1); + testing.expect((try a.to(i32)) == 1); try a.sub(neg_two, neg_one); - debug.assert((try a.to(i32)) == -1); + testing.expect((try a.to(i32)) == -1); } test "big.int mul single-single" { @@ -1598,7 +1591,7 @@ test "big.int mul single-single" { var c = try Int.init(al); try c.mul(a, b); - debug.assert((try c.to(u64)) == 250); + testing.expect((try c.to(u64)) == 250); } test "big.int mul multi-single" { @@ -1608,7 +1601,7 @@ test "big.int mul multi-single" { var c = try Int.init(al); try c.mul(a, b); - debug.assert((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); + testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); } test "big.int mul multi-multi" { @@ -1620,7 +1613,7 @@ test "big.int mul multi-multi" { var c = try Int.init(al); try c.mul(a, b); - debug.assert((try c.to(u256)) == op1 * op2); + testing.expect((try c.to(u256)) == op1 * op2); } test "big.int mul alias r with a" { @@ -1629,7 +1622,7 @@ test "big.int mul alias r with a" { try a.mul(a, b); - debug.assert((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); + testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); } test "big.int mul alias r with b" { @@ -1638,7 +1631,7 @@ test "big.int mul alias r with b" { try a.mul(b, a); - debug.assert((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); + testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); } test "big.int mul alias r with a and b" { @@ -1646,7 +1639,7 @@ test "big.int mul alias r with a and b" { try a.mul(a, a); - debug.assert((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); + testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); } test "big.int mul a*0" { @@ -1656,7 +1649,7 @@ test "big.int mul a*0" { var c = try Int.init(al); try c.mul(a, b); - debug.assert((try c.to(u32)) == 0); + testing.expect((try c.to(u32)) == 0); } test "big.int mul 0*0" { @@ -1666,7 +1659,7 @@ test "big.int mul 0*0" { var c = try Int.init(al); try c.mul(a, b); - debug.assert((try c.to(u32)) == 0); + testing.expect((try c.to(u32)) == 0); } test "big.int div single-single no rem" { @@ -1677,8 +1670,8 @@ test "big.int div single-single no rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u32)) == 10); - debug.assert((try r.to(u32)) == 0); + testing.expect((try q.to(u32)) == 10); + testing.expect((try r.to(u32)) == 0); } test "big.int div single-single with rem" { @@ -1689,8 +1682,8 @@ test "big.int div single-single with rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u32)) == 9); - debug.assert((try r.to(u32)) == 4); + testing.expect((try q.to(u32)) == 9); + testing.expect((try r.to(u32)) == 4); } test "big.int div multi-single no rem" { @@ -1704,8 +1697,8 @@ test "big.int div multi-single no rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u64)) == op1 / op2); - debug.assert((try r.to(u64)) == 0); + testing.expect((try q.to(u64)) == op1 / op2); + testing.expect((try r.to(u64)) == 0); } test "big.int div multi-single with rem" { @@ -1719,8 +1712,8 @@ test "big.int div multi-single with rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u64)) == op1 / op2); - debug.assert((try r.to(u64)) == 3); + testing.expect((try q.to(u64)) == op1 / op2); + testing.expect((try r.to(u64)) == 3); } test "big.int div multi>2-single" { @@ -1734,8 +1727,8 @@ test "big.int div multi>2-single" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == op1 / op2); - debug.assert((try r.to(u32)) == 0x3e4e); + testing.expect((try q.to(u128)) == op1 / op2); + testing.expect((try r.to(u32)) == 0x3e4e); } test "big.int div single-single q < r" { @@ -1746,8 +1739,8 @@ test "big.int div single-single q < r" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u64)) == 0); - debug.assert((try r.to(u64)) == 0x0078f432); + testing.expect((try q.to(u64)) == 0); + testing.expect((try r.to(u64)) == 0x0078f432); } test "big.int div single-single q == r" { @@ -1758,8 +1751,8 @@ test "big.int div single-single q == r" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u64)) == 1); - debug.assert((try r.to(u64)) == 0); + testing.expect((try q.to(u64)) == 1); + testing.expect((try r.to(u64)) == 0); } test "big.int div q=0 alias" { @@ -1768,8 +1761,8 @@ test "big.int div q=0 alias" { try Int.divTrunc(&a, &b, a, b); - debug.assert((try a.to(u64)) == 0); - debug.assert((try b.to(u64)) == 3); + testing.expect((try a.to(u64)) == 0); + testing.expect((try b.to(u64)) == 3); } test "big.int div multi-multi q < r" { @@ -1782,8 +1775,8 @@ test "big.int div multi-multi q < r" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == 0); - debug.assert((try r.to(u128)) == op1); + testing.expect((try q.to(u128)) == 0); + testing.expect((try r.to(u128)) == op1); } test "big.int div trunc single-single +/+" { @@ -1802,8 +1795,8 @@ test "big.int div trunc single-single +/+" { const eq = @divTrunc(u, v); const er = @mod(u, v); - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div trunc single-single -/+" { @@ -1822,8 +1815,8 @@ test "big.int div trunc single-single -/+" { const eq = -1; const er = -2; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div trunc single-single +/-" { @@ -1842,8 +1835,8 @@ test "big.int div trunc single-single +/-" { const eq = -1; const er = 2; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div trunc single-single -/-" { @@ -1862,8 +1855,8 @@ test "big.int div trunc single-single -/-" { const eq = 1; const er = -2; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div floor single-single +/+" { @@ -1882,8 +1875,8 @@ test "big.int div floor single-single +/+" { const eq = 1; const er = 2; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div floor single-single -/+" { @@ -1902,8 +1895,8 @@ test "big.int div floor single-single -/+" { const eq = -2; const er = 1; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div floor single-single +/-" { @@ -1922,8 +1915,8 @@ test "big.int div floor single-single +/-" { const eq = -2; const er = -1; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div floor single-single -/-" { @@ -1942,8 +1935,8 @@ test "big.int div floor single-single -/-" { const eq = 1; const er = -2; - debug.assert((try q.to(i32)) == eq); - debug.assert((try r.to(i32)) == er); + testing.expect((try q.to(i32)) == eq); + testing.expect((try r.to(i32)) == er); } test "big.int div multi-multi with rem" { @@ -1954,8 +1947,8 @@ test "big.int div multi-multi with rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); - debug.assert((try r.to(u128)) == 0x28de0acacd806823638); + testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); + testing.expect((try r.to(u128)) == 0x28de0acacd806823638); } test "big.int div multi-multi no rem" { @@ -1966,8 +1959,8 @@ test "big.int div multi-multi no rem" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); - debug.assert((try r.to(u128)) == 0); + testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); + testing.expect((try r.to(u128)) == 0); } test "big.int div multi-multi (2 branch)" { @@ -1978,8 +1971,8 @@ test "big.int div multi-multi (2 branch)" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == 0x10000000000000000); - debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111); + testing.expect((try q.to(u128)) == 0x10000000000000000); + testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); } test "big.int div multi-multi (3.1/3.3 branch)" { @@ -1990,53 +1983,53 @@ test "big.int div multi-multi (3.1/3.3 branch)" { var r = try Int.init(al); try Int.divTrunc(&q, &r, a, b); - debug.assert((try q.to(u128)) == 0xfffffffffffffffffff); - debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); + testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); + testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); } test "big.int shift-right single" { var a = try Int.initSet(al, 0xffff0000); try a.shiftRight(a, 16); - debug.assert((try a.to(u32)) == 0xffff); + testing.expect((try a.to(u32)) == 0xffff); } test "big.int shift-right multi" { var a = try Int.initSet(al, 0xffff0000eeee1111dddd2222cccc3333); try a.shiftRight(a, 67); - debug.assert((try a.to(u64)) == 0x1fffe0001dddc222); + testing.expect((try a.to(u64)) == 0x1fffe0001dddc222); } test "big.int shift-left single" { var a = try Int.initSet(al, 0xffff); try a.shiftLeft(a, 16); - debug.assert((try a.to(u64)) == 0xffff0000); + testing.expect((try a.to(u64)) == 0xffff0000); } test "big.int shift-left multi" { var a = try Int.initSet(al, 0x1fffe0001dddc222); try a.shiftLeft(a, 67); - debug.assert((try a.to(u128)) == 0xffff0000eeee11100000000000000000); + testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000); } test "big.int shift-right negative" { var a = try Int.init(al); try a.shiftRight(try Int.initSet(al, -20), 2); - debug.assert((try a.to(i32)) == -20 >> 2); + testing.expect((try a.to(i32)) == -20 >> 2); try a.shiftRight(try Int.initSet(al, -5), 10); - debug.assert((try a.to(i32)) == -5 >> 10); + testing.expect((try a.to(i32)) == -5 >> 10); } test "big.int shift-left negative" { var a = try Int.init(al); try a.shiftRight(try Int.initSet(al, -10), 1232); - debug.assert((try a.to(i32)) == -10 >> 1232); + testing.expect((try a.to(i32)) == -10 >> 1232); } test "big.int bitwise and simple" { @@ -2045,7 +2038,7 @@ test "big.int bitwise and simple" { try a.bitAnd(a, b); - debug.assert((try a.to(u64)) == 0xeeeeeeee00000000); + testing.expect((try a.to(u64)) == 0xeeeeeeee00000000); } test "big.int bitwise and multi-limb" { @@ -2054,7 +2047,7 @@ test "big.int bitwise and multi-limb" { try a.bitAnd(a, b); - debug.assert((try a.to(u128)) == 0); + testing.expect((try a.to(u128)) == 0); } test "big.int bitwise xor simple" { @@ -2063,7 +2056,7 @@ test "big.int bitwise xor simple" { try a.bitXor(a, b); - debug.assert((try a.to(u64)) == 0x1111111133333333); + testing.expect((try a.to(u64)) == 0x1111111133333333); } test "big.int bitwise xor multi-limb" { @@ -2072,7 +2065,7 @@ test "big.int bitwise xor multi-limb" { try a.bitXor(a, b); - debug.assert((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); + testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); } test "big.int bitwise or simple" { @@ -2081,7 +2074,7 @@ test "big.int bitwise or simple" { try a.bitOr(a, b); - debug.assert((try a.to(u64)) == 0xffffffff33333333); + testing.expect((try a.to(u64)) == 0xffffffff33333333); } test "big.int bitwise or multi-limb" { @@ -2091,15 +2084,15 @@ test "big.int bitwise or multi-limb" { try a.bitOr(a, b); // TODO: big.int.cpp or is wrong on multi-limb. - debug.assert((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); + testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); } test "big.int var args" { var a = try Int.initSet(al, 5); try a.add(a, try Int.initSet(al, 6)); - debug.assert((try a.to(u64)) == 11); + testing.expect((try a.to(u64)) == 11); - debug.assert(a.cmp(try Int.initSet(al, 11)) == 0); - debug.assert(a.cmp(try Int.initSet(al, 14)) <= 0); + testing.expect(a.cmp(try Int.initSet(al, 11)) == 0); + testing.expect(a.cmp(try Int.initSet(al, 14)) <= 0); } diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index c067c5155a..957e026af4 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn cbrt(x: var) @typeOf(x) { const T = @typeOf(x); @@ -114,44 +114,44 @@ fn cbrt64(x: f64) f64 { } test "math.cbrt" { - assert(cbrt(f32(0.0)) == cbrt32(0.0)); - assert(cbrt(f64(0.0)) == cbrt64(0.0)); + expect(cbrt(f32(0.0)) == cbrt32(0.0)); + expect(cbrt(f64(0.0)) == cbrt64(0.0)); } test "math.cbrt32" { const epsilon = 0.000001; - assert(cbrt32(0.0) == 0.0); - assert(math.approxEq(f32, cbrt32(0.2), 0.584804, epsilon)); - assert(math.approxEq(f32, cbrt32(0.8923), 0.962728, epsilon)); - assert(math.approxEq(f32, cbrt32(1.5), 1.144714, epsilon)); - assert(math.approxEq(f32, cbrt32(37.45), 3.345676, epsilon)); - assert(math.approxEq(f32, cbrt32(123123.234375), 49.748501, epsilon)); + expect(cbrt32(0.0) == 0.0); + expect(math.approxEq(f32, cbrt32(0.2), 0.584804, epsilon)); + expect(math.approxEq(f32, cbrt32(0.8923), 0.962728, epsilon)); + expect(math.approxEq(f32, cbrt32(1.5), 1.144714, epsilon)); + expect(math.approxEq(f32, cbrt32(37.45), 3.345676, epsilon)); + expect(math.approxEq(f32, cbrt32(123123.234375), 49.748501, epsilon)); } test "math.cbrt64" { const epsilon = 0.000001; - assert(cbrt64(0.0) == 0.0); - assert(math.approxEq(f64, cbrt64(0.2), 0.584804, epsilon)); - assert(math.approxEq(f64, cbrt64(0.8923), 0.962728, epsilon)); - assert(math.approxEq(f64, cbrt64(1.5), 1.144714, epsilon)); - assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon)); - assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon)); + expect(cbrt64(0.0) == 0.0); + expect(math.approxEq(f64, cbrt64(0.2), 0.584804, epsilon)); + expect(math.approxEq(f64, cbrt64(0.8923), 0.962728, epsilon)); + expect(math.approxEq(f64, cbrt64(1.5), 1.144714, epsilon)); + expect(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon)); + expect(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon)); } test "math.cbrt.special" { - assert(cbrt32(0.0) == 0.0); - assert(cbrt32(-0.0) == -0.0); - assert(math.isPositiveInf(cbrt32(math.inf(f32)))); - assert(math.isNegativeInf(cbrt32(-math.inf(f32)))); - assert(math.isNan(cbrt32(math.nan(f32)))); + expect(cbrt32(0.0) == 0.0); + expect(cbrt32(-0.0) == -0.0); + expect(math.isPositiveInf(cbrt32(math.inf(f32)))); + expect(math.isNegativeInf(cbrt32(-math.inf(f32)))); + expect(math.isNan(cbrt32(math.nan(f32)))); } test "math.cbrt64.special" { - assert(cbrt64(0.0) == 0.0); - assert(cbrt64(-0.0) == -0.0); - assert(math.isPositiveInf(cbrt64(math.inf(f64)))); - assert(math.isNegativeInf(cbrt64(-math.inf(f64)))); - assert(math.isNan(cbrt64(math.nan(f64)))); + expect(cbrt64(0.0) == 0.0); + expect(cbrt64(-0.0) == -0.0); + expect(math.isPositiveInf(cbrt64(math.inf(f64)))); + expect(math.isNegativeInf(cbrt64(-math.inf(f64)))); + expect(math.isNan(cbrt64(math.nan(f64)))); } diff --git a/std/math/ceil.zig b/std/math/ceil.zig index 8a5221d862..5c6b98b2ca 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn ceil(x: var) @typeOf(x) { const T = @typeOf(x); @@ -81,34 +81,34 @@ fn ceil64(x: f64) f64 { } test "math.ceil" { - assert(ceil(f32(0.0)) == ceil32(0.0)); - assert(ceil(f64(0.0)) == ceil64(0.0)); + expect(ceil(f32(0.0)) == ceil32(0.0)); + expect(ceil(f64(0.0)) == ceil64(0.0)); } test "math.ceil32" { - assert(ceil32(1.3) == 2.0); - assert(ceil32(-1.3) == -1.0); - assert(ceil32(0.2) == 1.0); + expect(ceil32(1.3) == 2.0); + expect(ceil32(-1.3) == -1.0); + expect(ceil32(0.2) == 1.0); } test "math.ceil64" { - assert(ceil64(1.3) == 2.0); - assert(ceil64(-1.3) == -1.0); - assert(ceil64(0.2) == 1.0); + expect(ceil64(1.3) == 2.0); + expect(ceil64(-1.3) == -1.0); + expect(ceil64(0.2) == 1.0); } test "math.ceil32.special" { - assert(ceil32(0.0) == 0.0); - assert(ceil32(-0.0) == -0.0); - assert(math.isPositiveInf(ceil32(math.inf(f32)))); - assert(math.isNegativeInf(ceil32(-math.inf(f32)))); - assert(math.isNan(ceil32(math.nan(f32)))); + expect(ceil32(0.0) == 0.0); + expect(ceil32(-0.0) == -0.0); + expect(math.isPositiveInf(ceil32(math.inf(f32)))); + expect(math.isNegativeInf(ceil32(-math.inf(f32)))); + expect(math.isNan(ceil32(math.nan(f32)))); } test "math.ceil64.special" { - assert(ceil64(0.0) == 0.0); - assert(ceil64(-0.0) == -0.0); - assert(math.isPositiveInf(ceil64(math.inf(f64)))); - assert(math.isNegativeInf(ceil64(-math.inf(f64)))); - assert(math.isNan(ceil64(math.nan(f64)))); + expect(ceil64(0.0) == 0.0); + expect(ceil64(-0.0) == -0.0); + expect(math.isPositiveInf(ceil64(math.inf(f64)))); + expect(math.isNegativeInf(ceil64(-math.inf(f64)))); + expect(math.isNan(ceil64(math.nan(f64)))); } diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig index 4cd095c46b..245d67d4c5 100644 --- a/std/math/complex/abs.zig +++ b/std/math/complex/abs.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -14,5 +14,5 @@ const epsilon = 0.0001; test "complex.cabs" { const a = Complex(f32).new(5, 3); const c = abs(a); - debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); + testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); } diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig index a5760b4ace..1b314bc31a 100644 --- a/std/math/complex/acos.zig +++ b/std/math/complex/acos.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -16,6 +16,6 @@ test "complex.cacos" { const a = Complex(f32).new(5, 3); const c = acos(a); - debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon)); - debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon)); + testing.expect(math.approxEq(f32, c.re, 0.546975, epsilon)); + testing.expect(math.approxEq(f32, c.im, -2.452914, epsilon)); } diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig index 8dd91b2836..0e4c0121f4 100644 --- a/std/math/complex/acosh.zig +++ b/std/math/complex/acosh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -16,6 +16,6 @@ test "complex.cacosh" { const a = Complex(f32).new(5, 3); const c = acosh(a); - debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon)); - debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon)); + testing.expect(math.approxEq(f32, c.re, 2.452914, epsilon)); + testing.expect(math.approxEq(f32, c.im, 0.546975, epsilon)); } diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig index f24512ac73..be117a5940 100644 --- a/std/math/complex/arg.zig +++ b/std/math/complex/arg.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -14,5 +14,5 @@ const epsilon = 0.0001; test "complex.carg" { const a = Complex(f32).new(5, 3); const c = arg(a); - debug.assert(math.approxEq(f32, c, 0.540420, epsilon)); + testing.expect(math.approxEq(f32, c, 0.540420, epsilon)); } diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig index 584a3a1a9b..cf802ea206 100644 --- a/std/math/complex/asin.zig +++ b/std/math/complex/asin.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -22,6 +22,6 @@ test "complex.casin" { const a = Complex(f32).new(5, 3); const c = asin(a); - debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon)); - debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon)); + testing.expect(math.approxEq(f32, c.re, 1.023822, epsilon)); + testing.expect(math.approxEq(f32, c.im, 2.452914, epsilon)); } diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig index 0c4dc2b6e4..0386a636b0 100644 --- a/std/math/complex/asinh.zig +++ b/std/math/complex/asinh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -17,6 +17,6 @@ test "complex.casinh" { const a = Complex(f32).new(5, 3); const c = asinh(a); - debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon)); - debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon)); + testing.expect(math.approxEq(f32, c.re, 2.459831, epsilon)); + testing.expect(math.approxEq(f32, c.im, 0.533999, epsilon)); } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index de60f2546d..8e60e58e43 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -117,14 +117,14 @@ test "complex.catan32" { const a = Complex(f32).new(5, 3); const c = atan(a); - debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon)); - debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon)); + testing.expect(math.approxEq(f32, c.re, 1.423679, epsilon)); + testing.expect(math.approxEq(f32, c.im, 0.086569, epsilon)); } test "complex.catan64" { const a = Complex(f64).new(5, 3); const c = atan(a); - debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon)); - debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon)); + testing.expect(math.approxEq(f64, c.re, 1.423679, epsilon)); + testing.expect(math.approxEq(f64, c.im, 0.086569, epsilon)); } diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig index f70c741765..5b18fe1992 100644 --- a/std/math/complex/atanh.zig +++ b/std/math/complex/atanh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -17,6 +17,6 @@ test "complex.catanh" { const a = Complex(f32).new(5, 3); const c = atanh(a); - debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon)); - debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon)); + testing.expect(math.approxEq(f32, c.re, 0.146947, epsilon)); + testing.expect(math.approxEq(f32, c.im, 1.480870, epsilon)); } diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig index ad3e8b5036..143543f9e7 100644 --- a/std/math/complex/conj.zig +++ b/std/math/complex/conj.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -13,5 +13,5 @@ test "complex.conj" { const a = Complex(f32).new(5, 3); const c = a.conjugate(); - debug.assert(c.re == 5 and c.im == -3); + testing.expect(c.re == 5 and c.im == -3); } diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig index 96e4ffcdb0..658d19c3b6 100644 --- a/std/math/complex/cos.zig +++ b/std/math/complex/cos.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -16,6 +16,6 @@ test "complex.ccos" { const a = Complex(f32).new(5, 3); const c = cos(a); - debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon)); - debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon)); + testing.expect(math.approxEq(f32, c.re, 2.855815, epsilon)); + testing.expect(math.approxEq(f32, c.im, 9.606383, epsilon)); } diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 91875a0c47..5ce10b03f8 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -152,14 +152,14 @@ test "complex.ccosh32" { const a = Complex(f32).new(5, 3); const c = cosh(a); - debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon)); - debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon)); + testing.expect(math.approxEq(f32, c.re, -73.467300, epsilon)); + testing.expect(math.approxEq(f32, c.im, 10.471557, epsilon)); } test "complex.ccosh64" { const a = Complex(f64).new(5, 3); const c = cosh(a); - debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon)); - debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon)); + testing.expect(math.approxEq(f64, c.re, -73.467300, epsilon)); + testing.expect(math.approxEq(f64, c.im, 10.471557, epsilon)); } diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 0473f653b6..9ded698d08 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -118,14 +118,14 @@ test "complex.cexp32" { const a = Complex(f32).new(5, 3); const c = exp(a); - debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon)); - debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon)); + testing.expect(math.approxEq(f32, c.re, -146.927917, epsilon)); + testing.expect(math.approxEq(f32, c.im, 20.944065, epsilon)); } test "complex.cexp64" { const a = Complex(f64).new(5, 3); const c = exp(a); - debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon)); - debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon)); + testing.expect(math.approxEq(f64, c.re, -146.927917, epsilon)); + testing.expect(math.approxEq(f64, c.im, 20.944065, epsilon)); } diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index 171c4c5829..ffbf14d83e 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; pub const abs = @import("abs.zig").abs; @@ -97,7 +97,7 @@ test "complex.add" { const b = Complex(f32).new(2, 7); const c = a.add(b); - debug.assert(c.re == 7 and c.im == 10); + testing.expect(c.re == 7 and c.im == 10); } test "complex.sub" { @@ -105,7 +105,7 @@ test "complex.sub" { const b = Complex(f32).new(2, 7); const c = a.sub(b); - debug.assert(c.re == 3 and c.im == -4); + testing.expect(c.re == 3 and c.im == -4); } test "complex.mul" { @@ -113,7 +113,7 @@ test "complex.mul" { const b = Complex(f32).new(2, 7); const c = a.mul(b); - debug.assert(c.re == -11 and c.im == 41); + testing.expect(c.re == -11 and c.im == 41); } test "complex.div" { @@ -121,7 +121,7 @@ test "complex.div" { const b = Complex(f32).new(2, 7); const c = a.div(b); - debug.assert(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and + testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); } @@ -129,14 +129,14 @@ test "complex.conjugate" { const a = Complex(f32).new(5, 3); const c = a.conjugate(); - debug.assert(c.re == 5 and c.im == -3); + testing.expect(c.re == 5 and c.im == -3); } test "complex.reciprocal" { const a = Complex(f32).new(5, 3); const c = a.reciprocal(); - debug.assert(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and + testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); } @@ -144,7 +144,7 @@ test "complex.magnitude" { const a = Complex(f32).new(5, 3); const c = a.magnitude(); - debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); + testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); } test "complex.cmath" { diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig index a4a1d1664f..360bb7d21e 100644 --- a/std/math/complex/log.zig +++ b/std/math/complex/log.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -18,6 +18,6 @@ test "complex.clog" { const a = Complex(f32).new(5, 3); const c = log(a); - debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon)); - debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon)); + testing.expect(math.approxEq(f32, c.re, 1.763180, epsilon)); + testing.expect(math.approxEq(f32, c.im, 0.540419, epsilon)); } diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index edf68653b6..bd625626c8 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -17,6 +17,6 @@ test "complex.cpow" { const b = Complex(f32).new(2.3, -1.3); const c = pow(Complex(f32), a, b); - debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon)); - debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon)); + testing.expect(math.approxEq(f32, c.re, 58.049110, epsilon)); + testing.expect(math.approxEq(f32, c.im, -101.003433, epsilon)); } diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig index b6c4cc046e..d31006129e 100644 --- a/std/math/complex/proj.zig +++ b/std/math/complex/proj.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -20,5 +20,5 @@ test "complex.cproj" { const a = Complex(f32).new(5, 3); const c = proj(a); - debug.assert(c.re == 5 and c.im == 3); + testing.expect(c.re == 5 and c.im == 3); } diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig index d32b771d3b..9d54ab0969 100644 --- a/std/math/complex/sin.zig +++ b/std/math/complex/sin.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -17,6 +17,6 @@ test "complex.csin" { const a = Complex(f32).new(5, 3); const c = sin(a); - debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon)); - debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon)); + testing.expect(math.approxEq(f32, c.re, -9.654126, epsilon)); + testing.expect(math.approxEq(f32, c.im, 2.841692, epsilon)); } diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index dc19a0ba1b..469ea6067a 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -151,14 +151,14 @@ test "complex.csinh32" { const a = Complex(f32).new(5, 3); const c = sinh(a); - debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon)); - debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon)); + testing.expect(math.approxEq(f32, c.re, -73.460617, epsilon)); + testing.expect(math.approxEq(f32, c.im, 10.472508, epsilon)); } test "complex.csinh64" { const a = Complex(f64).new(5, 3); const c = sinh(a); - debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon)); - debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon)); + testing.expect(math.approxEq(f64, c.re, -73.460617, epsilon)); + testing.expect(math.approxEq(f64, c.im, 10.472508, epsilon)); } diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index 47367816f7..60f6061baa 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -125,14 +125,14 @@ test "complex.csqrt32" { const a = Complex(f32).new(5, 3); const c = sqrt(a); - debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon)); - debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon)); + testing.expect(math.approxEq(f32, c.re, 2.327117, epsilon)); + testing.expect(math.approxEq(f32, c.im, 0.644574, epsilon)); } test "complex.csqrt64" { const a = Complex(f64).new(5, 3); const c = sqrt(a); - debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon)); - debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon)); + testing.expect(math.approxEq(f64, c.re, 2.3271175190399496, epsilon)); + testing.expect(math.approxEq(f64, c.im, 0.6445742373246469, epsilon)); } diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig index 4ea5182fa7..db34580598 100644 --- a/std/math/complex/tan.zig +++ b/std/math/complex/tan.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -17,6 +17,6 @@ test "complex.ctan" { const a = Complex(f32).new(5, 3); const c = tan(a); - debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon)); - debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon)); + testing.expect(math.approxEq(f32, c.re, -0.002708233, epsilon)); + testing.expect(math.approxEq(f32, c.im, 1.004165, epsilon)); } diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index e48d438783..03ab431312 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -1,5 +1,5 @@ const std = @import("../../index.zig"); -const debug = std.debug; +const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; @@ -100,14 +100,14 @@ test "complex.ctanh32" { const a = Complex(f32).new(5, 3); const c = tanh(a); - debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon)); - debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon)); + testing.expect(math.approxEq(f32, c.re, 0.999913, epsilon)); + testing.expect(math.approxEq(f32, c.im, -0.000025, epsilon)); } test "complex.ctanh64" { const a = Complex(f64).new(5, 3); const c = tanh(a); - debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon)); - debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon)); + testing.expect(math.approxEq(f64, c.re, 0.999913, epsilon)); + testing.expect(math.approxEq(f64, c.im, -0.000025, epsilon)); } diff --git a/std/math/copysign.zig b/std/math/copysign.zig index 4c6e333d6c..dbf8f6e1ef 100644 --- a/std/math/copysign.zig +++ b/std/math/copysign.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn copysign(comptime T: type, x: T, y: T) T { @@ -40,28 +40,28 @@ fn copysign64(x: f64, y: f64) f64 { } test "math.copysign" { - assert(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0)); - assert(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); - assert(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); + expect(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0)); + expect(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); + expect(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); } test "math.copysign16" { - assert(copysign16(5.0, 1.0) == 5.0); - assert(copysign16(5.0, -1.0) == -5.0); - assert(copysign16(-5.0, -1.0) == -5.0); - assert(copysign16(-5.0, 1.0) == 5.0); + expect(copysign16(5.0, 1.0) == 5.0); + expect(copysign16(5.0, -1.0) == -5.0); + expect(copysign16(-5.0, -1.0) == -5.0); + expect(copysign16(-5.0, 1.0) == 5.0); } test "math.copysign32" { - assert(copysign32(5.0, 1.0) == 5.0); - assert(copysign32(5.0, -1.0) == -5.0); - assert(copysign32(-5.0, -1.0) == -5.0); - assert(copysign32(-5.0, 1.0) == 5.0); + expect(copysign32(5.0, 1.0) == 5.0); + expect(copysign32(5.0, -1.0) == -5.0); + expect(copysign32(-5.0, -1.0) == -5.0); + expect(copysign32(-5.0, 1.0) == 5.0); } test "math.copysign64" { - assert(copysign64(5.0, 1.0) == 5.0); - assert(copysign64(5.0, -1.0) == -5.0); - assert(copysign64(-5.0, -1.0) == -5.0); - assert(copysign64(-5.0, 1.0) == 5.0); + expect(copysign64(5.0, 1.0) == 5.0); + expect(copysign64(5.0, -1.0) == -5.0); + expect(copysign64(-5.0, -1.0) == -5.0); + expect(copysign64(-5.0, 1.0) == 5.0); } diff --git a/std/math/cos.zig b/std/math/cos.zig index b6a2fbffe6..7783ddc09b 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn cos(x: var) @typeOf(x) { const T = @typeOf(x); @@ -139,40 +139,40 @@ fn cos64(x_: f64) f64 { } test "math.cos" { - assert(cos(f32(0.0)) == cos32(0.0)); - assert(cos(f64(0.0)) == cos64(0.0)); + expect(cos(f32(0.0)) == cos32(0.0)); + expect(cos(f64(0.0)) == cos64(0.0)); } test "math.cos32" { const epsilon = 0.000001; - assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); - assert(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); - assert(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); - assert(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); - assert(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); - assert(math.approxEq(f32, cos32(89.123), 0.400798, epsilon)); + expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); + expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); + expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); + expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); + expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); + expect(math.approxEq(f32, cos32(89.123), 0.400798, epsilon)); } test "math.cos64" { const epsilon = 0.000001; - assert(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); - assert(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); - assert(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); - assert(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); - assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); - assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); + expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); + expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); + expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); + expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); + expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); + expect(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); } test "math.cos32.special" { - assert(math.isNan(cos32(math.inf(f32)))); - assert(math.isNan(cos32(-math.inf(f32)))); - assert(math.isNan(cos32(math.nan(f32)))); + expect(math.isNan(cos32(math.inf(f32)))); + expect(math.isNan(cos32(-math.inf(f32)))); + expect(math.isNan(cos32(math.nan(f32)))); } test "math.cos64.special" { - assert(math.isNan(cos64(math.inf(f64)))); - assert(math.isNan(cos64(-math.inf(f64)))); - assert(math.isNan(cos64(math.nan(f64)))); + expect(math.isNan(cos64(math.inf(f64)))); + expect(math.isNan(cos64(-math.inf(f64)))); + expect(math.isNan(cos64(math.nan(f64)))); } diff --git a/std/math/cosh.zig b/std/math/cosh.zig index 77e02855fd..57c5eef828 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -8,7 +8,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; const expo2 = @import("expo2.zig").expo2; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn cosh(x: var) @typeOf(x) { @@ -82,40 +82,40 @@ fn cosh64(x: f64) f64 { } test "math.cosh" { - assert(cosh(f32(1.5)) == cosh32(1.5)); - assert(cosh(f64(1.5)) == cosh64(1.5)); + expect(cosh(f32(1.5)) == cosh32(1.5)); + expect(cosh(f64(1.5)) == cosh64(1.5)); } test "math.cosh32" { const epsilon = 0.000001; - assert(math.approxEq(f32, cosh32(0.0), 1.0, epsilon)); - assert(math.approxEq(f32, cosh32(0.2), 1.020067, epsilon)); - assert(math.approxEq(f32, cosh32(0.8923), 1.425225, epsilon)); - assert(math.approxEq(f32, cosh32(1.5), 2.352410, epsilon)); + expect(math.approxEq(f32, cosh32(0.0), 1.0, epsilon)); + expect(math.approxEq(f32, cosh32(0.2), 1.020067, epsilon)); + expect(math.approxEq(f32, cosh32(0.8923), 1.425225, epsilon)); + expect(math.approxEq(f32, cosh32(1.5), 2.352410, epsilon)); } test "math.cosh64" { const epsilon = 0.000001; - assert(math.approxEq(f64, cosh64(0.0), 1.0, epsilon)); - assert(math.approxEq(f64, cosh64(0.2), 1.020067, epsilon)); - assert(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon)); - assert(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon)); + expect(math.approxEq(f64, cosh64(0.0), 1.0, epsilon)); + expect(math.approxEq(f64, cosh64(0.2), 1.020067, epsilon)); + expect(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon)); + expect(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon)); } test "math.cosh32.special" { - assert(cosh32(0.0) == 1.0); - assert(cosh32(-0.0) == 1.0); - assert(math.isPositiveInf(cosh32(math.inf(f32)))); - assert(math.isPositiveInf(cosh32(-math.inf(f32)))); - assert(math.isNan(cosh32(math.nan(f32)))); + expect(cosh32(0.0) == 1.0); + expect(cosh32(-0.0) == 1.0); + expect(math.isPositiveInf(cosh32(math.inf(f32)))); + expect(math.isPositiveInf(cosh32(-math.inf(f32)))); + expect(math.isNan(cosh32(math.nan(f32)))); } test "math.cosh64.special" { - assert(cosh64(0.0) == 1.0); - assert(cosh64(-0.0) == 1.0); - assert(math.isPositiveInf(cosh64(math.inf(f64)))); - assert(math.isPositiveInf(cosh64(-math.inf(f64)))); - assert(math.isNan(cosh64(math.nan(f64)))); + expect(cosh64(0.0) == 1.0); + expect(cosh64(-0.0) == 1.0); + expect(math.isPositiveInf(cosh64(math.inf(f64)))); + expect(math.isPositiveInf(cosh64(-math.inf(f64)))); + expect(math.isNan(cosh64(math.nan(f64)))); } diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 3d8e5d692e..ba0001a09a 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -5,7 +5,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn exp2(x: var) @typeOf(x) { const T = @typeOf(x); @@ -415,35 +415,35 @@ fn exp2_64(x: f64) f64 { } test "math.exp2" { - assert(exp2(f32(0.8923)) == exp2_32(0.8923)); - assert(exp2(f64(0.8923)) == exp2_64(0.8923)); + expect(exp2(f32(0.8923)) == exp2_32(0.8923)); + expect(exp2(f64(0.8923)) == exp2_64(0.8923)); } test "math.exp2_32" { const epsilon = 0.000001; - assert(exp2_32(0.0) == 1.0); - assert(math.approxEq(f32, exp2_32(0.2), 1.148698, epsilon)); - assert(math.approxEq(f32, exp2_32(0.8923), 1.856133, epsilon)); - assert(math.approxEq(f32, exp2_32(1.5), 2.828427, epsilon)); - assert(math.approxEq(f32, exp2_32(37.45), 187747237888, epsilon)); + expect(exp2_32(0.0) == 1.0); + expect(math.approxEq(f32, exp2_32(0.2), 1.148698, epsilon)); + expect(math.approxEq(f32, exp2_32(0.8923), 1.856133, epsilon)); + expect(math.approxEq(f32, exp2_32(1.5), 2.828427, epsilon)); + expect(math.approxEq(f32, exp2_32(37.45), 187747237888, epsilon)); } test "math.exp2_64" { const epsilon = 0.000001; - assert(exp2_64(0.0) == 1.0); - assert(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon)); - assert(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon)); - assert(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon)); + expect(exp2_64(0.0) == 1.0); + expect(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon)); + expect(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon)); + expect(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon)); } test "math.exp2_32.special" { - assert(math.isPositiveInf(exp2_32(math.inf(f32)))); - assert(math.isNan(exp2_32(math.nan(f32)))); + expect(math.isPositiveInf(exp2_32(math.inf(f32)))); + expect(math.isNan(exp2_32(math.nan(f32)))); } test "math.exp2_64.special" { - assert(math.isPositiveInf(exp2_64(math.inf(f64)))); - assert(math.isNan(exp2_64(math.nan(f64)))); + expect(math.isPositiveInf(exp2_64(math.inf(f64)))); + expect(math.isNan(exp2_64(math.nan(f64)))); } diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 6729417f60..ba00ec2561 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn expm1(x: var) @typeOf(x) { const T = @typeOf(x); @@ -278,42 +278,42 @@ fn expm1_64(x_: f64) f64 { } test "math.exp1m" { - assert(expm1(f32(0.0)) == expm1_32(0.0)); - assert(expm1(f64(0.0)) == expm1_64(0.0)); + expect(expm1(f32(0.0)) == expm1_32(0.0)); + expect(expm1(f64(0.0)) == expm1_64(0.0)); } test "math.expm1_32" { const epsilon = 0.000001; - assert(expm1_32(0.0) == 0.0); - assert(math.approxEq(f32, expm1_32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, expm1_32(0.2), 0.221403, epsilon)); - assert(math.approxEq(f32, expm1_32(0.8923), 1.440737, epsilon)); - assert(math.approxEq(f32, expm1_32(1.5), 3.481689, epsilon)); + expect(expm1_32(0.0) == 0.0); + expect(math.approxEq(f32, expm1_32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, expm1_32(0.2), 0.221403, epsilon)); + expect(math.approxEq(f32, expm1_32(0.8923), 1.440737, epsilon)); + expect(math.approxEq(f32, expm1_32(1.5), 3.481689, epsilon)); } test "math.expm1_64" { const epsilon = 0.000001; - assert(expm1_64(0.0) == 0.0); - assert(math.approxEq(f64, expm1_64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, expm1_64(0.2), 0.221403, epsilon)); - assert(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon)); - assert(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon)); + expect(expm1_64(0.0) == 0.0); + expect(math.approxEq(f64, expm1_64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, expm1_64(0.2), 0.221403, epsilon)); + expect(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon)); + expect(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon)); } test "math.expm1_32.special" { const epsilon = 0.000001; - assert(math.isPositiveInf(expm1_32(math.inf(f32)))); - assert(expm1_32(-math.inf(f32)) == -1.0); - assert(math.isNan(expm1_32(math.nan(f32)))); + expect(math.isPositiveInf(expm1_32(math.inf(f32)))); + expect(expm1_32(-math.inf(f32)) == -1.0); + expect(math.isNan(expm1_32(math.nan(f32)))); } test "math.expm1_64.special" { const epsilon = 0.000001; - assert(math.isPositiveInf(expm1_64(math.inf(f64)))); - assert(expm1_64(-math.inf(f64)) == -1.0); - assert(math.isNan(expm1_64(math.nan(f64)))); + expect(math.isPositiveInf(expm1_64(math.inf(f64)))); + expect(expm1_64(-math.inf(f64)) == -1.0); + expect(math.isNan(expm1_64(math.nan(f64)))); } diff --git a/std/math/fabs.zig b/std/math/fabs.zig index 443010ac7f..a605f4f33f 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -5,7 +5,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn fabs(x: var) @typeOf(x) { @@ -14,6 +14,7 @@ pub fn fabs(x: var) @typeOf(x) { f16 => fabs16(x), f32 => fabs32(x), f64 => fabs64(x), + f128 => fabs128(x), else => @compileError("fabs not implemented for " ++ @typeName(T)), }; } @@ -36,41 +37,59 @@ fn fabs64(x: f64) f64 { return @bitCast(f64, u); } +fn fabs128(x: f128) f128 { + var u = @bitCast(u128, x); + u &= maxInt(u128) >> 1; + return @bitCast(f128, u); +} + test "math.fabs" { - assert(fabs(f16(1.0)) == fabs16(1.0)); - assert(fabs(f32(1.0)) == fabs32(1.0)); - assert(fabs(f64(1.0)) == fabs64(1.0)); + 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)); } test "math.fabs16" { - assert(fabs16(1.0) == 1.0); - assert(fabs16(-1.0) == 1.0); + expect(fabs16(1.0) == 1.0); + expect(fabs16(-1.0) == 1.0); } test "math.fabs32" { - assert(fabs32(1.0) == 1.0); - assert(fabs32(-1.0) == 1.0); + expect(fabs32(1.0) == 1.0); + expect(fabs32(-1.0) == 1.0); } test "math.fabs64" { - assert(fabs64(1.0) == 1.0); - assert(fabs64(-1.0) == 1.0); + expect(fabs64(1.0) == 1.0); + expect(fabs64(-1.0) == 1.0); +} + +test "math.fabs128" { + expect(fabs128(1.0) == 1.0); + expect(fabs128(-1.0) == 1.0); } test "math.fabs16.special" { - assert(math.isPositiveInf(fabs(math.inf(f16)))); - assert(math.isPositiveInf(fabs(-math.inf(f16)))); - assert(math.isNan(fabs(math.nan(f16)))); + expect(math.isPositiveInf(fabs(math.inf(f16)))); + expect(math.isPositiveInf(fabs(-math.inf(f16)))); + expect(math.isNan(fabs(math.nan(f16)))); } test "math.fabs32.special" { - assert(math.isPositiveInf(fabs(math.inf(f32)))); - assert(math.isPositiveInf(fabs(-math.inf(f32)))); - assert(math.isNan(fabs(math.nan(f32)))); + expect(math.isPositiveInf(fabs(math.inf(f32)))); + expect(math.isPositiveInf(fabs(-math.inf(f32)))); + expect(math.isNan(fabs(math.nan(f32)))); } test "math.fabs64.special" { - assert(math.isPositiveInf(fabs(math.inf(f64)))); - assert(math.isPositiveInf(fabs(-math.inf(f64)))); - assert(math.isNan(fabs(math.nan(f64)))); + expect(math.isPositiveInf(fabs(math.inf(f64)))); + expect(math.isPositiveInf(fabs(-math.inf(f64)))); + expect(math.isNan(fabs(math.nan(f64)))); +} + +test "math.fabs128.special" { + expect(math.isPositiveInf(fabs(math.inf(f128)))); + expect(math.isPositiveInf(fabs(-math.inf(f128)))); + expect(math.isNan(fabs(math.nan(f128)))); } diff --git a/std/math/floor.zig b/std/math/floor.zig index 6ce462b10f..c7c12e37ba 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -5,7 +5,7 @@ // - floor(nan) = nan const builtin = @import("builtin"); -const assert = std.debug.assert; +const expect = std.testing.expect; const std = @import("../index.zig"); const math = std.math; @@ -117,49 +117,49 @@ fn floor64(x: f64) f64 { } test "math.floor" { - assert(floor(f16(1.3)) == floor16(1.3)); - assert(floor(f32(1.3)) == floor32(1.3)); - assert(floor(f64(1.3)) == floor64(1.3)); + expect(floor(f16(1.3)) == floor16(1.3)); + expect(floor(f32(1.3)) == floor32(1.3)); + expect(floor(f64(1.3)) == floor64(1.3)); } test "math.floor16" { - assert(floor16(1.3) == 1.0); - assert(floor16(-1.3) == -2.0); - assert(floor16(0.2) == 0.0); + expect(floor16(1.3) == 1.0); + expect(floor16(-1.3) == -2.0); + expect(floor16(0.2) == 0.0); } test "math.floor32" { - assert(floor32(1.3) == 1.0); - assert(floor32(-1.3) == -2.0); - assert(floor32(0.2) == 0.0); + expect(floor32(1.3) == 1.0); + expect(floor32(-1.3) == -2.0); + expect(floor32(0.2) == 0.0); } test "math.floor64" { - assert(floor64(1.3) == 1.0); - assert(floor64(-1.3) == -2.0); - assert(floor64(0.2) == 0.0); + expect(floor64(1.3) == 1.0); + expect(floor64(-1.3) == -2.0); + expect(floor64(0.2) == 0.0); } test "math.floor16.special" { - assert(floor16(0.0) == 0.0); - assert(floor16(-0.0) == -0.0); - assert(math.isPositiveInf(floor16(math.inf(f16)))); - assert(math.isNegativeInf(floor16(-math.inf(f16)))); - assert(math.isNan(floor16(math.nan(f16)))); + expect(floor16(0.0) == 0.0); + expect(floor16(-0.0) == -0.0); + expect(math.isPositiveInf(floor16(math.inf(f16)))); + expect(math.isNegativeInf(floor16(-math.inf(f16)))); + expect(math.isNan(floor16(math.nan(f16)))); } test "math.floor32.special" { - assert(floor32(0.0) == 0.0); - assert(floor32(-0.0) == -0.0); - assert(math.isPositiveInf(floor32(math.inf(f32)))); - assert(math.isNegativeInf(floor32(-math.inf(f32)))); - assert(math.isNan(floor32(math.nan(f32)))); + expect(floor32(0.0) == 0.0); + expect(floor32(-0.0) == -0.0); + expect(math.isPositiveInf(floor32(math.inf(f32)))); + expect(math.isNegativeInf(floor32(-math.inf(f32)))); + expect(math.isNan(floor32(math.nan(f32)))); } test "math.floor64.special" { - assert(floor64(0.0) == 0.0); - assert(floor64(-0.0) == -0.0); - assert(math.isPositiveInf(floor64(math.inf(f64)))); - assert(math.isNegativeInf(floor64(-math.inf(f64)))); - assert(math.isNan(floor64(math.nan(f64)))); + expect(floor64(0.0) == 0.0); + expect(floor64(-0.0) == -0.0); + expect(math.isPositiveInf(floor64(math.inf(f64)))); + expect(math.isNegativeInf(floor64(-math.inf(f64)))); + expect(math.isNan(floor64(math.nan(f64)))); } diff --git a/std/math/fma.zig b/std/math/fma.zig index 21faf4118d..b084cf3cbd 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn fma(comptime T: type, x: T, y: T, z: T) T { return switch (T) { @@ -135,30 +135,30 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 { } test "math.fma" { - assert(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0)); - assert(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0)); + expect(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0)); + expect(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0)); } test "math.fma32" { const epsilon = 0.000001; - assert(math.approxEq(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon)); - assert(math.approxEq(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon)); - assert(math.approxEq(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon)); - assert(math.approxEq(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon)); - assert(math.approxEq(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon)); - assert(math.approxEq(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon)); - assert(math.approxEq(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); + expect(math.approxEq(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon)); + expect(math.approxEq(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon)); + expect(math.approxEq(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon)); + expect(math.approxEq(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon)); + expect(math.approxEq(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon)); + expect(math.approxEq(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon)); + expect(math.approxEq(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); } test "math.fma64" { const epsilon = 0.000001; - assert(math.approxEq(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon)); - assert(math.approxEq(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon)); - assert(math.approxEq(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon)); - assert(math.approxEq(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon)); - assert(math.approxEq(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon)); - assert(math.approxEq(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon)); - assert(math.approxEq(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); + expect(math.approxEq(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon)); + expect(math.approxEq(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon)); + expect(math.approxEq(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon)); + expect(math.approxEq(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon)); + expect(math.approxEq(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon)); + expect(math.approxEq(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon)); + expect(math.approxEq(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); } diff --git a/std/math/frexp.zig b/std/math/frexp.zig index dfc790fdd9..35f3e081a9 100644 --- a/std/math/frexp.zig +++ b/std/math/frexp.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; fn frexp_result(comptime T: type) type { return struct { @@ -103,11 +103,11 @@ fn frexp64(x: f64) frexp64_result { test "math.frexp" { const a = frexp(f32(1.3)); const b = frexp32(1.3); - assert(a.significand == b.significand and a.exponent == b.exponent); + expect(a.significand == b.significand and a.exponent == b.exponent); const c = frexp(f64(1.3)); const d = frexp64(1.3); - assert(c.significand == d.significand and c.exponent == d.exponent); + expect(c.significand == d.significand and c.exponent == d.exponent); } test "math.frexp32" { @@ -115,10 +115,10 @@ test "math.frexp32" { var r: frexp32_result = undefined; r = frexp32(1.3); - assert(math.approxEq(f32, r.significand, 0.65, epsilon) and r.exponent == 1); + expect(math.approxEq(f32, r.significand, 0.65, epsilon) and r.exponent == 1); r = frexp32(78.0234); - assert(math.approxEq(f32, r.significand, 0.609558, epsilon) and r.exponent == 7); + expect(math.approxEq(f32, r.significand, 0.609558, epsilon) and r.exponent == 7); } test "math.frexp64" { @@ -126,46 +126,46 @@ test "math.frexp64" { var r: frexp64_result = undefined; r = frexp64(1.3); - assert(math.approxEq(f64, r.significand, 0.65, epsilon) and r.exponent == 1); + expect(math.approxEq(f64, r.significand, 0.65, epsilon) and r.exponent == 1); r = frexp64(78.0234); - assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); + expect(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); } test "math.frexp32.special" { var r: frexp32_result = undefined; r = frexp32(0.0); - assert(r.significand == 0.0 and r.exponent == 0); + expect(r.significand == 0.0 and r.exponent == 0); r = frexp32(-0.0); - assert(r.significand == -0.0 and r.exponent == 0); + expect(r.significand == -0.0 and r.exponent == 0); r = frexp32(math.inf(f32)); - assert(math.isPositiveInf(r.significand) and r.exponent == 0); + expect(math.isPositiveInf(r.significand) and r.exponent == 0); r = frexp32(-math.inf(f32)); - assert(math.isNegativeInf(r.significand) and r.exponent == 0); + expect(math.isNegativeInf(r.significand) and r.exponent == 0); r = frexp32(math.nan(f32)); - assert(math.isNan(r.significand)); + expect(math.isNan(r.significand)); } test "math.frexp64.special" { var r: frexp64_result = undefined; r = frexp64(0.0); - assert(r.significand == 0.0 and r.exponent == 0); + expect(r.significand == 0.0 and r.exponent == 0); r = frexp64(-0.0); - assert(r.significand == -0.0 and r.exponent == 0); + expect(r.significand == -0.0 and r.exponent == 0); r = frexp64(math.inf(f64)); - assert(math.isPositiveInf(r.significand) and r.exponent == 0); + expect(math.isPositiveInf(r.significand) and r.exponent == 0); r = frexp64(-math.inf(f64)); - assert(math.isNegativeInf(r.significand) and r.exponent == 0); + expect(math.isNegativeInf(r.significand) and r.exponent == 0); r = frexp64(math.nan(f64)); - assert(math.isNan(r.significand)); + expect(math.isNan(r.significand)); } diff --git a/std/math/hypot.zig b/std/math/hypot.zig index a63657a621..dea11e0a61 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn hypot(comptime T: type, x: T, y: T) T { @@ -115,48 +115,48 @@ fn hypot64(x: f64, y: f64) f64 { } test "math.hypot" { - assert(hypot(f32, 0.0, -1.2) == hypot32(0.0, -1.2)); - assert(hypot(f64, 0.0, -1.2) == hypot64(0.0, -1.2)); + expect(hypot(f32, 0.0, -1.2) == hypot32(0.0, -1.2)); + expect(hypot(f64, 0.0, -1.2) == hypot64(0.0, -1.2)); } test "math.hypot32" { const epsilon = 0.000001; - assert(math.approxEq(f32, hypot32(0.0, -1.2), 1.2, epsilon)); - assert(math.approxEq(f32, hypot32(0.2, -0.34), 0.394462, epsilon)); - assert(math.approxEq(f32, hypot32(0.8923, 2.636890), 2.783772, epsilon)); - assert(math.approxEq(f32, hypot32(1.5, 5.25), 5.460083, epsilon)); - assert(math.approxEq(f32, hypot32(37.45, 159.835), 164.163742, epsilon)); - assert(math.approxEq(f32, hypot32(89.123, 382.028905), 392.286865, epsilon)); - assert(math.approxEq(f32, hypot32(123123.234375, 529428.707813), 543556.875, epsilon)); + expect(math.approxEq(f32, hypot32(0.0, -1.2), 1.2, epsilon)); + expect(math.approxEq(f32, hypot32(0.2, -0.34), 0.394462, epsilon)); + expect(math.approxEq(f32, hypot32(0.8923, 2.636890), 2.783772, epsilon)); + expect(math.approxEq(f32, hypot32(1.5, 5.25), 5.460083, epsilon)); + expect(math.approxEq(f32, hypot32(37.45, 159.835), 164.163742, epsilon)); + expect(math.approxEq(f32, hypot32(89.123, 382.028905), 392.286865, epsilon)); + expect(math.approxEq(f32, hypot32(123123.234375, 529428.707813), 543556.875, epsilon)); } test "math.hypot64" { const epsilon = 0.000001; - assert(math.approxEq(f64, hypot64(0.0, -1.2), 1.2, epsilon)); - assert(math.approxEq(f64, hypot64(0.2, -0.34), 0.394462, epsilon)); - assert(math.approxEq(f64, hypot64(0.8923, 2.636890), 2.783772, epsilon)); - assert(math.approxEq(f64, hypot64(1.5, 5.25), 5.460082, epsilon)); - assert(math.approxEq(f64, hypot64(37.45, 159.835), 164.163728, epsilon)); - assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon)); - assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon)); + expect(math.approxEq(f64, hypot64(0.0, -1.2), 1.2, epsilon)); + expect(math.approxEq(f64, hypot64(0.2, -0.34), 0.394462, epsilon)); + expect(math.approxEq(f64, hypot64(0.8923, 2.636890), 2.783772, epsilon)); + expect(math.approxEq(f64, hypot64(1.5, 5.25), 5.460082, epsilon)); + expect(math.approxEq(f64, hypot64(37.45, 159.835), 164.163728, epsilon)); + expect(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon)); + expect(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon)); } test "math.hypot32.special" { - assert(math.isPositiveInf(hypot32(math.inf(f32), 0.0))); - assert(math.isPositiveInf(hypot32(-math.inf(f32), 0.0))); - assert(math.isPositiveInf(hypot32(0.0, math.inf(f32)))); - assert(math.isPositiveInf(hypot32(0.0, -math.inf(f32)))); - assert(math.isNan(hypot32(math.nan(f32), 0.0))); - assert(math.isNan(hypot32(0.0, math.nan(f32)))); + expect(math.isPositiveInf(hypot32(math.inf(f32), 0.0))); + expect(math.isPositiveInf(hypot32(-math.inf(f32), 0.0))); + expect(math.isPositiveInf(hypot32(0.0, math.inf(f32)))); + expect(math.isPositiveInf(hypot32(0.0, -math.inf(f32)))); + expect(math.isNan(hypot32(math.nan(f32), 0.0))); + expect(math.isNan(hypot32(0.0, math.nan(f32)))); } test "math.hypot64.special" { - assert(math.isPositiveInf(hypot64(math.inf(f64), 0.0))); - assert(math.isPositiveInf(hypot64(-math.inf(f64), 0.0))); - assert(math.isPositiveInf(hypot64(0.0, math.inf(f64)))); - assert(math.isPositiveInf(hypot64(0.0, -math.inf(f64)))); - assert(math.isNan(hypot64(math.nan(f64), 0.0))); - assert(math.isNan(hypot64(0.0, math.nan(f64)))); + expect(math.isPositiveInf(hypot64(math.inf(f64), 0.0))); + expect(math.isPositiveInf(hypot64(-math.inf(f64), 0.0))); + expect(math.isPositiveInf(hypot64(0.0, math.inf(f64)))); + expect(math.isPositiveInf(hypot64(0.0, -math.inf(f64)))); + expect(math.isNan(hypot64(math.nan(f64), 0.0))); + expect(math.isNan(hypot64(0.0, math.nan(f64)))); } diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig index e6bdb14012..e7b6485357 100644 --- a/std/math/ilogb.zig +++ b/std/math/ilogb.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -95,38 +95,38 @@ fn ilogb64(x: f64) i32 { } test "math.ilogb" { - assert(ilogb(f32(0.2)) == ilogb32(0.2)); - assert(ilogb(f64(0.2)) == ilogb64(0.2)); + expect(ilogb(f32(0.2)) == ilogb32(0.2)); + expect(ilogb(f64(0.2)) == ilogb64(0.2)); } test "math.ilogb32" { - assert(ilogb32(0.0) == fp_ilogb0); - assert(ilogb32(0.5) == -1); - assert(ilogb32(0.8923) == -1); - assert(ilogb32(10.0) == 3); - assert(ilogb32(-123984) == 16); - assert(ilogb32(2398.23) == 11); + expect(ilogb32(0.0) == fp_ilogb0); + expect(ilogb32(0.5) == -1); + expect(ilogb32(0.8923) == -1); + expect(ilogb32(10.0) == 3); + expect(ilogb32(-123984) == 16); + expect(ilogb32(2398.23) == 11); } test "math.ilogb64" { - assert(ilogb64(0.0) == fp_ilogb0); - assert(ilogb64(0.5) == -1); - assert(ilogb64(0.8923) == -1); - assert(ilogb64(10.0) == 3); - assert(ilogb64(-123984) == 16); - assert(ilogb64(2398.23) == 11); + expect(ilogb64(0.0) == fp_ilogb0); + expect(ilogb64(0.5) == -1); + expect(ilogb64(0.8923) == -1); + expect(ilogb64(10.0) == 3); + expect(ilogb64(-123984) == 16); + expect(ilogb64(2398.23) == 11); } test "math.ilogb32.special" { - assert(ilogb32(math.inf(f32)) == maxInt(i32)); - assert(ilogb32(-math.inf(f32)) == maxInt(i32)); - assert(ilogb32(0.0) == minInt(i32)); - assert(ilogb32(math.nan(f32)) == maxInt(i32)); + expect(ilogb32(math.inf(f32)) == maxInt(i32)); + expect(ilogb32(-math.inf(f32)) == maxInt(i32)); + expect(ilogb32(0.0) == minInt(i32)); + expect(ilogb32(math.nan(f32)) == maxInt(i32)); } test "math.ilogb64.special" { - assert(ilogb64(math.inf(f64)) == maxInt(i32)); - assert(ilogb64(-math.inf(f64)) == maxInt(i32)); - assert(ilogb64(0.0) == minInt(i32)); - assert(ilogb64(math.nan(f64)) == maxInt(i32)); + expect(ilogb64(math.inf(f64)) == maxInt(i32)); + expect(ilogb64(-math.inf(f64)) == maxInt(i32)); + expect(ilogb64(0.0) == minInt(i32)); + expect(ilogb64(math.nan(f64)) == maxInt(i32)); } diff --git a/std/math/index.zig b/std/math/index.zig index 83bd2310d0..20648139b8 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -2,10 +2,18 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const TypeId = builtin.TypeId; const assert = std.debug.assert; +const testing = std.testing; pub const e = 2.71828182845904523536028747135266249775724709369995; pub const pi = 3.14159265358979323846264338327950288419716939937510; +// 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_toint = 1.0 / f128_epsilon; + // float.h details pub const f64_true_min = 4.94065645841246544177e-324; pub const f64_min = 2.2250738585072014e-308; @@ -43,6 +51,12 @@ pub const nan_f64 = @bitCast(f64, nan_u64); pub const inf_u64 = u64(0x7FF << 52); pub const inf_f64 = @bitCast(f64, inf_u64); +pub const nan_u128 = u128(0x7fff0000000000000000000000000001); +pub const nan_f128 = @bitCast(f128, nan_u128); + +pub const inf_u128 = u128(0x7fff0000000000000000000000000000); +pub const inf_f128 = @bitCast(f128, inf_u128); + pub const nan = @import("nan.zig").nan; pub const snan = @import("nan.zig").snan; pub const inf = @import("inf.zig").inf; @@ -233,7 +247,7 @@ pub fn min(x: var, y: var) @typeOf(x + y) { } test "math.min" { - assert(min(i32(-1), i32(2)) == -1); + testing.expect(min(i32(-1), i32(2)) == -1); } pub fn max(x: var, y: var) @typeOf(x + y) { @@ -241,7 +255,7 @@ pub fn max(x: var, y: var) @typeOf(x + y) { } test "math.max" { - assert(max(i32(-1), i32(2)) == 2); + testing.expect(max(i32(-1), i32(2)) == 2); } pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { @@ -286,10 +300,10 @@ pub fn shl(comptime T: type, a: T, shift_amt: var) T { } test "math.shl" { - assert(shl(u8, 0b11111111, usize(3)) == 0b11111000); - assert(shl(u8, 0b11111111, usize(8)) == 0); - assert(shl(u8, 0b11111111, usize(9)) == 0); - assert(shl(u8, 0b11111111, isize(-2)) == 0b00111111); + 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); } /// Shifts right. Overflowed bits are truncated. @@ -310,10 +324,10 @@ pub fn shr(comptime T: type, a: T, shift_amt: var) T { } test "math.shr" { - assert(shr(u8, 0b11111111, usize(3)) == 0b00011111); - assert(shr(u8, 0b11111111, usize(8)) == 0); - assert(shr(u8, 0b11111111, usize(9)) == 0); - assert(shr(u8, 0b11111111, isize(-2)) == 0b11111100); + 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); } /// Rotates right. Only unsigned values can be rotated. @@ -328,11 +342,11 @@ pub fn rotr(comptime T: type, x: T, r: var) T { } test "math.rotr" { - assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); - assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); - assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); + 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); } /// Rotates left. Only unsigned values can be rotated. @@ -347,11 +361,11 @@ pub fn rotl(comptime T: type, x: T, r: var) T { } test "math.rotl" { - assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); - assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); - assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); + 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); } pub fn Log2Int(comptime T: type) type { @@ -371,7 +385,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t return u0; } const is_signed = from < 0; - const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement + const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement const base = log2(largest_positive_integer); const upper = (1 << base) - 1; var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; @@ -382,50 +396,50 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t } test "math.IntFittingRange" { - assert(IntFittingRange(0, 0) == u0); - assert(IntFittingRange(0, 1) == u1); - assert(IntFittingRange(0, 2) == u2); - assert(IntFittingRange(0, 3) == u2); - assert(IntFittingRange(0, 4) == u3); - assert(IntFittingRange(0, 7) == u3); - assert(IntFittingRange(0, 8) == u4); - assert(IntFittingRange(0, 9) == u4); - assert(IntFittingRange(0, 15) == u4); - assert(IntFittingRange(0, 16) == u5); - assert(IntFittingRange(0, 17) == u5); - assert(IntFittingRange(0, 4095) == u12); - assert(IntFittingRange(2000, 4095) == u12); - assert(IntFittingRange(0, 4096) == u13); - assert(IntFittingRange(2000, 4096) == u13); - assert(IntFittingRange(0, 4097) == u13); - assert(IntFittingRange(2000, 4097) == u13); - assert(IntFittingRange(0, 123456789123456798123456789) == u87); - assert(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); + testing.expect(IntFittingRange(0, 0) == u0); + testing.expect(IntFittingRange(0, 1) == u1); + testing.expect(IntFittingRange(0, 2) == u2); + testing.expect(IntFittingRange(0, 3) == u2); + testing.expect(IntFittingRange(0, 4) == u3); + testing.expect(IntFittingRange(0, 7) == u3); + testing.expect(IntFittingRange(0, 8) == u4); + testing.expect(IntFittingRange(0, 9) == u4); + testing.expect(IntFittingRange(0, 15) == u4); + testing.expect(IntFittingRange(0, 16) == u5); + testing.expect(IntFittingRange(0, 17) == u5); + testing.expect(IntFittingRange(0, 4095) == u12); + testing.expect(IntFittingRange(2000, 4095) == u12); + testing.expect(IntFittingRange(0, 4096) == u13); + testing.expect(IntFittingRange(2000, 4096) == u13); + testing.expect(IntFittingRange(0, 4097) == u13); + testing.expect(IntFittingRange(2000, 4097) == u13); + testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87); + testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); - assert(IntFittingRange(-1, -1) == i1); - assert(IntFittingRange(-1, 0) == i1); - assert(IntFittingRange(-1, 1) == i2); - assert(IntFittingRange(-2, -2) == i2); - assert(IntFittingRange(-2, -1) == i2); - assert(IntFittingRange(-2, 0) == i2); - assert(IntFittingRange(-2, 1) == i2); - assert(IntFittingRange(-2, 2) == i3); - assert(IntFittingRange(-1, 2) == i3); - assert(IntFittingRange(-1, 3) == i3); - assert(IntFittingRange(-1, 4) == i4); - assert(IntFittingRange(-1, 7) == i4); - assert(IntFittingRange(-1, 8) == i5); - assert(IntFittingRange(-1, 9) == i5); - assert(IntFittingRange(-1, 15) == i5); - assert(IntFittingRange(-1, 16) == i6); - assert(IntFittingRange(-1, 17) == i6); - assert(IntFittingRange(-1, 4095) == i13); - assert(IntFittingRange(-4096, 4095) == i13); - assert(IntFittingRange(-1, 4096) == i14); - assert(IntFittingRange(-4097, 4095) == i14); - assert(IntFittingRange(-1, 4097) == i14); - assert(IntFittingRange(-1, 123456789123456798123456789) == i88); - assert(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); + testing.expect(IntFittingRange(-1, -1) == i1); + testing.expect(IntFittingRange(-1, 0) == i1); + testing.expect(IntFittingRange(-1, 1) == i2); + testing.expect(IntFittingRange(-2, -2) == i2); + testing.expect(IntFittingRange(-2, -1) == i2); + testing.expect(IntFittingRange(-2, 0) == i2); + testing.expect(IntFittingRange(-2, 1) == i2); + testing.expect(IntFittingRange(-2, 2) == i3); + testing.expect(IntFittingRange(-1, 2) == i3); + testing.expect(IntFittingRange(-1, 3) == i3); + testing.expect(IntFittingRange(-1, 4) == i4); + testing.expect(IntFittingRange(-1, 7) == i4); + testing.expect(IntFittingRange(-1, 8) == i5); + testing.expect(IntFittingRange(-1, 9) == i5); + testing.expect(IntFittingRange(-1, 15) == i5); + testing.expect(IntFittingRange(-1, 16) == i6); + testing.expect(IntFittingRange(-1, 17) == i6); + testing.expect(IntFittingRange(-1, 4095) == i13); + testing.expect(IntFittingRange(-4096, 4095) == i13); + testing.expect(IntFittingRange(-1, 4096) == i14); + testing.expect(IntFittingRange(-4097, 4095) == i14); + testing.expect(IntFittingRange(-1, 4097) == i14); + testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88); + testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); } test "math overflow functions" { @@ -434,10 +448,10 @@ test "math overflow functions" { } fn testOverflow() void { - assert((mul(i32, 3, 4) catch unreachable) == 12); - assert((add(i32, 3, 4) catch unreachable) == 7); - assert((sub(i32, 3, 4) catch unreachable) == -1); - assert((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); + testing.expect((mul(i32, 3, 4) catch unreachable) == 12); + testing.expect((add(i32, 3, 4) catch unreachable) == 7); + testing.expect((sub(i32, 3, 4) catch unreachable) == -1); + testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); } pub fn absInt(x: var) !@typeOf(x) { @@ -458,8 +472,8 @@ test "math.absInt" { comptime testAbsInt(); } fn testAbsInt() void { - assert((absInt(i32(-10)) catch unreachable) == 10); - assert((absInt(i32(10)) catch unreachable) == 10); + testing.expect((absInt(i32(-10)) catch unreachable) == 10); + testing.expect((absInt(i32(10)) catch unreachable) == 10); } pub const absFloat = @import("fabs.zig").fabs; @@ -476,13 +490,13 @@ test "math.divTrunc" { comptime testDivTrunc(); } fn testDivTrunc() void { - assert((divTrunc(i32, 5, 3) catch unreachable) == 1); - assert((divTrunc(i32, -5, 3) catch unreachable) == -1); - if (divTrunc(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); - if (divTrunc(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); + testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1); + testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1); + testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0)); + testing.expectError(error.Overflow, divTrunc(i8, -128, -1)); - assert((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); - assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); + testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); + testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); } pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { @@ -497,13 +511,13 @@ test "math.divFloor" { comptime testDivFloor(); } fn testDivFloor() void { - assert((divFloor(i32, 5, 3) catch unreachable) == 1); - assert((divFloor(i32, -5, 3) catch unreachable) == -2); - if (divFloor(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); - if (divFloor(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); + testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1); + testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2); + testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0)); + testing.expectError(error.Overflow, divFloor(i8, -128, -1)); - assert((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); - assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); + testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); + testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); } pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { @@ -520,15 +534,15 @@ test "math.divExact" { comptime testDivExact(); } fn testDivExact() void { - assert((divExact(i32, 10, 5) catch unreachable) == 2); - assert((divExact(i32, -10, 5) catch unreachable) == -2); - if (divExact(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); - if (divExact(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); - if (divExact(i32, 5, 2)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); + testing.expect((divExact(i32, 10, 5) catch unreachable) == 2); + testing.expect((divExact(i32, -10, 5) catch unreachable) == -2); + testing.expectError(error.DivisionByZero, divExact(i8, -5, 0)); + testing.expectError(error.Overflow, divExact(i8, -128, -1)); + testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2)); - assert((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); - assert((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); - if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); + testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); + testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); + testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0)); } pub fn mod(comptime T: type, numerator: T, denominator: T) !T { @@ -543,15 +557,15 @@ test "math.mod" { comptime testMod(); } fn testMod() void { - assert((mod(i32, -5, 3) catch unreachable) == 1); - assert((mod(i32, 5, 3) catch unreachable) == 2); - if (mod(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); - if (mod(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); + testing.expect((mod(i32, -5, 3) catch unreachable) == 1); + testing.expect((mod(i32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, mod(i32, 10, -1)); + testing.expectError(error.DivisionByZero, mod(i32, 10, 0)); - assert((mod(f32, -5, 3) catch unreachable) == 1); - assert((mod(f32, 5, 3) catch unreachable) == 2); - if (mod(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); - if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); + testing.expect((mod(f32, -5, 3) catch unreachable) == 1); + testing.expect((mod(f32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, mod(f32, 10, -1)); + testing.expectError(error.DivisionByZero, mod(f32, 10, 0)); } pub fn rem(comptime T: type, numerator: T, denominator: T) !T { @@ -566,15 +580,15 @@ test "math.rem" { comptime testRem(); } fn testRem() void { - assert((rem(i32, -5, 3) catch unreachable) == -2); - assert((rem(i32, 5, 3) catch unreachable) == 2); - if (rem(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); - if (rem(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); + testing.expect((rem(i32, -5, 3) catch unreachable) == -2); + testing.expect((rem(i32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, rem(i32, 10, -1)); + testing.expectError(error.DivisionByZero, rem(i32, 10, 0)); - assert((rem(f32, -5, 3) catch unreachable) == -2); - assert((rem(f32, 5, 3) catch unreachable) == 2); - if (rem(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); - if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); + testing.expect((rem(f32, -5, 3) catch unreachable) == -2); + testing.expect((rem(f32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, rem(f32, 10, -1)); + testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); } /// Returns the absolute value of the integer parameter. @@ -587,14 +601,14 @@ pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { } test "math.absCast" { - assert(absCast(i32(-999)) == 999); - assert(@typeOf(absCast(i32(-999))) == u32); + testing.expect(absCast(i32(-999)) == 999); + testing.expect(@typeOf(absCast(i32(-999))) == u32); - assert(absCast(i32(999)) == 999); - assert(@typeOf(absCast(i32(999))) == u32); + testing.expect(absCast(i32(999)) == 999); + testing.expect(@typeOf(absCast(i32(999))) == u32); - assert(absCast(i32(minInt(i32))) == -minInt(i32)); - assert(@typeOf(absCast(i32(minInt(i32)))) == u32); + testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); + testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); } /// Returns the negation of the integer parameter. @@ -611,13 +625,13 @@ pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { } test "math.negateCast" { - assert((negateCast(u32(999)) catch unreachable) == -999); - assert(@typeOf(negateCast(u32(999)) catch unreachable) == i32); + testing.expect((negateCast(u32(999)) catch unreachable) == -999); + testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); - assert((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); - assert(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); + testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); + testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); - if (negateCast(u32(maxInt(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow); + testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); } /// Cast an integer to a different integer type. If the value doesn't fit, @@ -635,13 +649,13 @@ pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { } test "math.cast" { - if (cast(u8, u32(300))) |_| @panic("fail") else |err| assert(err == error.Overflow); - if (cast(i8, i32(-200))) |_| @panic("fail") else |err| assert(err == error.Overflow); - if (cast(u8, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow); - if (cast(u64, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow); + 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))); - assert((try cast(u8, u32(255))) == u8(255)); - assert(@typeOf(try cast(u8, u32(255))) == u8); + testing.expect((try cast(u8, u32(255))) == u8(255)); + testing.expect(@typeOf(try cast(u8, u32(255))) == u8); } pub const AlignCastError = error{UnalignedMemory}; @@ -685,25 +699,25 @@ pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { } test "std.math.log2_int_ceil" { - assert(log2_int_ceil(u32, 1) == 0); - assert(log2_int_ceil(u32, 2) == 1); - assert(log2_int_ceil(u32, 3) == 2); - assert(log2_int_ceil(u32, 4) == 2); - assert(log2_int_ceil(u32, 5) == 3); - assert(log2_int_ceil(u32, 6) == 3); - assert(log2_int_ceil(u32, 7) == 3); - assert(log2_int_ceil(u32, 8) == 3); - assert(log2_int_ceil(u32, 9) == 4); - assert(log2_int_ceil(u32, 10) == 4); + testing.expect(log2_int_ceil(u32, 1) == 0); + testing.expect(log2_int_ceil(u32, 2) == 1); + testing.expect(log2_int_ceil(u32, 3) == 2); + testing.expect(log2_int_ceil(u32, 4) == 2); + testing.expect(log2_int_ceil(u32, 5) == 3); + testing.expect(log2_int_ceil(u32, 6) == 3); + testing.expect(log2_int_ceil(u32, 7) == 3); + testing.expect(log2_int_ceil(u32, 8) == 3); + testing.expect(log2_int_ceil(u32, 9) == 4); + testing.expect(log2_int_ceil(u32, 10) == 4); } fn testFloorPowerOfTwo() void { - assert(floorPowerOfTwo(u32, 63) == 32); - assert(floorPowerOfTwo(u32, 64) == 64); - assert(floorPowerOfTwo(u32, 65) == 64); - assert(floorPowerOfTwo(u4, 7) == 4); - assert(floorPowerOfTwo(u4, 8) == 8); - assert(floorPowerOfTwo(u4, 9) == 8); + testing.expect(floorPowerOfTwo(u32, 63) == 32); + testing.expect(floorPowerOfTwo(u32, 64) == 64); + testing.expect(floorPowerOfTwo(u32, 65) == 64); + testing.expect(floorPowerOfTwo(u4, 7) == 4); + testing.expect(floorPowerOfTwo(u4, 8) == 8); + testing.expect(floorPowerOfTwo(u4, 9) == 8); } pub fn lossyCast(comptime T: type, value: var) T { @@ -719,7 +733,7 @@ pub fn lossyCast(comptime T: type, value: var) T { test "math.f64_min" { const f64_min_u64 = 0x0010000000000000; const fmin: f64 = f64_min; - assert(@bitCast(u64, fmin) == f64_min_u64); + testing.expect(@bitCast(u64, fmin) == f64_min_u64); } pub fn maxInt(comptime T: type) comptime_int { @@ -738,36 +752,40 @@ pub fn minInt(comptime T: type) comptime_int { } test "minInt and maxInt" { - assert(maxInt(u0) == 0); - assert(maxInt(u1) == 1); - assert(maxInt(u8) == 255); - assert(maxInt(u16) == 65535); - assert(maxInt(u32) == 4294967295); - assert(maxInt(u64) == 18446744073709551615); + testing.expect(maxInt(u0) == 0); + testing.expect(maxInt(u1) == 1); + testing.expect(maxInt(u8) == 255); + testing.expect(maxInt(u16) == 65535); + testing.expect(maxInt(u32) == 4294967295); + testing.expect(maxInt(u64) == 18446744073709551615); + testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); - assert(maxInt(i0) == 0); - assert(maxInt(i1) == 0); - assert(maxInt(i8) == 127); - assert(maxInt(i16) == 32767); - assert(maxInt(i32) == 2147483647); - assert(maxInt(i63) == 4611686018427387903); - assert(maxInt(i64) == 9223372036854775807); + testing.expect(maxInt(i0) == 0); + testing.expect(maxInt(i1) == 0); + testing.expect(maxInt(i8) == 127); + testing.expect(maxInt(i16) == 32767); + testing.expect(maxInt(i32) == 2147483647); + testing.expect(maxInt(i63) == 4611686018427387903); + testing.expect(maxInt(i64) == 9223372036854775807); + testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); - assert(minInt(u0) == 0); - assert(minInt(u1) == 0); - assert(minInt(u8) == 0); - assert(minInt(u16) == 0); - assert(minInt(u32) == 0); - assert(minInt(u63) == 0); - assert(minInt(u64) == 0); + testing.expect(minInt(u0) == 0); + testing.expect(minInt(u1) == 0); + testing.expect(minInt(u8) == 0); + testing.expect(minInt(u16) == 0); + testing.expect(minInt(u32) == 0); + testing.expect(minInt(u63) == 0); + testing.expect(minInt(u64) == 0); + testing.expect(minInt(u128) == 0); - assert(minInt(i0) == 0); - assert(minInt(i1) == -1); - assert(minInt(i8) == -128); - assert(minInt(i16) == -32768); - assert(minInt(i32) == -2147483648); - assert(minInt(i63) == -4611686018427387904); - assert(minInt(i64) == -9223372036854775808); + testing.expect(minInt(i0) == 0); + testing.expect(minInt(i1) == -1); + testing.expect(minInt(i8) == -128); + testing.expect(minInt(i16) == -32768); + testing.expect(minInt(i32) == -2147483648); + testing.expect(minInt(i63) == -4611686018427387904); + testing.expect(minInt(i64) == -9223372036854775808); + testing.expect(minInt(i128) == -170141183460469231731687303715884105728); } test "max value type" { @@ -775,5 +793,5 @@ test "max value type" { // u32 would not work. But since the value is a number literal, // it works fine. const x: u32 = maxInt(i32); - assert(x == 2147483647); + testing.expect(x == 2147483647); } diff --git a/std/math/inf.zig b/std/math/inf.zig index 62f5ef7c0d..fb7a3489c5 100644 --- a/std/math/inf.zig +++ b/std/math/inf.zig @@ -3,9 +3,10 @@ const math = std.math; pub fn inf(comptime T: type) T { return switch (T) { - f16 => @bitCast(f16, math.inf_u16), - f32 => @bitCast(f32, math.inf_u32), - f64 => @bitCast(f64, math.inf_u64), + f16 => math.inf_f16, + f32 => math.inf_f32, + f64 => math.inf_f64, + f128 => math.inf_f128, else => @compileError("inf not implemented for " ++ @typeName(T)), }; } diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig index bdfdff8f9f..bf1c9ac63c 100644 --- a/std/math/isfinite.zig +++ b/std/math/isfinite.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn isFinite(x: var) bool { @@ -25,16 +25,16 @@ pub fn isFinite(x: var) bool { } test "math.isFinite" { - assert(isFinite(f16(0.0))); - assert(isFinite(f16(-0.0))); - assert(isFinite(f32(0.0))); - assert(isFinite(f32(-0.0))); - assert(isFinite(f64(0.0))); - assert(isFinite(f64(-0.0))); - assert(!isFinite(math.inf(f16))); - assert(!isFinite(-math.inf(f16))); - assert(!isFinite(math.inf(f32))); - assert(!isFinite(-math.inf(f32))); - assert(!isFinite(math.inf(f64))); - assert(!isFinite(-math.inf(f64))); + 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(math.inf(f16))); + expect(!isFinite(-math.inf(f16))); + expect(!isFinite(math.inf(f32))); + expect(!isFinite(-math.inf(f32))); + expect(!isFinite(math.inf(f64))); + expect(!isFinite(-math.inf(f64))); } diff --git a/std/math/isinf.zig b/std/math/isinf.zig index 93f66cc870..b1e3f7795e 100644 --- a/std/math/isinf.zig +++ b/std/math/isinf.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn isInf(x: var) bool { @@ -18,6 +18,10 @@ pub fn isInf(x: var) bool { const bits = @bitCast(u64, x); return bits & (maxInt(u64) >> 1) == (0x7FF << 52); }, + f128 => { + const bits = @bitCast(u128, x); + return bits & (maxInt(u128) >> 1) == (0x7FFF << 112); + }, else => { @compileError("isInf not implemented for " ++ @typeName(T)); }, @@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool { f64 => { return @bitCast(u64, x) == 0x7FF << 52; }, + f128 => { + return @bitCast(u128, x) == 0x7FFF << 112; + }, else => { @compileError("isPositiveInf not implemented for " ++ @typeName(T)); }, @@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool { f64 => { return @bitCast(u64, x) == 0xFFF << 52; }, + f128 => { + return @bitCast(u128, x) == 0xFFFF << 112; + }, else => { @compileError("isNegativeInf not implemented for " ++ @typeName(T)); }, @@ -61,46 +71,58 @@ pub fn isNegativeInf(x: var) bool { } test "math.isInf" { - assert(!isInf(f16(0.0))); - assert(!isInf(f16(-0.0))); - assert(!isInf(f32(0.0))); - assert(!isInf(f32(-0.0))); - assert(!isInf(f64(0.0))); - assert(!isInf(f64(-0.0))); - assert(isInf(math.inf(f16))); - assert(isInf(-math.inf(f16))); - assert(isInf(math.inf(f32))); - assert(isInf(-math.inf(f32))); - assert(isInf(math.inf(f64))); - assert(isInf(-math.inf(f64))); + 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(math.inf(f16))); + expect(isInf(-math.inf(f16))); + expect(isInf(math.inf(f32))); + expect(isInf(-math.inf(f32))); + expect(isInf(math.inf(f64))); + expect(isInf(-math.inf(f64))); + expect(isInf(math.inf(f128))); + expect(isInf(-math.inf(f128))); } test "math.isPositiveInf" { - assert(!isPositiveInf(f16(0.0))); - assert(!isPositiveInf(f16(-0.0))); - assert(!isPositiveInf(f32(0.0))); - assert(!isPositiveInf(f32(-0.0))); - assert(!isPositiveInf(f64(0.0))); - assert(!isPositiveInf(f64(-0.0))); - assert(isPositiveInf(math.inf(f16))); - assert(!isPositiveInf(-math.inf(f16))); - assert(isPositiveInf(math.inf(f32))); - assert(!isPositiveInf(-math.inf(f32))); - assert(isPositiveInf(math.inf(f64))); - assert(!isPositiveInf(-math.inf(f64))); + 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(math.inf(f16))); + expect(!isPositiveInf(-math.inf(f16))); + expect(isPositiveInf(math.inf(f32))); + expect(!isPositiveInf(-math.inf(f32))); + expect(isPositiveInf(math.inf(f64))); + expect(!isPositiveInf(-math.inf(f64))); + expect(isPositiveInf(math.inf(f128))); + expect(!isPositiveInf(-math.inf(f128))); } test "math.isNegativeInf" { - assert(!isNegativeInf(f16(0.0))); - assert(!isNegativeInf(f16(-0.0))); - assert(!isNegativeInf(f32(0.0))); - assert(!isNegativeInf(f32(-0.0))); - assert(!isNegativeInf(f64(0.0))); - assert(!isNegativeInf(f64(-0.0))); - assert(!isNegativeInf(math.inf(f16))); - assert(isNegativeInf(-math.inf(f16))); - assert(!isNegativeInf(math.inf(f32))); - assert(isNegativeInf(-math.inf(f32))); - assert(!isNegativeInf(math.inf(f64))); - assert(isNegativeInf(-math.inf(f64))); + 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(math.inf(f16))); + expect(isNegativeInf(-math.inf(f16))); + expect(!isNegativeInf(math.inf(f32))); + expect(isNegativeInf(-math.inf(f32))); + expect(!isNegativeInf(math.inf(f64))); + expect(isNegativeInf(-math.inf(f64))); + expect(!isNegativeInf(math.inf(f128))); + expect(isNegativeInf(-math.inf(f128))); } diff --git a/std/math/isnan.zig b/std/math/isnan.zig index a2cb85b4f7..e8b03a1e34 100644 --- a/std/math/isnan.zig +++ b/std/math/isnan.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn isNan(x: var) bool { @@ -18,6 +18,10 @@ pub fn isNan(x: var) bool { const bits = @bitCast(u64, x); return (bits & (maxInt(u64) >> 1)) > (u64(0x7FF) << 52); }, + f128 => { + const bits = @bitCast(u128, x); + return (bits & (maxInt(u128) >> 1)) > (u128(0x7FFF) << 112); + }, else => { @compileError("isNan not implemented for " ++ @typeName(T)); }, @@ -31,10 +35,12 @@ pub fn isSignalNan(x: var) bool { } test "math.isNan" { - assert(isNan(math.nan(f16))); - assert(isNan(math.nan(f32))); - assert(isNan(math.nan(f64))); - assert(!isNan(f16(1.0))); - assert(!isNan(f32(1.0))); - assert(!isNan(f64(1.0))); + expect(isNan(math.nan(f16))); + 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))); } diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig index cc088e46a0..2c57aea7a9 100644 --- a/std/math/isnormal.zig +++ b/std/math/isnormal.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn isNormal(x: var) bool { @@ -25,13 +25,13 @@ pub fn isNormal(x: var) bool { } test "math.isNormal" { - assert(!isNormal(math.nan(f16))); - assert(!isNormal(math.nan(f32))); - assert(!isNormal(math.nan(f64))); - assert(!isNormal(f16(0))); - assert(!isNormal(f32(0))); - assert(!isNormal(f64(0))); - assert(isNormal(f16(1.0))); - assert(isNormal(f32(1.0))); - assert(isNormal(f64(1.0))); + 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))); } diff --git a/std/math/ln.zig b/std/math/ln.zig index a560fee8ec..257ce8054f 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const TypeId = builtin.TypeId; @@ -143,42 +143,42 @@ pub fn ln_64(x_: f64) f64 { } test "math.ln" { - assert(ln(f32(0.2)) == ln_32(0.2)); - assert(ln(f64(0.2)) == ln_64(0.2)); + expect(ln(f32(0.2)) == ln_32(0.2)); + expect(ln(f64(0.2)) == ln_64(0.2)); } test "math.ln32" { const epsilon = 0.000001; - assert(math.approxEq(f32, ln_32(0.2), -1.609438, epsilon)); - assert(math.approxEq(f32, ln_32(0.8923), -0.113953, epsilon)); - assert(math.approxEq(f32, ln_32(1.5), 0.405465, epsilon)); - assert(math.approxEq(f32, ln_32(37.45), 3.623007, epsilon)); - assert(math.approxEq(f32, ln_32(89.123), 4.490017, epsilon)); - assert(math.approxEq(f32, ln_32(123123.234375), 11.720941, epsilon)); + expect(math.approxEq(f32, ln_32(0.2), -1.609438, epsilon)); + expect(math.approxEq(f32, ln_32(0.8923), -0.113953, epsilon)); + expect(math.approxEq(f32, ln_32(1.5), 0.405465, epsilon)); + expect(math.approxEq(f32, ln_32(37.45), 3.623007, epsilon)); + expect(math.approxEq(f32, ln_32(89.123), 4.490017, epsilon)); + expect(math.approxEq(f32, ln_32(123123.234375), 11.720941, epsilon)); } test "math.ln64" { const epsilon = 0.000001; - assert(math.approxEq(f64, ln_64(0.2), -1.609438, epsilon)); - assert(math.approxEq(f64, ln_64(0.8923), -0.113953, epsilon)); - assert(math.approxEq(f64, ln_64(1.5), 0.405465, epsilon)); - assert(math.approxEq(f64, ln_64(37.45), 3.623007, epsilon)); - assert(math.approxEq(f64, ln_64(89.123), 4.490017, epsilon)); - assert(math.approxEq(f64, ln_64(123123.234375), 11.720941, epsilon)); + expect(math.approxEq(f64, ln_64(0.2), -1.609438, epsilon)); + expect(math.approxEq(f64, ln_64(0.8923), -0.113953, epsilon)); + expect(math.approxEq(f64, ln_64(1.5), 0.405465, epsilon)); + expect(math.approxEq(f64, ln_64(37.45), 3.623007, epsilon)); + expect(math.approxEq(f64, ln_64(89.123), 4.490017, epsilon)); + expect(math.approxEq(f64, ln_64(123123.234375), 11.720941, epsilon)); } test "math.ln32.special" { - assert(math.isPositiveInf(ln_32(math.inf(f32)))); - assert(math.isNegativeInf(ln_32(0.0))); - assert(math.isNan(ln_32(-1.0))); - assert(math.isNan(ln_32(math.nan(f32)))); + expect(math.isPositiveInf(ln_32(math.inf(f32)))); + expect(math.isNegativeInf(ln_32(0.0))); + expect(math.isNan(ln_32(-1.0))); + expect(math.isNan(ln_32(math.nan(f32)))); } test "math.ln64.special" { - assert(math.isPositiveInf(ln_64(math.inf(f64)))); - assert(math.isNegativeInf(ln_64(0.0))); - assert(math.isNan(ln_64(-1.0))); - assert(math.isNan(ln_64(math.nan(f64)))); + expect(math.isPositiveInf(ln_64(math.inf(f64)))); + expect(math.isNegativeInf(ln_64(0.0))); + expect(math.isNan(ln_64(-1.0))); + expect(math.isNan(ln_64(math.nan(f64)))); } diff --git a/std/math/log.zig b/std/math/log.zig index 20b6d055e8..21cffcc078 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -2,7 +2,7 @@ const std = @import("../index.zig"); const math = std.math; const builtin = @import("builtin"); const TypeId = builtin.TypeId; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn log(comptime T: type, base: T, x: T) T { if (base == 2) { @@ -41,25 +41,25 @@ pub fn log(comptime T: type, base: T, x: T) T { } test "math.log integer" { - assert(log(u8, 2, 0x1) == 0); - assert(log(u8, 2, 0x2) == 1); - assert(log(i16, 2, 0x72) == 6); - assert(log(u32, 2, 0xFFFFFF) == 23); - assert(log(u64, 2, 0x7FF0123456789ABC) == 62); + expect(log(u8, 2, 0x1) == 0); + expect(log(u8, 2, 0x2) == 1); + expect(log(i16, 2, 0x72) == 6); + expect(log(u32, 2, 0xFFFFFF) == 23); + expect(log(u64, 2, 0x7FF0123456789ABC) == 62); } test "math.log float" { const epsilon = 0.000001; - assert(math.approxEq(f32, log(f32, 6, 0.23947), -0.797723, epsilon)); - assert(math.approxEq(f32, log(f32, 89, 0.23947), -0.318432, epsilon)); - assert(math.approxEq(f64, log(f64, 123897, 12389216414), 1.981724596, epsilon)); + expect(math.approxEq(f32, log(f32, 6, 0.23947), -0.797723, epsilon)); + expect(math.approxEq(f32, log(f32, 89, 0.23947), -0.318432, epsilon)); + expect(math.approxEq(f64, log(f64, 123897, 12389216414), 1.981724596, epsilon)); } test "math.log float_special" { - assert(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974))); - assert(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974))); + expect(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974))); + expect(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974))); - assert(log(f64, 2, 213.23019799993) == math.log2(f64(213.23019799993))); - assert(log(f64, 10, 213.23019799993) == math.log10(f64(213.23019799993))); + expect(log(f64, 2, 213.23019799993) == math.log2(f64(213.23019799993))); + expect(log(f64, 10, 213.23019799993) == math.log10(f64(213.23019799993))); } diff --git a/std/math/log10.zig b/std/math/log10.zig index 2b53d8a6ae..8055f71280 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const testing = std.testing; const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; @@ -171,42 +171,42 @@ pub fn log10_64(x_: f64) f64 { } test "math.log10" { - assert(log10(f32(0.2)) == log10_32(0.2)); - assert(log10(f64(0.2)) == log10_64(0.2)); + testing.expect(log10(f32(0.2)) == log10_32(0.2)); + testing.expect(log10(f64(0.2)) == log10_64(0.2)); } test "math.log10_32" { const epsilon = 0.000001; - assert(math.approxEq(f32, log10_32(0.2), -0.698970, epsilon)); - assert(math.approxEq(f32, log10_32(0.8923), -0.049489, epsilon)); - assert(math.approxEq(f32, log10_32(1.5), 0.176091, epsilon)); - assert(math.approxEq(f32, log10_32(37.45), 1.573452, epsilon)); - assert(math.approxEq(f32, log10_32(89.123), 1.94999, epsilon)); - assert(math.approxEq(f32, log10_32(123123.234375), 5.09034, epsilon)); + testing.expect(math.approxEq(f32, log10_32(0.2), -0.698970, epsilon)); + testing.expect(math.approxEq(f32, log10_32(0.8923), -0.049489, epsilon)); + testing.expect(math.approxEq(f32, log10_32(1.5), 0.176091, epsilon)); + testing.expect(math.approxEq(f32, log10_32(37.45), 1.573452, epsilon)); + testing.expect(math.approxEq(f32, log10_32(89.123), 1.94999, epsilon)); + testing.expect(math.approxEq(f32, log10_32(123123.234375), 5.09034, epsilon)); } test "math.log10_64" { const epsilon = 0.000001; - assert(math.approxEq(f64, log10_64(0.2), -0.698970, epsilon)); - assert(math.approxEq(f64, log10_64(0.8923), -0.049489, epsilon)); - assert(math.approxEq(f64, log10_64(1.5), 0.176091, epsilon)); - assert(math.approxEq(f64, log10_64(37.45), 1.573452, epsilon)); - assert(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon)); - assert(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon)); + testing.expect(math.approxEq(f64, log10_64(0.2), -0.698970, epsilon)); + testing.expect(math.approxEq(f64, log10_64(0.8923), -0.049489, epsilon)); + testing.expect(math.approxEq(f64, log10_64(1.5), 0.176091, epsilon)); + testing.expect(math.approxEq(f64, log10_64(37.45), 1.573452, epsilon)); + testing.expect(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon)); + testing.expect(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon)); } test "math.log10_32.special" { - assert(math.isPositiveInf(log10_32(math.inf(f32)))); - assert(math.isNegativeInf(log10_32(0.0))); - assert(math.isNan(log10_32(-1.0))); - assert(math.isNan(log10_32(math.nan(f32)))); + testing.expect(math.isPositiveInf(log10_32(math.inf(f32)))); + testing.expect(math.isNegativeInf(log10_32(0.0))); + testing.expect(math.isNan(log10_32(-1.0))); + testing.expect(math.isNan(log10_32(math.nan(f32)))); } test "math.log10_64.special" { - assert(math.isPositiveInf(log10_64(math.inf(f64)))); - assert(math.isNegativeInf(log10_64(0.0))); - assert(math.isNan(log10_64(-1.0))); - assert(math.isNan(log10_64(math.nan(f64)))); + testing.expect(math.isPositiveInf(log10_64(math.inf(f64)))); + testing.expect(math.isNegativeInf(log10_64(0.0))); + testing.expect(math.isNan(log10_64(-1.0))); + testing.expect(math.isNan(log10_64(math.nan(f64)))); } diff --git a/std/math/log1p.zig b/std/math/log1p.zig index 903fceac05..257e7b90d4 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -9,7 +9,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn log1p(x: var) @typeOf(x) { const T = @typeOf(x); @@ -177,48 +177,48 @@ fn log1p_64(x: f64) f64 { } test "math.log1p" { - assert(log1p(f32(0.0)) == log1p_32(0.0)); - assert(log1p(f64(0.0)) == log1p_64(0.0)); + expect(log1p(f32(0.0)) == log1p_32(0.0)); + expect(log1p(f64(0.0)) == log1p_64(0.0)); } test "math.log1p_32" { const epsilon = 0.000001; - assert(math.approxEq(f32, log1p_32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, log1p_32(0.2), 0.182322, epsilon)); - assert(math.approxEq(f32, log1p_32(0.8923), 0.637793, epsilon)); - assert(math.approxEq(f32, log1p_32(1.5), 0.916291, epsilon)); - assert(math.approxEq(f32, log1p_32(37.45), 3.649359, epsilon)); - assert(math.approxEq(f32, log1p_32(89.123), 4.501175, epsilon)); - assert(math.approxEq(f32, log1p_32(123123.234375), 11.720949, epsilon)); + expect(math.approxEq(f32, log1p_32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, log1p_32(0.2), 0.182322, epsilon)); + expect(math.approxEq(f32, log1p_32(0.8923), 0.637793, epsilon)); + expect(math.approxEq(f32, log1p_32(1.5), 0.916291, epsilon)); + expect(math.approxEq(f32, log1p_32(37.45), 3.649359, epsilon)); + expect(math.approxEq(f32, log1p_32(89.123), 4.501175, epsilon)); + expect(math.approxEq(f32, log1p_32(123123.234375), 11.720949, epsilon)); } test "math.log1p_64" { const epsilon = 0.000001; - assert(math.approxEq(f64, log1p_64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, log1p_64(0.2), 0.182322, epsilon)); - assert(math.approxEq(f64, log1p_64(0.8923), 0.637793, epsilon)); - assert(math.approxEq(f64, log1p_64(1.5), 0.916291, epsilon)); - assert(math.approxEq(f64, log1p_64(37.45), 3.649359, epsilon)); - assert(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon)); - assert(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon)); + expect(math.approxEq(f64, log1p_64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, log1p_64(0.2), 0.182322, epsilon)); + expect(math.approxEq(f64, log1p_64(0.8923), 0.637793, epsilon)); + expect(math.approxEq(f64, log1p_64(1.5), 0.916291, epsilon)); + expect(math.approxEq(f64, log1p_64(37.45), 3.649359, epsilon)); + expect(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon)); + expect(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon)); } test "math.log1p_32.special" { - assert(math.isPositiveInf(log1p_32(math.inf(f32)))); - assert(log1p_32(0.0) == 0.0); - assert(log1p_32(-0.0) == -0.0); - assert(math.isNegativeInf(log1p_32(-1.0))); - assert(math.isNan(log1p_32(-2.0))); - assert(math.isNan(log1p_32(math.nan(f32)))); + expect(math.isPositiveInf(log1p_32(math.inf(f32)))); + expect(log1p_32(0.0) == 0.0); + expect(log1p_32(-0.0) == -0.0); + expect(math.isNegativeInf(log1p_32(-1.0))); + expect(math.isNan(log1p_32(-2.0))); + expect(math.isNan(log1p_32(math.nan(f32)))); } test "math.log1p_64.special" { - assert(math.isPositiveInf(log1p_64(math.inf(f64)))); - assert(log1p_64(0.0) == 0.0); - assert(log1p_64(-0.0) == -0.0); - assert(math.isNegativeInf(log1p_64(-1.0))); - assert(math.isNan(log1p_64(-2.0))); - assert(math.isNan(log1p_64(math.nan(f64)))); + expect(math.isPositiveInf(log1p_64(math.inf(f64)))); + expect(log1p_64(0.0) == 0.0); + expect(log1p_64(-0.0) == -0.0); + expect(math.isNegativeInf(log1p_64(-1.0))); + expect(math.isNan(log1p_64(-2.0))); + expect(math.isNan(log1p_64(math.nan(f64)))); } diff --git a/std/math/log2.zig b/std/math/log2.zig index 555c0bdf18..1bb51bf9f9 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; @@ -169,40 +169,40 @@ pub fn log2_64(x_: f64) f64 { } test "math.log2" { - assert(log2(f32(0.2)) == log2_32(0.2)); - assert(log2(f64(0.2)) == log2_64(0.2)); + expect(log2(f32(0.2)) == log2_32(0.2)); + expect(log2(f64(0.2)) == log2_64(0.2)); } test "math.log2_32" { const epsilon = 0.000001; - assert(math.approxEq(f32, log2_32(0.2), -2.321928, epsilon)); - assert(math.approxEq(f32, log2_32(0.8923), -0.164399, epsilon)); - assert(math.approxEq(f32, log2_32(1.5), 0.584962, epsilon)); - assert(math.approxEq(f32, log2_32(37.45), 5.226894, epsilon)); - assert(math.approxEq(f32, log2_32(123123.234375), 16.909744, epsilon)); + expect(math.approxEq(f32, log2_32(0.2), -2.321928, epsilon)); + expect(math.approxEq(f32, log2_32(0.8923), -0.164399, epsilon)); + expect(math.approxEq(f32, log2_32(1.5), 0.584962, epsilon)); + expect(math.approxEq(f32, log2_32(37.45), 5.226894, epsilon)); + expect(math.approxEq(f32, log2_32(123123.234375), 16.909744, epsilon)); } test "math.log2_64" { const epsilon = 0.000001; - assert(math.approxEq(f64, log2_64(0.2), -2.321928, epsilon)); - assert(math.approxEq(f64, log2_64(0.8923), -0.164399, epsilon)); - assert(math.approxEq(f64, log2_64(1.5), 0.584962, epsilon)); - assert(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon)); - assert(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon)); + expect(math.approxEq(f64, log2_64(0.2), -2.321928, epsilon)); + expect(math.approxEq(f64, log2_64(0.8923), -0.164399, epsilon)); + expect(math.approxEq(f64, log2_64(1.5), 0.584962, epsilon)); + expect(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon)); + expect(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon)); } test "math.log2_32.special" { - assert(math.isPositiveInf(log2_32(math.inf(f32)))); - assert(math.isNegativeInf(log2_32(0.0))); - assert(math.isNan(log2_32(-1.0))); - assert(math.isNan(log2_32(math.nan(f32)))); + expect(math.isPositiveInf(log2_32(math.inf(f32)))); + expect(math.isNegativeInf(log2_32(0.0))); + expect(math.isNan(log2_32(-1.0))); + expect(math.isNan(log2_32(math.nan(f32)))); } test "math.log2_64.special" { - assert(math.isPositiveInf(log2_64(math.inf(f64)))); - assert(math.isNegativeInf(log2_64(0.0))); - assert(math.isNan(log2_64(-1.0))); - assert(math.isNan(log2_64(math.nan(f64)))); + expect(math.isPositiveInf(log2_64(math.inf(f64)))); + expect(math.isNegativeInf(log2_64(0.0))); + expect(math.isNan(log2_64(-1.0))); + expect(math.isNan(log2_64(math.nan(f64)))); } diff --git a/std/math/modf.zig b/std/math/modf.zig index 0f619f25bc..2dadda76a9 100644 --- a/std/math/modf.zig +++ b/std/math/modf.zig @@ -5,7 +5,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; fn modf_result(comptime T: type) type { @@ -119,11 +119,11 @@ test "math.modf" { const a = modf(f32(1.0)); const b = modf32(1.0); // NOTE: No struct comparison on generic return type function? non-named, makes sense, but still. - assert(a.ipart == b.ipart and a.fpart == b.fpart); + expect(a.ipart == b.ipart and a.fpart == b.fpart); const c = modf(f64(1.0)); const d = modf64(1.0); - assert(a.ipart == b.ipart and a.fpart == b.fpart); + expect(a.ipart == b.ipart and a.fpart == b.fpart); } test "math.modf32" { @@ -131,24 +131,24 @@ test "math.modf32" { var r: modf32_result = undefined; r = modf32(1.0); - assert(math.approxEq(f32, r.ipart, 1.0, epsilon)); - assert(math.approxEq(f32, r.fpart, 0.0, epsilon)); + expect(math.approxEq(f32, r.ipart, 1.0, epsilon)); + expect(math.approxEq(f32, r.fpart, 0.0, epsilon)); r = modf32(2.545); - assert(math.approxEq(f32, r.ipart, 2.0, epsilon)); - assert(math.approxEq(f32, r.fpart, 0.545, epsilon)); + expect(math.approxEq(f32, r.ipart, 2.0, epsilon)); + expect(math.approxEq(f32, r.fpart, 0.545, epsilon)); r = modf32(3.978123); - assert(math.approxEq(f32, r.ipart, 3.0, epsilon)); - assert(math.approxEq(f32, r.fpart, 0.978123, epsilon)); + expect(math.approxEq(f32, r.ipart, 3.0, epsilon)); + expect(math.approxEq(f32, r.fpart, 0.978123, epsilon)); r = modf32(43874.3); - assert(math.approxEq(f32, r.ipart, 43874, epsilon)); - assert(math.approxEq(f32, r.fpart, 0.300781, epsilon)); + expect(math.approxEq(f32, r.ipart, 43874, epsilon)); + expect(math.approxEq(f32, r.fpart, 0.300781, epsilon)); r = modf32(1234.340780); - assert(math.approxEq(f32, r.ipart, 1234, epsilon)); - assert(math.approxEq(f32, r.fpart, 0.340820, epsilon)); + expect(math.approxEq(f32, r.ipart, 1234, epsilon)); + expect(math.approxEq(f32, r.fpart, 0.340820, epsilon)); } test "math.modf64" { @@ -156,48 +156,48 @@ test "math.modf64" { var r: modf64_result = undefined; r = modf64(1.0); - assert(math.approxEq(f64, r.ipart, 1.0, epsilon)); - assert(math.approxEq(f64, r.fpart, 0.0, epsilon)); + expect(math.approxEq(f64, r.ipart, 1.0, epsilon)); + expect(math.approxEq(f64, r.fpart, 0.0, epsilon)); r = modf64(2.545); - assert(math.approxEq(f64, r.ipart, 2.0, epsilon)); - assert(math.approxEq(f64, r.fpart, 0.545, epsilon)); + expect(math.approxEq(f64, r.ipart, 2.0, epsilon)); + expect(math.approxEq(f64, r.fpart, 0.545, epsilon)); r = modf64(3.978123); - assert(math.approxEq(f64, r.ipart, 3.0, epsilon)); - assert(math.approxEq(f64, r.fpart, 0.978123, epsilon)); + expect(math.approxEq(f64, r.ipart, 3.0, epsilon)); + expect(math.approxEq(f64, r.fpart, 0.978123, epsilon)); r = modf64(43874.3); - assert(math.approxEq(f64, r.ipart, 43874, epsilon)); - assert(math.approxEq(f64, r.fpart, 0.3, epsilon)); + expect(math.approxEq(f64, r.ipart, 43874, epsilon)); + expect(math.approxEq(f64, r.fpart, 0.3, epsilon)); r = modf64(1234.340780); - assert(math.approxEq(f64, r.ipart, 1234, epsilon)); - assert(math.approxEq(f64, r.fpart, 0.340780, epsilon)); + expect(math.approxEq(f64, r.ipart, 1234, epsilon)); + expect(math.approxEq(f64, r.fpart, 0.340780, epsilon)); } test "math.modf32.special" { var r: modf32_result = undefined; r = modf32(math.inf(f32)); - assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); + expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); r = modf32(-math.inf(f32)); - assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); + expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); r = modf32(math.nan(f32)); - assert(math.isNan(r.ipart) and math.isNan(r.fpart)); + expect(math.isNan(r.ipart) and math.isNan(r.fpart)); } test "math.modf64.special" { var r: modf64_result = undefined; r = modf64(math.inf(f64)); - assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); + expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); r = modf64(-math.inf(f64)); - assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); + expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); r = modf64(math.nan(f64)); - assert(math.isNan(r.ipart) and math.isNan(r.fpart)); + expect(math.isNan(r.ipart) and math.isNan(r.fpart)); } diff --git a/std/math/nan.zig b/std/math/nan.zig index 2cbcbee81b..d3ad43da23 100644 --- a/std/math/nan.zig +++ b/std/math/nan.zig @@ -2,9 +2,10 @@ const math = @import("index.zig"); pub fn nan(comptime T: type) T { return switch (T) { - f16 => @bitCast(f16, math.nan_u16), - f32 => @bitCast(f32, math.nan_u32), - f64 => @bitCast(f64, math.nan_u64), + f16 => math.nan_f16, + f32 => math.nan_f32, + f64 => math.nan_f64, + f128 => math.nan_f128, else => @compileError("nan not implemented for " ++ @typeName(T)), }; } diff --git a/std/math/pow.zig b/std/math/pow.zig index 39a2bfa9f7..f037f66d7e 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -24,7 +24,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; // This implementation is taken from the go stlib, musl is a bit more complex. pub fn pow(comptime T: type, x: T, y: T) T { @@ -179,56 +179,56 @@ fn isOddInteger(x: f64) bool { test "math.pow" { const epsilon = 0.000001; - assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); - assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon)); - assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon)); - assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon)); - assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon)); - assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon)); + expect(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); + expect(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon)); + expect(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon)); + expect(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon)); + expect(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon)); + expect(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon)); - assert(math.approxEq(f64, pow(f64, 0.0, 3.3), 0.0, epsilon)); - assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon)); - assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon)); - assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon)); - assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon)); - assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon)); + expect(math.approxEq(f64, pow(f64, 0.0, 3.3), 0.0, epsilon)); + expect(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon)); + expect(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon)); + expect(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon)); + expect(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon)); + expect(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon)); } test "math.pow.special" { const epsilon = 0.000001; - assert(pow(f32, 4, 0.0) == 1.0); - assert(pow(f32, 7, -0.0) == 1.0); - assert(pow(f32, 45, 1.0) == 45); - assert(pow(f32, -45, 1.0) == -45); - assert(math.isNan(pow(f32, math.nan(f32), 5.0))); - assert(math.isNan(pow(f32, 5.0, math.nan(f32)))); - assert(math.isPositiveInf(pow(f32, 0.0, -1.0))); - //assert(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required? - assert(math.isPositiveInf(pow(f32, 0.0, -math.inf(f32)))); - assert(math.isPositiveInf(pow(f32, -0.0, -math.inf(f32)))); - assert(pow(f32, 0.0, math.inf(f32)) == 0.0); - assert(pow(f32, -0.0, math.inf(f32)) == 0.0); - assert(math.isPositiveInf(pow(f32, 0.0, -2.0))); - assert(math.isPositiveInf(pow(f32, -0.0, -2.0))); - assert(pow(f32, 0.0, 1.0) == 0.0); - assert(pow(f32, -0.0, 1.0) == -0.0); - assert(pow(f32, 0.0, 2.0) == 0.0); - assert(pow(f32, -0.0, 2.0) == 0.0); - assert(math.approxEq(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon)); - assert(math.approxEq(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon)); - assert(math.isPositiveInf(pow(f32, 1.2, math.inf(f32)))); - assert(math.isPositiveInf(pow(f32, -1.2, math.inf(f32)))); - assert(pow(f32, 1.2, -math.inf(f32)) == 0.0); - assert(pow(f32, -1.2, -math.inf(f32)) == 0.0); - assert(pow(f32, 0.2, math.inf(f32)) == 0.0); - assert(pow(f32, -0.2, math.inf(f32)) == 0.0); - assert(math.isPositiveInf(pow(f32, 0.2, -math.inf(f32)))); - assert(math.isPositiveInf(pow(f32, -0.2, -math.inf(f32)))); - assert(math.isPositiveInf(pow(f32, math.inf(f32), 1.0))); - assert(pow(f32, math.inf(f32), -1.0) == 0.0); - //assert(pow(f32, -math.inf(f32), 5.0) == pow(f32, -0.0, -5.0)); TODO support negative 0? - assert(pow(f32, -math.inf(f32), -5.2) == pow(f32, -0.0, 5.2)); - assert(math.isNan(pow(f32, -1.0, 1.2))); - assert(math.isNan(pow(f32, -12.4, 78.5))); + expect(pow(f32, 4, 0.0) == 1.0); + expect(pow(f32, 7, -0.0) == 1.0); + expect(pow(f32, 45, 1.0) == 45); + expect(pow(f32, -45, 1.0) == -45); + expect(math.isNan(pow(f32, math.nan(f32), 5.0))); + expect(math.isNan(pow(f32, 5.0, math.nan(f32)))); + expect(math.isPositiveInf(pow(f32, 0.0, -1.0))); + //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required? + expect(math.isPositiveInf(pow(f32, 0.0, -math.inf(f32)))); + expect(math.isPositiveInf(pow(f32, -0.0, -math.inf(f32)))); + expect(pow(f32, 0.0, math.inf(f32)) == 0.0); + expect(pow(f32, -0.0, math.inf(f32)) == 0.0); + expect(math.isPositiveInf(pow(f32, 0.0, -2.0))); + expect(math.isPositiveInf(pow(f32, -0.0, -2.0))); + expect(pow(f32, 0.0, 1.0) == 0.0); + expect(pow(f32, -0.0, 1.0) == -0.0); + expect(pow(f32, 0.0, 2.0) == 0.0); + expect(pow(f32, -0.0, 2.0) == 0.0); + expect(math.approxEq(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon)); + expect(math.approxEq(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon)); + expect(math.isPositiveInf(pow(f32, 1.2, math.inf(f32)))); + expect(math.isPositiveInf(pow(f32, -1.2, math.inf(f32)))); + expect(pow(f32, 1.2, -math.inf(f32)) == 0.0); + expect(pow(f32, -1.2, -math.inf(f32)) == 0.0); + expect(pow(f32, 0.2, math.inf(f32)) == 0.0); + expect(pow(f32, -0.2, math.inf(f32)) == 0.0); + expect(math.isPositiveInf(pow(f32, 0.2, -math.inf(f32)))); + expect(math.isPositiveInf(pow(f32, -0.2, -math.inf(f32)))); + expect(math.isPositiveInf(pow(f32, math.inf(f32), 1.0))); + expect(pow(f32, math.inf(f32), -1.0) == 0.0); + //expect(pow(f32, -math.inf(f32), 5.0) == pow(f32, -0.0, -5.0)); TODO support negative 0? + expect(pow(f32, -math.inf(f32), -5.2) == pow(f32, -0.0, 5.2)); + expect(math.isNan(pow(f32, -1.0, 1.2))); + expect(math.isNan(pow(f32, -12.4, 78.5))); } diff --git a/std/math/powi.zig b/std/math/powi.zig index 13c09b192e..9c2a4a4965 100644 --- a/std/math/powi.zig +++ b/std/math/powi.zig @@ -12,7 +12,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; const assert = std.debug.assert; -const assertError = std.debug.assertError; +const testing = std.testing; // This implementation is based on that from the rust stlib pub fn powi(comptime T: type, x: T, y: T) (error{ @@ -103,75 +103,75 @@ pub fn powi(comptime T: type, x: T, y: T) (error{ } test "math.powi" { - assertError(powi(i8, -66, 6), error.Underflow); - assertError(powi(i16, -13, 13), error.Underflow); - assertError(powi(i32, -32, 21), error.Underflow); - assertError(powi(i64, -24, 61), error.Underflow); - assertError(powi(i17, -15, 15), error.Underflow); - assertError(powi(i42, -6, 40), error.Underflow); + testing.expectError(error.Underflow, powi(i8, -66, 6)); + testing.expectError(error.Underflow, powi(i16, -13, 13)); + testing.expectError(error.Underflow, powi(i32, -32, 21)); + testing.expectError(error.Underflow, powi(i64, -24, 61)); + testing.expectError(error.Underflow, powi(i17, -15, 15)); + testing.expectError(error.Underflow, powi(i42, -6, 40)); - assert((try powi(i8, -5, 3)) == -125); - assert((try powi(i16, -16, 3)) == -4096); - assert((try powi(i32, -91, 3)) == -753571); - assert((try powi(i64, -36, 6)) == 2176782336); - assert((try powi(i17, -2, 15)) == -32768); - assert((try powi(i42, -5, 7)) == -78125); + testing.expect((try powi(i8, -5, 3)) == -125); + testing.expect((try powi(i16, -16, 3)) == -4096); + testing.expect((try powi(i32, -91, 3)) == -753571); + testing.expect((try powi(i64, -36, 6)) == 2176782336); + testing.expect((try powi(i17, -2, 15)) == -32768); + testing.expect((try powi(i42, -5, 7)) == -78125); - assert((try powi(u8, 6, 2)) == 36); - assert((try powi(u16, 5, 4)) == 625); - assert((try powi(u32, 12, 6)) == 2985984); - assert((try powi(u64, 34, 2)) == 1156); - assert((try powi(u17, 16, 3)) == 4096); - assert((try powi(u42, 34, 6)) == 1544804416); + testing.expect((try powi(u8, 6, 2)) == 36); + testing.expect((try powi(u16, 5, 4)) == 625); + testing.expect((try powi(u32, 12, 6)) == 2985984); + testing.expect((try powi(u64, 34, 2)) == 1156); + testing.expect((try powi(u17, 16, 3)) == 4096); + testing.expect((try powi(u42, 34, 6)) == 1544804416); - assertError(powi(i8, 120, 7), error.Overflow); - assertError(powi(i16, 73, 15), error.Overflow); - assertError(powi(i32, 23, 31), error.Overflow); - assertError(powi(i64, 68, 61), error.Overflow); - assertError(powi(i17, 15, 15), error.Overflow); - assertError(powi(i42, 121312, 41), error.Overflow); + testing.expectError(error.Overflow, powi(i8, 120, 7)); + testing.expectError(error.Overflow, powi(i16, 73, 15)); + testing.expectError(error.Overflow, powi(i32, 23, 31)); + testing.expectError(error.Overflow, powi(i64, 68, 61)); + testing.expectError(error.Overflow, powi(i17, 15, 15)); + testing.expectError(error.Overflow, powi(i42, 121312, 41)); - assertError(powi(u8, 123, 7), error.Overflow); - assertError(powi(u16, 2313, 15), error.Overflow); - assertError(powi(u32, 8968, 31), error.Overflow); - assertError(powi(u64, 2342, 63), error.Overflow); - assertError(powi(u17, 2723, 16), error.Overflow); - assertError(powi(u42, 8234, 41), error.Overflow); + testing.expectError(error.Overflow, powi(u8, 123, 7)); + testing.expectError(error.Overflow, powi(u16, 2313, 15)); + testing.expectError(error.Overflow, powi(u32, 8968, 31)); + testing.expectError(error.Overflow, powi(u64, 2342, 63)); + testing.expectError(error.Overflow, powi(u17, 2723, 16)); + testing.expectError(error.Overflow, powi(u42, 8234, 41)); } test "math.powi.special" { - assertError(powi(i8, -2, 8), error.Underflow); - assertError(powi(i16, -2, 16), error.Underflow); - assertError(powi(i32, -2, 32), error.Underflow); - assertError(powi(i64, -2, 64), error.Underflow); - assertError(powi(i17, -2, 17), error.Underflow); - assertError(powi(i42, -2, 42), error.Underflow); + testing.expectError(error.Underflow, powi(i8, -2, 8)); + testing.expectError(error.Underflow, powi(i16, -2, 16)); + testing.expectError(error.Underflow, powi(i32, -2, 32)); + testing.expectError(error.Underflow, powi(i64, -2, 64)); + testing.expectError(error.Underflow, powi(i17, -2, 17)); + testing.expectError(error.Underflow, powi(i42, -2, 42)); - assert((try powi(i8, -1, 3)) == -1); - assert((try powi(i16, -1, 2)) == 1); - assert((try powi(i32, -1, 16)) == 1); - assert((try powi(i64, -1, 6)) == 1); - assert((try powi(i17, -1, 15)) == -1); - assert((try powi(i42, -1, 7)) == -1); + testing.expect((try powi(i8, -1, 3)) == -1); + testing.expect((try powi(i16, -1, 2)) == 1); + testing.expect((try powi(i32, -1, 16)) == 1); + testing.expect((try powi(i64, -1, 6)) == 1); + testing.expect((try powi(i17, -1, 15)) == -1); + testing.expect((try powi(i42, -1, 7)) == -1); - assert((try powi(u8, 1, 2)) == 1); - assert((try powi(u16, 1, 4)) == 1); - assert((try powi(u32, 1, 6)) == 1); - assert((try powi(u64, 1, 2)) == 1); - assert((try powi(u17, 1, 3)) == 1); - assert((try powi(u42, 1, 6)) == 1); + testing.expect((try powi(u8, 1, 2)) == 1); + testing.expect((try powi(u16, 1, 4)) == 1); + testing.expect((try powi(u32, 1, 6)) == 1); + testing.expect((try powi(u64, 1, 2)) == 1); + testing.expect((try powi(u17, 1, 3)) == 1); + testing.expect((try powi(u42, 1, 6)) == 1); - assertError(powi(i8, 2, 7), error.Overflow); - assertError(powi(i16, 2, 15), error.Overflow); - assertError(powi(i32, 2, 31), error.Overflow); - assertError(powi(i64, 2, 63), error.Overflow); - assertError(powi(i17, 2, 16), error.Overflow); - assertError(powi(i42, 2, 41), error.Overflow); + testing.expectError(error.Overflow, powi(i8, 2, 7)); + testing.expectError(error.Overflow, powi(i16, 2, 15)); + testing.expectError(error.Overflow, powi(i32, 2, 31)); + testing.expectError(error.Overflow, powi(i64, 2, 63)); + testing.expectError(error.Overflow, powi(i17, 2, 16)); + testing.expectError(error.Overflow, powi(i42, 2, 41)); - assertError(powi(u8, 2, 8), error.Overflow); - assertError(powi(u16, 2, 16), error.Overflow); - assertError(powi(u32, 2, 32), error.Overflow); - assertError(powi(u64, 2, 64), error.Overflow); - assertError(powi(u17, 2, 17), error.Overflow); - assertError(powi(u42, 2, 42), error.Overflow); + testing.expectError(error.Overflow, powi(u8, 2, 8)); + testing.expectError(error.Overflow, powi(u16, 2, 16)); + testing.expectError(error.Overflow, powi(u32, 2, 32)); + testing.expectError(error.Overflow, powi(u64, 2, 64)); + testing.expectError(error.Overflow, powi(u17, 2, 17)); + testing.expectError(error.Overflow, powi(u42, 2, 42)); } diff --git a/std/math/round.zig b/std/math/round.zig index 4fe35365c8..7346b703c9 100644 --- a/std/math/round.zig +++ b/std/math/round.zig @@ -5,7 +5,7 @@ // - round(nan) = nan const builtin = @import("builtin"); -const assert = std.debug.assert; +const expect = std.testing.expect; const std = @import("../index.zig"); const math = std.math; @@ -85,36 +85,36 @@ fn round64(x_: f64) f64 { } test "math.round" { - assert(round(f32(1.3)) == round32(1.3)); - assert(round(f64(1.3)) == round64(1.3)); + expect(round(f32(1.3)) == round32(1.3)); + expect(round(f64(1.3)) == round64(1.3)); } test "math.round32" { - assert(round32(1.3) == 1.0); - assert(round32(-1.3) == -1.0); - assert(round32(0.2) == 0.0); - assert(round32(1.8) == 2.0); + expect(round32(1.3) == 1.0); + expect(round32(-1.3) == -1.0); + expect(round32(0.2) == 0.0); + expect(round32(1.8) == 2.0); } test "math.round64" { - assert(round64(1.3) == 1.0); - assert(round64(-1.3) == -1.0); - assert(round64(0.2) == 0.0); - assert(round64(1.8) == 2.0); + expect(round64(1.3) == 1.0); + expect(round64(-1.3) == -1.0); + expect(round64(0.2) == 0.0); + expect(round64(1.8) == 2.0); } test "math.round32.special" { - assert(round32(0.0) == 0.0); - assert(round32(-0.0) == -0.0); - assert(math.isPositiveInf(round32(math.inf(f32)))); - assert(math.isNegativeInf(round32(-math.inf(f32)))); - assert(math.isNan(round32(math.nan(f32)))); + expect(round32(0.0) == 0.0); + expect(round32(-0.0) == -0.0); + expect(math.isPositiveInf(round32(math.inf(f32)))); + expect(math.isNegativeInf(round32(-math.inf(f32)))); + expect(math.isNan(round32(math.nan(f32)))); } test "math.round64.special" { - assert(round64(0.0) == 0.0); - assert(round64(-0.0) == -0.0); - assert(math.isPositiveInf(round64(math.inf(f64)))); - assert(math.isNegativeInf(round64(-math.inf(f64)))); - assert(math.isNan(round64(math.nan(f64)))); + expect(round64(0.0) == 0.0); + expect(round64(-0.0) == -0.0); + expect(math.isPositiveInf(round64(math.inf(f64)))); + expect(math.isNegativeInf(round64(-math.inf(f64)))); + expect(math.isNan(round64(math.nan(f64)))); } diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index f72c7e866f..d37a8659a9 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn scalbn(x: var, n: i32) @typeOf(x) { const T = @typeOf(x); @@ -72,14 +72,14 @@ fn scalbn64(x: f64, n_: i32) f64 { } test "math.scalbn" { - assert(scalbn(f32(1.5), 4) == scalbn32(1.5, 4)); - assert(scalbn(f64(1.5), 4) == scalbn64(1.5, 4)); + expect(scalbn(f32(1.5), 4) == scalbn32(1.5, 4)); + expect(scalbn(f64(1.5), 4) == scalbn64(1.5, 4)); } test "math.scalbn32" { - assert(scalbn32(1.5, 4) == 24.0); + expect(scalbn32(1.5, 4) == 24.0); } test "math.scalbn64" { - assert(scalbn64(1.5, 4) == 24.0); + expect(scalbn64(1.5, 4) == 24.0); } diff --git a/std/math/signbit.zig b/std/math/signbit.zig index 8c6829dfcd..728f651aec 100644 --- a/std/math/signbit.zig +++ b/std/math/signbit.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn signbit(x: var) bool { const T = @typeOf(x); @@ -28,22 +28,22 @@ fn signbit64(x: f64) bool { } test "math.signbit" { - assert(signbit(f16(4.0)) == signbit16(4.0)); - assert(signbit(f32(4.0)) == signbit32(4.0)); - assert(signbit(f64(4.0)) == signbit64(4.0)); + expect(signbit(f16(4.0)) == signbit16(4.0)); + expect(signbit(f32(4.0)) == signbit32(4.0)); + expect(signbit(f64(4.0)) == signbit64(4.0)); } test "math.signbit16" { - assert(!signbit16(4.0)); - assert(signbit16(-3.0)); + expect(!signbit16(4.0)); + expect(signbit16(-3.0)); } test "math.signbit32" { - assert(!signbit32(4.0)); - assert(signbit32(-3.0)); + expect(!signbit32(4.0)); + expect(signbit32(-3.0)); } test "math.signbit64" { - assert(!signbit64(4.0)); - assert(signbit64(-3.0)); + expect(!signbit64(4.0)); + expect(signbit64(-3.0)); } diff --git a/std/math/sin.zig b/std/math/sin.zig index 15b2f9f17a..5ade6636c7 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn sin(x: var) @typeOf(x) { const T = @typeOf(x); @@ -142,45 +142,45 @@ fn sin64(x_: f64) f64 { } test "math.sin" { - assert(sin(f32(0.0)) == sin32(0.0)); - assert(sin(f64(0.0)) == sin64(0.0)); - assert(comptime (math.sin(f64(2))) == math.sin(f64(2))); + expect(sin(f32(0.0)) == sin32(0.0)); + expect(sin(f64(0.0)) == sin64(0.0)); + expect(comptime (math.sin(f64(2))) == math.sin(f64(2))); } test "math.sin32" { const epsilon = 0.000001; - assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); - assert(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); - assert(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); - assert(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); - assert(math.approxEq(f32, sin32(89.123), 0.916166, epsilon)); + expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); + expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); + expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); + expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); + expect(math.approxEq(f32, sin32(89.123), 0.916166, epsilon)); } test "math.sin64" { const epsilon = 0.000001; - assert(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); - assert(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); - assert(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); - assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); - assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); + expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); + expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); + expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); + expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); + expect(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); } test "math.sin32.special" { - assert(sin32(0.0) == 0.0); - assert(sin32(-0.0) == -0.0); - assert(math.isNan(sin32(math.inf(f32)))); - assert(math.isNan(sin32(-math.inf(f32)))); - assert(math.isNan(sin32(math.nan(f32)))); + expect(sin32(0.0) == 0.0); + expect(sin32(-0.0) == -0.0); + expect(math.isNan(sin32(math.inf(f32)))); + expect(math.isNan(sin32(-math.inf(f32)))); + expect(math.isNan(sin32(math.nan(f32)))); } test "math.sin64.special" { - assert(sin64(0.0) == 0.0); - assert(sin64(-0.0) == -0.0); - assert(math.isNan(sin64(math.inf(f64)))); - assert(math.isNan(sin64(-math.inf(f64)))); - assert(math.isNan(sin64(math.nan(f64)))); + expect(sin64(0.0) == 0.0); + expect(sin64(-0.0) == -0.0); + expect(math.isNan(sin64(math.inf(f64)))); + expect(math.isNan(sin64(-math.inf(f64)))); + expect(math.isNan(sin64(math.nan(f64)))); } diff --git a/std/math/sinh.zig b/std/math/sinh.zig index 733b89754a..95924ba55a 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; const maxInt = std.math.maxInt; @@ -87,40 +87,40 @@ fn sinh64(x: f64) f64 { } test "math.sinh" { - assert(sinh(f32(1.5)) == sinh32(1.5)); - assert(sinh(f64(1.5)) == sinh64(1.5)); + expect(sinh(f32(1.5)) == sinh32(1.5)); + expect(sinh(f64(1.5)) == sinh64(1.5)); } test "math.sinh32" { const epsilon = 0.000001; - assert(math.approxEq(f32, sinh32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, sinh32(0.2), 0.201336, epsilon)); - assert(math.approxEq(f32, sinh32(0.8923), 1.015512, epsilon)); - assert(math.approxEq(f32, sinh32(1.5), 2.129279, epsilon)); + expect(math.approxEq(f32, sinh32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, sinh32(0.2), 0.201336, epsilon)); + expect(math.approxEq(f32, sinh32(0.8923), 1.015512, epsilon)); + expect(math.approxEq(f32, sinh32(1.5), 2.129279, epsilon)); } test "math.sinh64" { const epsilon = 0.000001; - assert(math.approxEq(f64, sinh64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, sinh64(0.2), 0.201336, epsilon)); - assert(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon)); - assert(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon)); + expect(math.approxEq(f64, sinh64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, sinh64(0.2), 0.201336, epsilon)); + expect(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon)); + expect(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon)); } test "math.sinh32.special" { - assert(sinh32(0.0) == 0.0); - assert(sinh32(-0.0) == -0.0); - assert(math.isPositiveInf(sinh32(math.inf(f32)))); - assert(math.isNegativeInf(sinh32(-math.inf(f32)))); - assert(math.isNan(sinh32(math.nan(f32)))); + expect(sinh32(0.0) == 0.0); + expect(sinh32(-0.0) == -0.0); + expect(math.isPositiveInf(sinh32(math.inf(f32)))); + expect(math.isNegativeInf(sinh32(-math.inf(f32)))); + expect(math.isNan(sinh32(math.nan(f32)))); } test "math.sinh64.special" { - assert(sinh64(0.0) == 0.0); - assert(sinh64(-0.0) == -0.0); - assert(math.isPositiveInf(sinh64(math.inf(f64)))); - assert(math.isNegativeInf(sinh64(-math.inf(f64)))); - assert(math.isNan(sinh64(math.nan(f64)))); + expect(sinh64(0.0) == 0.0); + expect(sinh64(-0.0) == -0.0); + expect(math.isPositiveInf(sinh64(math.inf(f64)))); + expect(math.isNegativeInf(sinh64(-math.inf(f64)))); + expect(math.isNan(sinh64(math.nan(f64)))); } diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 4300f20f5a..9996b44b20 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; @@ -32,75 +32,75 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ } test "math.sqrt" { - assert(sqrt(f16(0.0)) == @sqrt(f16, 0.0)); - assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0)); - assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0)); + 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; - assert(@sqrt(f16, 0.0) == 0.0); - assert(math.approxEq(f16, @sqrt(f16, 2.0), 1.414214, epsilon)); - assert(math.approxEq(f16, @sqrt(f16, 3.6), 1.897367, epsilon)); - assert(@sqrt(f16, 4.0) == 2.0); - assert(math.approxEq(f16, @sqrt(f16, 7.539840), 2.745877, epsilon)); - assert(math.approxEq(f16, @sqrt(f16, 19.230934), 4.385309, epsilon)); - assert(@sqrt(f16, 64.0) == 8.0); - assert(math.approxEq(f16, @sqrt(f16, 64.1), 8.006248, epsilon)); - assert(math.approxEq(f16, @sqrt(f16, 8942.230469), 94.563370, epsilon)); + 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; - assert(@sqrt(f32, 0.0) == 0.0); - assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon)); - assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon)); - assert(@sqrt(f32, 4.0) == 2.0); - assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon)); - assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon)); - assert(@sqrt(f32, 64.0) == 8.0); - assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon)); - assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon)); + 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; - assert(@sqrt(f64, 0.0) == 0.0); - assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon)); - assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon)); - assert(@sqrt(f64, 4.0) == 2.0); - assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon)); - assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon)); - assert(@sqrt(f64, 64.0) == 8.0); - assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon)); - assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon)); + 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" { - assert(math.isPositiveInf(@sqrt(f16, math.inf(f16)))); - assert(@sqrt(f16, 0.0) == 0.0); - assert(@sqrt(f16, -0.0) == -0.0); - assert(math.isNan(@sqrt(f16, -1.0))); - assert(math.isNan(@sqrt(f16, math.nan(f16)))); + 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" { - assert(math.isPositiveInf(@sqrt(f32, math.inf(f32)))); - assert(@sqrt(f32, 0.0) == 0.0); - assert(@sqrt(f32, -0.0) == -0.0); - assert(math.isNan(@sqrt(f32, -1.0))); - assert(math.isNan(@sqrt(f32, math.nan(f32)))); + 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" { - assert(math.isPositiveInf(@sqrt(f64, math.inf(f64)))); - assert(@sqrt(f64, 0.0) == 0.0); - assert(@sqrt(f64, -0.0) == -0.0); - assert(math.isNan(@sqrt(f64, -1.0))); - assert(math.isNan(@sqrt(f64, math.nan(f64)))); + 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) { @@ -127,10 +127,10 @@ fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { } test "math.sqrt_int" { - assert(sqrt_int(u32, 3) == 1); - assert(sqrt_int(u32, 4) == 2); - assert(sqrt_int(u32, 5) == 2); - assert(sqrt_int(u32, 8) == 2); - assert(sqrt_int(u32, 9) == 3); - assert(sqrt_int(u32, 10) == 3); + expect(sqrt_int(u32, 3) == 1); + expect(sqrt_int(u32, 4) == 2); + expect(sqrt_int(u32, 5) == 2); + expect(sqrt_int(u32, 8) == 2); + expect(sqrt_int(u32, 9) == 3); + expect(sqrt_int(u32, 10) == 3); } diff --git a/std/math/tan.zig b/std/math/tan.zig index a71a17e625..ec43092320 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; pub fn tan(x: var) @typeOf(x) { const T = @typeOf(x); @@ -129,44 +129,44 @@ fn tan64(x_: f64) f64 { } test "math.tan" { - assert(tan(f32(0.0)) == tan32(0.0)); - assert(tan(f64(0.0)) == tan64(0.0)); + expect(tan(f32(0.0)) == tan32(0.0)); + expect(tan(f64(0.0)) == tan64(0.0)); } test "math.tan32" { const epsilon = 0.000001; - assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); - assert(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); - assert(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); - assert(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); - assert(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); + expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); + expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); + expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); + expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); + expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); } test "math.tan64" { const epsilon = 0.000001; - assert(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); - assert(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); - assert(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); - assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); - assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); + expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); + expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); + expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); + expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); + expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); } test "math.tan32.special" { - assert(tan32(0.0) == 0.0); - assert(tan32(-0.0) == -0.0); - assert(math.isNan(tan32(math.inf(f32)))); - assert(math.isNan(tan32(-math.inf(f32)))); - assert(math.isNan(tan32(math.nan(f32)))); + expect(tan32(0.0) == 0.0); + expect(tan32(-0.0) == -0.0); + expect(math.isNan(tan32(math.inf(f32)))); + expect(math.isNan(tan32(-math.inf(f32)))); + expect(math.isNan(tan32(math.nan(f32)))); } test "math.tan64.special" { - assert(tan64(0.0) == 0.0); - assert(tan64(-0.0) == -0.0); - assert(math.isNan(tan64(math.inf(f64)))); - assert(math.isNan(tan64(-math.inf(f64)))); - assert(math.isNan(tan64(math.nan(f64)))); + expect(tan64(0.0) == 0.0); + expect(tan64(-0.0) == -0.0); + expect(math.isNan(tan64(math.inf(f64)))); + expect(math.isNan(tan64(-math.inf(f64)))); + expect(math.isNan(tan64(math.nan(f64)))); } diff --git a/std/math/tanh.zig b/std/math/tanh.zig index faeb2641cc..a35449a053 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -7,7 +7,7 @@ const builtin = @import("builtin"); const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; const maxInt = std.math.maxInt; @@ -113,42 +113,42 @@ fn tanh64(x: f64) f64 { } test "math.tanh" { - assert(tanh(f32(1.5)) == tanh32(1.5)); - assert(tanh(f64(1.5)) == tanh64(1.5)); + expect(tanh(f32(1.5)) == tanh32(1.5)); + expect(tanh(f64(1.5)) == tanh64(1.5)); } test "math.tanh32" { const epsilon = 0.000001; - assert(math.approxEq(f32, tanh32(0.0), 0.0, epsilon)); - assert(math.approxEq(f32, tanh32(0.2), 0.197375, epsilon)); - assert(math.approxEq(f32, tanh32(0.8923), 0.712528, epsilon)); - assert(math.approxEq(f32, tanh32(1.5), 0.905148, epsilon)); - assert(math.approxEq(f32, tanh32(37.45), 1.0, epsilon)); + expect(math.approxEq(f32, tanh32(0.0), 0.0, epsilon)); + expect(math.approxEq(f32, tanh32(0.2), 0.197375, epsilon)); + expect(math.approxEq(f32, tanh32(0.8923), 0.712528, epsilon)); + expect(math.approxEq(f32, tanh32(1.5), 0.905148, epsilon)); + expect(math.approxEq(f32, tanh32(37.45), 1.0, epsilon)); } test "math.tanh64" { const epsilon = 0.000001; - assert(math.approxEq(f64, tanh64(0.0), 0.0, epsilon)); - assert(math.approxEq(f64, tanh64(0.2), 0.197375, epsilon)); - assert(math.approxEq(f64, tanh64(0.8923), 0.712528, epsilon)); - assert(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon)); - assert(math.approxEq(f64, tanh64(37.45), 1.0, epsilon)); + expect(math.approxEq(f64, tanh64(0.0), 0.0, epsilon)); + expect(math.approxEq(f64, tanh64(0.2), 0.197375, epsilon)); + expect(math.approxEq(f64, tanh64(0.8923), 0.712528, epsilon)); + expect(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon)); + expect(math.approxEq(f64, tanh64(37.45), 1.0, epsilon)); } test "math.tanh32.special" { - assert(tanh32(0.0) == 0.0); - assert(tanh32(-0.0) == -0.0); - assert(tanh32(math.inf(f32)) == 1.0); - assert(tanh32(-math.inf(f32)) == -1.0); - assert(math.isNan(tanh32(math.nan(f32)))); + expect(tanh32(0.0) == 0.0); + expect(tanh32(-0.0) == -0.0); + expect(tanh32(math.inf(f32)) == 1.0); + expect(tanh32(-math.inf(f32)) == -1.0); + expect(math.isNan(tanh32(math.nan(f32)))); } test "math.tanh64.special" { - assert(tanh64(0.0) == 0.0); - assert(tanh64(-0.0) == -0.0); - assert(tanh64(math.inf(f64)) == 1.0); - assert(tanh64(-math.inf(f64)) == -1.0); - assert(math.isNan(tanh64(math.nan(f64)))); + expect(tanh64(0.0) == 0.0); + expect(tanh64(-0.0) == -0.0); + expect(tanh64(math.inf(f64)) == 1.0); + expect(tanh64(-math.inf(f64)) == -1.0); + expect(math.isNan(tanh64(math.nan(f64)))); } diff --git a/std/math/trunc.zig b/std/math/trunc.zig index bb309a1e24..8c91ccc568 100644 --- a/std/math/trunc.zig +++ b/std/math/trunc.zig @@ -6,7 +6,7 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; pub fn trunc(x: var) @typeOf(x) { @@ -61,34 +61,34 @@ fn trunc64(x: f64) f64 { } test "math.trunc" { - assert(trunc(f32(1.3)) == trunc32(1.3)); - assert(trunc(f64(1.3)) == trunc64(1.3)); + expect(trunc(f32(1.3)) == trunc32(1.3)); + expect(trunc(f64(1.3)) == trunc64(1.3)); } test "math.trunc32" { - assert(trunc32(1.3) == 1.0); - assert(trunc32(-1.3) == -1.0); - assert(trunc32(0.2) == 0.0); + expect(trunc32(1.3) == 1.0); + expect(trunc32(-1.3) == -1.0); + expect(trunc32(0.2) == 0.0); } test "math.trunc64" { - assert(trunc64(1.3) == 1.0); - assert(trunc64(-1.3) == -1.0); - assert(trunc64(0.2) == 0.0); + expect(trunc64(1.3) == 1.0); + expect(trunc64(-1.3) == -1.0); + expect(trunc64(0.2) == 0.0); } test "math.trunc32.special" { - assert(trunc32(0.0) == 0.0); // 0x3F800000 - assert(trunc32(-0.0) == -0.0); - assert(math.isPositiveInf(trunc32(math.inf(f32)))); - assert(math.isNegativeInf(trunc32(-math.inf(f32)))); - assert(math.isNan(trunc32(math.nan(f32)))); + expect(trunc32(0.0) == 0.0); // 0x3F800000 + expect(trunc32(-0.0) == -0.0); + expect(math.isPositiveInf(trunc32(math.inf(f32)))); + expect(math.isNegativeInf(trunc32(-math.inf(f32)))); + expect(math.isNan(trunc32(math.nan(f32)))); } test "math.trunc64.special" { - assert(trunc64(0.0) == 0.0); - assert(trunc64(-0.0) == -0.0); - assert(math.isPositiveInf(trunc64(math.inf(f64)))); - assert(math.isNegativeInf(trunc64(-math.inf(f64)))); - assert(math.isNan(trunc64(math.nan(f64)))); + expect(trunc64(0.0) == 0.0); + expect(trunc64(-0.0) == -0.0); + expect(math.isPositiveInf(trunc64(math.inf(f64)))); + expect(math.isNegativeInf(trunc64(-math.inf(f64)))); + expect(math.isNan(trunc64(math.nan(f64)))); } diff --git a/std/mem.zig b/std/mem.zig index 9914a08e65..39b9701754 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -6,6 +6,7 @@ const builtin = @import("builtin"); const mem = @This(); const meta = std.meta; const trait = meta.trait; +const testing = std.testing; pub const Allocator = struct { pub const Error = error{OutOfMemory}; @@ -36,20 +37,9 @@ pub const Allocator = struct { /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: *Allocator, old_mem: []u8) void, - /// Call `destroy` with the result - /// TODO this is deprecated. use createOne instead - pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { - const T = @typeOf(init); - if (@sizeOf(T) == 0) return &(T{}); - const slice = try self.alloc(T, 1); - const ptr = &slice[0]; - ptr.* = init; - return ptr; - } - /// Call `destroy` with the result. /// Returns undefined memory. - pub fn createOne(self: *Allocator, comptime T: type) Error!*T { + pub fn create(self: *Allocator, comptime T: type) Error!*T { if (@sizeOf(T) == 0) return &(T{}); const slice = try self.alloc(T, 1); return &slice[0]; @@ -192,7 +182,7 @@ test "mem.secureZero" { set(u8, a[0..], 0); secureZero(u8, b[0..]); - assert(eql(u8, a[0..], b[0..])); + testing.expectEqualSlices(u8, a[0..], b[0..]); } pub fn compare(comptime T: type, lhs: []const T, rhs: []const T) Compare { @@ -221,11 +211,11 @@ pub fn compare(comptime T: type, lhs: []const T, rhs: []const T) Compare { } test "mem.compare" { - assert(compare(u8, "abcd", "bee") == Compare.LessThan); - assert(compare(u8, "abc", "abc") == Compare.Equal); - assert(compare(u8, "abc", "abc0") == Compare.LessThan); - assert(compare(u8, "", "") == Compare.Equal); - assert(compare(u8, "", "a") == Compare.LessThan); + 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); } /// Returns true if lhs < rhs, false otherwise @@ -238,11 +228,11 @@ pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool { } test "mem.lessThan" { - assert(lessThan(u8, "abcd", "bee")); - assert(!lessThan(u8, "abc", "abc")); - assert(lessThan(u8, "abc", "abc0")); - assert(!lessThan(u8, "", "")); - assert(lessThan(u8, "", "a")); + testing.expect(lessThan(u8, "abcd", "bee")); + testing.expect(!lessThan(u8, "abc", "abc")); + testing.expect(lessThan(u8, "abc", "abc0")); + testing.expect(!lessThan(u8, "", "")); + testing.expect(lessThan(u8, "", "a")); } /// Compares two slices and returns whether they are equal. @@ -307,10 +297,10 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co } test "mem.trim" { - assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n ")); - assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo")); - assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo")); - assert(eql(u8, trim(u8, "foo", " \n"), "foo")); + testing.expectEqualSlices(u8, "foo\n ", trimLeft(u8, " foo\n ", " \n")); + testing.expectEqualSlices(u8, " foo", trimRight(u8, " foo\n ", " \n")); + testing.expectEqualSlices(u8, "foo", trim(u8, " foo\n ", " \n")); + testing.expectEqualSlices(u8, "foo", trim(u8, "foo", " \n")); } /// Linear search for the index of a scalar value inside a slice. @@ -391,31 +381,27 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee } test "mem.indexOf" { - assert(indexOf(u8, "one two three four", "four").? == 14); - assert(lastIndexOf(u8, "one two three two four", "two").? == 14); - assert(indexOf(u8, "one two three four", "gour") == null); - assert(lastIndexOf(u8, "one two three four", "gour") == null); - assert(indexOf(u8, "foo", "foo").? == 0); - assert(lastIndexOf(u8, "foo", "foo").? == 0); - assert(indexOf(u8, "foo", "fool") == null); - assert(lastIndexOf(u8, "foo", "lfoo") == null); - assert(lastIndexOf(u8, "foo", "fool") == null); + testing.expect(indexOf(u8, "one two three four", "four").? == 14); + testing.expect(lastIndexOf(u8, "one two three two four", "two").? == 14); + testing.expect(indexOf(u8, "one two three four", "gour") == null); + testing.expect(lastIndexOf(u8, "one two three four", "gour") == null); + testing.expect(indexOf(u8, "foo", "foo").? == 0); + testing.expect(lastIndexOf(u8, "foo", "foo").? == 0); + testing.expect(indexOf(u8, "foo", "fool") == null); + testing.expect(lastIndexOf(u8, "foo", "lfoo") == null); + testing.expect(lastIndexOf(u8, "foo", "fool") == null); - assert(indexOf(u8, "foo foo", "foo").? == 0); - assert(lastIndexOf(u8, "foo foo", "foo").? == 4); - assert(lastIndexOfAny(u8, "boo, cat", "abo").? == 6); - assert(lastIndexOfScalar(u8, "boo", 'o').? == 2); + testing.expect(indexOf(u8, "foo foo", "foo").? == 0); + testing.expect(lastIndexOf(u8, "foo foo", "foo").? == 4); + testing.expect(lastIndexOfAny(u8, "boo, cat", "abo").? == 6); + testing.expect(lastIndexOfScalar(u8, "boo", 'o').? == 2); } /// Reads an integer from memory with size equal to bytes.len. /// T specifies the return type, which must be large enough to store /// the result. -/// See also ::readIntBE or ::readIntLE. -pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) T { - if (T.bit_count == 8) { - return bytes[0]; - } - var result: T = 0; +pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.Endian) ReturnType { + var result: ReturnType = 0; switch (endian) { builtin.Endian.Big => { for (bytes) |b| { @@ -423,171 +409,262 @@ pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) T { } }, builtin.Endian.Little => { - const ShiftType = math.Log2Int(T); + const ShiftType = math.Log2Int(ReturnType); for (bytes) |b, index| { - result = result | (T(b) << @intCast(ShiftType, index * 8)); + result = result | (ReturnType(b) << @intCast(ShiftType, index * 8)); } }, } return result; } -/// Reads a big-endian int of type T from bytes. -/// bytes.len must be exactly @sizeOf(T). -pub fn readIntBE(comptime T: type, bytes: []const u8) T { - if (T.is_signed) { - return @bitCast(T, readIntBE(@IntType(false, T.bit_count), bytes)); - } - assert(bytes.len == @sizeOf(T)); - if (T == u8) return bytes[0]; - var result: T = 0; - { - comptime var i = 0; - inline while (i < @sizeOf(T)) : (i += 1) { - result = (result << 8) | T(bytes[i]); - } - } - return result; +/// Reads an integer from memory with bit count specified by T. +/// The bit count of T must be evenly divisible by 8. +/// This function cannot fail and cannot cause undefined behavior. +/// Assumes the endianness of memory is native. This means the function can +/// simply pointer cast memory. +pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T { + return @ptrCast(*align(1) const T, bytes).*; } -/// Reads a little-endian int of type T from bytes. -/// bytes.len must be exactly @sizeOf(T). -pub fn readIntLE(comptime T: type, bytes: []const u8) T { - if (T.is_signed) { - return @bitCast(T, readIntLE(@IntType(false, T.bit_count), bytes)); - } - assert(bytes.len == @sizeOf(T)); - if (T == u8) return bytes[0]; - var result: T = 0; - { - comptime var i = 0; - inline while (i < @sizeOf(T)) : (i += 1) { - result |= T(bytes[i]) << i * 8; - } - } - return result; +/// Reads an integer from memory with bit count specified by T. +/// The bit count of T must be evenly divisible by 8. +/// This function cannot fail and cannot cause undefined behavior. +/// Assumes the endianness of memory is foreign, so it must byte-swap. +pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T { + return @bswap(T, readIntNative(T, bytes)); } -test "readIntBE/LE" { - assert(readIntBE(u0, []u8{}) == 0x0); - assert(readIntLE(u0, []u8{}) == 0x0); +pub const readIntLittle = switch (builtin.endian) { + builtin.Endian.Little => readIntNative, + builtin.Endian.Big => readIntForeign, +}; - assert(readIntBE(u8, []u8{0x32}) == 0x32); - assert(readIntLE(u8, []u8{0x12}) == 0x12); +pub const readIntBig = switch (builtin.endian) { + builtin.Endian.Little => readIntForeign, + builtin.Endian.Big => readIntNative, +}; - assert(readIntBE(u16, []u8{ 0x12, 0x34 }) == 0x1234); - assert(readIntLE(u16, []u8{ 0x12, 0x34 }) == 0x3412); - - assert(readIntBE(u72, []u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }) == 0x123456789abcdef024); - assert(readIntLE(u72, []u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }) == 0xfedcba9876543210ec); - - assert(readIntBE(i8, []u8{0xff}) == -1); - assert(readIntLE(i8, []u8{0xfe}) == -2); - - assert(readIntBE(i16, []u8{ 0xff, 0xfd }) == -3); - assert(readIntLE(i16, []u8{ 0xfc, 0xff }) == -4); +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 +/// and ignores extra bytes. +/// The bit count of T must be evenly divisible by 8. +/// Assumes the endianness of memory is native. This means the function can +/// simply pointer cast memory. +pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T { + const n = @divExact(T.bit_count, 8); + assert(bytes.len >= n); + // TODO https://github.com/ziglang/zig/issues/863 + return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr)); } -/// Writes an integer to memory with size equal to bytes.len. Pads with zeroes -/// to fill the entire buffer provided. -/// value must be an integer. -pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) void { - const uint = @IntType(false, @typeOf(value).bit_count); +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 +/// and ignores extra bytes. +/// The bit count of T must be evenly divisible by 8. +/// Assumes the endianness of memory is foreign, so it must byte-swap. +pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T { + return @bswap(T, readIntSliceNative(T, bytes)); +} + +pub const readIntSliceLittle = switch (builtin.endian) { + builtin.Endian.Little => readIntSliceNative, + builtin.Endian.Big => readIntSliceForeign, +}; + +pub const readIntSliceBig = switch (builtin.endian) { + builtin.Endian.Little => readIntSliceForeign, + builtin.Endian.Big => readIntSliceNative, +}; + +/// Reads an integer from memory with bit count specified by T. +/// The bit count of T must be evenly divisible by 8. +/// This function cannot fail and cannot cause undefined behavior. +pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, endian: builtin.Endian) T { + if (endian == builtin.endian) { + return readIntNative(T, bytes); + } else { + return readIntForeign(T, bytes); + } +} + +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 +/// and ignores extra bytes. +/// The bit count of T must be evenly divisible by 8. +pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T { + const n = @divExact(T.bit_count, 8); + assert(bytes.len >= n); + // TODO https://github.com/ziglang/zig/issues/863 + return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian); +} + +test "comptime read/write int" { + comptime { + var bytes: [2]u8 = undefined; + std.mem.writeIntLittle(u16, &bytes, 0x1234); + const result = std.mem.readIntBig(u16, &bytes); + testing.expect(result == 0x3412); + } + comptime { + var bytes: [2]u8 = undefined; + std.mem.writeIntBig(u16, &bytes, 0x1234); + const result = std.mem.readIntLittle(u16, &bytes); + testing.expect(result == 0x3412); + } +} + +test "readIntBig and readIntLittle" { + 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(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(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); +} + +/// Writes an integer to memory, storing it in twos-complement. +/// This function always succeeds, has defined behavior for all inputs, and +/// accepts any integer bit width. +/// This function stores in native endian, which means it is implemented as a simple +/// memory store. +pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value: T) void { + @ptrCast(*align(1) T, buf).* = value; +} + +/// Writes an integer to memory, storing it in twos-complement. +/// This function always succeeds, has defined behavior for all inputs, but +/// the integer bit width must be divisible by 8. +/// This function stores in foreign endian, which means it does a @bswap first. +pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, value: T) void { + writeIntNative(T, buf, @bswap(T, value)); +} + +pub const writeIntLittle = switch (builtin.endian) { + builtin.Endian.Little => writeIntNative, + builtin.Endian.Big => writeIntForeign, +}; + +pub const writeIntBig = switch (builtin.endian) { + builtin.Endian.Little => writeIntForeign, + builtin.Endian.Big => writeIntNative, +}; + +/// Writes an integer to memory, storing it in twos-complement. +/// This function always succeeds, has defined behavior for all inputs, but +/// the integer bit width must be divisible by 8. +pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: T, endian: builtin.Endian) void { + if (endian == builtin.endian) { + return writeIntNative(T, buffer, value); + } else { + return writeIntForeign(T, buffer, value); + } +} + +/// Writes a twos-complement little-endian integer to memory. +/// Asserts that buf.len >= T.bit_count / 8. +/// The bit count of T must be divisible by 8. +/// Any extra bytes in buffer after writing the integer are set to zero. To +/// avoid the branch to check for extra buffer bytes, use writeIntLittle +/// instead. +pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { + assert(buffer.len >= @divExact(T.bit_count, 8)); + + // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough + const uint = @IntType(false, T.bit_count); var bits = @truncate(uint, value); - switch (endian) { - builtin.Endian.Big => { - var index: usize = buf.len; - while (index != 0) { - index -= 1; - - buf[index] = @truncate(u8, bits); - bits >>= 8; - } - }, - builtin.Endian.Little => { - for (buf) |*b| { - b.* = @truncate(u8, bits); - bits >>= 8; - } - }, - } - assert(bits == 0); -} - -pub fn writeIntBE(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void { - assert(T.bit_count % 8 == 0); - const uint = @IntType(false, T.bit_count); - if (uint == u0) { - return; - } - var bits = @bitCast(uint, value); - if (uint == u8) { - buf[0] = bits; - return; - } - var index: usize = buf.len; - while (index != 0) { - index -= 1; - - buf[index] = @truncate(u8, bits); - bits >>= 8; - } - assert(bits == 0); -} - -pub fn writeIntLE(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void { - assert(T.bit_count % 8 == 0); - const uint = @IntType(false, T.bit_count); - if (uint == u0) { - return; - } - var bits = @bitCast(uint, value); - if (uint == u8) { - buf[0] = bits; - return; - } - for (buf) |*b| { + for (buffer) |*b| { b.* = @truncate(u8, bits); bits >>= 8; } - assert(bits == 0); } -test "writeIntBE/LE" { +/// Writes a twos-complement big-endian integer to memory. +/// Asserts that buffer.len >= T.bit_count / 8. +/// The bit count of T must be divisible by 8. +/// Any extra bytes in buffer before writing the integer are set to zero. To +/// avoid the branch to check for extra buffer bytes, use writeIntBig instead. +pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void { + assert(buffer.len >= @divExact(T.bit_count, 8)); + + // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough + const uint = @IntType(false, T.bit_count); + var bits = @truncate(uint, value); + var index: usize = buffer.len; + while (index != 0) { + index -= 1; + buffer[index] = @truncate(u8, bits); + bits >>= 8; + } +} + +pub const writeIntSliceNative = switch (builtin.endian) { + builtin.Endian.Little => writeIntSliceLittle, + builtin.Endian.Big => writeIntSliceBig, +}; + +pub const writeIntSliceForeign = switch (builtin.endian) { + builtin.Endian.Little => writeIntSliceBig, + builtin.Endian.Big => writeIntSliceLittle, +}; + +/// Writes a twos-complement integer to memory, with the specified endianness. +/// Asserts that buf.len >= T.bit_count / 8. +/// The bit count of T must be evenly divisible by 8. +/// Any extra bytes in buffer not part of the integer are set to zero, with +/// respect to endianness. To avoid the branch to check for extra buffer bytes, +/// use writeInt instead. +pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: builtin.Endian) void { + comptime assert(T.bit_count % 8 == 0); + switch (endian) { + builtin.Endian.Little => return writeIntSliceLittle(T, buffer, value), + builtin.Endian.Big => return writeIntSliceBig(T, buffer, value), + } +} + +test "writeIntBig and writeIntLittle" { var buf0: [0]u8 = undefined; var buf1: [1]u8 = undefined; var buf2: [2]u8 = undefined; var buf9: [9]u8 = undefined; - writeIntBE(u0, &buf0, 0x0); - assert(eql_slice_u8(buf0[0..], []u8{})); - writeIntLE(u0, &buf0, 0x0); - assert(eql_slice_u8(buf0[0..], []u8{})); + writeIntBig(u0, &buf0, 0x0); + testing.expect(eql_slice_u8(buf0[0..], []u8{})); + writeIntLittle(u0, &buf0, 0x0); + testing.expect(eql_slice_u8(buf0[0..], []u8{})); - writeIntBE(u8, &buf1, 0x12); - assert(eql_slice_u8(buf1[0..], []u8{0x12})); - writeIntLE(u8, &buf1, 0x34); - assert(eql_slice_u8(buf1[0..], []u8{0x34})); + writeIntBig(u8, &buf1, 0x12); + testing.expect(eql_slice_u8(buf1[0..], []u8{0x12})); + writeIntLittle(u8, &buf1, 0x34); + testing.expect(eql_slice_u8(buf1[0..], []u8{0x34})); - writeIntBE(u16, &buf2, 0x1234); - assert(eql_slice_u8(buf2[0..], []u8{ 0x12, 0x34 })); - writeIntLE(u16, &buf2, 0x5678); - assert(eql_slice_u8(buf2[0..], []u8{ 0x78, 0x56 })); + writeIntBig(u16, &buf2, 0x1234); + testing.expect(eql_slice_u8(buf2[0..], []u8{ 0x12, 0x34 })); + writeIntLittle(u16, &buf2, 0x5678); + testing.expect(eql_slice_u8(buf2[0..], []u8{ 0x78, 0x56 })); - writeIntBE(u72, &buf9, 0x123456789abcdef024); - assert(eql_slice_u8(buf9[0..], []u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); - writeIntLE(u72, &buf9, 0xfedcba9876543210ec); - assert(eql_slice_u8(buf9[0..], []u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); + writeIntBig(u72, &buf9, 0x123456789abcdef024); + testing.expect(eql_slice_u8(buf9[0..], []u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); + writeIntLittle(u72, &buf9, 0xfedcba9876543210ec); + testing.expect(eql_slice_u8(buf9[0..], []u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); - writeIntBE(i8, &buf1, -1); - assert(eql_slice_u8(buf1[0..], []u8{0xff})); - writeIntLE(i8, &buf1, -2); - assert(eql_slice_u8(buf1[0..], []u8{0xfe})); + writeIntBig(i8, &buf1, -1); + testing.expect(eql_slice_u8(buf1[0..], []u8{0xff})); + writeIntLittle(i8, &buf1, -2); + testing.expect(eql_slice_u8(buf1[0..], []u8{0xfe})); - writeIntBE(i16, &buf2, -3); - assert(eql_slice_u8(buf2[0..], []u8{ 0xff, 0xfd })); - writeIntLE(i16, &buf2, -4); - assert(eql_slice_u8(buf2[0..], []u8{ 0xfc, 0xff })); + writeIntBig(i16, &buf2, -3); + testing.expect(eql_slice_u8(buf2[0..], []u8{ 0xff, 0xfd })); + writeIntLittle(i16, &buf2, -4); + testing.expect(eql_slice_u8(buf2[0..], []u8{ 0xfc, 0xff })); } pub fn hash_slice_u8(k: []const u8) u32 { @@ -604,23 +681,114 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { } /// Returns an iterator that iterates over the slices of `buffer` that are not -/// any of the bytes in `split_bytes`. -/// split(" abc def ghi ", " ") +/// any of the bytes in `delimiter_bytes`. +/// tokenize(" abc def ghi ", " ") /// Will return slices for "abc", "def", "ghi", null, in that order. -pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { - return SplitIterator{ +/// If `buffer` is empty, the iterator will return null. +/// If `delimiter_bytes` does not exist in buffer, +/// the iterator will return `buffer`, null, in that order. +/// See also the related function `separate`. +pub fn tokenize(buffer: []const u8, delimiter_bytes: []const u8) TokenIterator { + return TokenIterator{ .index = 0, .buffer = buffer, - .split_bytes = split_bytes, + .delimiter_bytes = delimiter_bytes, }; } -test "mem.split" { - var it = split(" abc def ghi ", " "); - assert(eql(u8, it.next().?, "abc")); - assert(eql(u8, it.next().?, "def")); - assert(eql(u8, it.next().?, "ghi")); - assert(it.next() == null); +test "mem.tokenize" { + var it = tokenize(" abc def ghi ", " "); + testing.expect(eql(u8, it.next().?, "abc")); + testing.expect(eql(u8, it.next().?, "def")); + testing.expect(eql(u8, it.next().?, "ghi")); + testing.expect(it.next() == null); + + it = tokenize("..\\bob", "\\"); + testing.expect(eql(u8, it.next().?, "..")); + testing.expect(eql(u8, "..", "..\\bob"[0..it.index])); + testing.expect(eql(u8, it.next().?, "bob")); + testing.expect(it.next() == null); + + it = tokenize("//a/b", "/"); + testing.expect(eql(u8, it.next().?, "a")); + testing.expect(eql(u8, it.next().?, "b")); + testing.expect(eql(u8, "//a/b", "//a/b"[0..it.index])); + testing.expect(it.next() == null); + + it = tokenize("|", "|"); + testing.expect(it.next() == null); + + it = tokenize("", "|"); + testing.expect(it.next() == null); + + it = tokenize("hello", ""); + testing.expect(eql(u8, it.next().?, "hello")); + testing.expect(it.next() == null); + + it = tokenize("hello", " "); + testing.expect(eql(u8, it.next().?, "hello")); + testing.expect(it.next() == null); +} + +test "mem.tokenize (multibyte)" { + var it = tokenize("a|b,c/d e", " /,|"); + testing.expect(eql(u8, it.next().?, "a")); + testing.expect(eql(u8, it.next().?, "b")); + testing.expect(eql(u8, it.next().?, "c")); + testing.expect(eql(u8, it.next().?, "d")); + testing.expect(eql(u8, it.next().?, "e")); + testing.expect(it.next() == null); +} + +/// Returns an iterator that iterates over the slices of `buffer` that +/// are separated by bytes in `delimiter`. +/// separate("abc|def||ghi", "|") +/// will return slices for "abc", "def", "", "ghi", null, in that order. +/// If `delimiter` does not exist in buffer, +/// the iterator will return `buffer`, null, in that order. +/// The delimiter length must not be zero. +/// See also the related function `tokenize`. +/// It is planned to rename this function to `split` before 1.0.0, like this: +/// pub fn split(buffer: []const u8, delimiter: []const u8) SplitIterator { +pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator { + assert(delimiter.len != 0); + return SplitIterator{ + .index = 0, + .buffer = buffer, + .delimiter = delimiter, + }; +} + +test "mem.separate" { + var it = separate("abc|def||ghi", "|"); + testing.expect(eql(u8, it.next().?, "abc")); + testing.expect(eql(u8, it.next().?, "def")); + testing.expect(eql(u8, it.next().?, "")); + testing.expect(eql(u8, it.next().?, "ghi")); + testing.expect(it.next() == null); + + it = separate("", "|"); + testing.expect(eql(u8, it.next().?, "")); + testing.expect(it.next() == null); + + it = separate("|", "|"); + testing.expect(eql(u8, it.next().?, "")); + testing.expect(eql(u8, it.next().?, "")); + testing.expect(it.next() == null); + + it = separate("hello", " "); + testing.expect(eql(u8, it.next().?, "hello")); + testing.expect(it.next() == null); +} + +test "mem.separate (multibyte)" { + var it = separate("a, b ,, c, d, e", ", "); + testing.expect(eql(u8, it.next().?, "a")); + testing.expect(eql(u8, it.next().?, "b ,")); + testing.expect(eql(u8, it.next().?, "c")); + testing.expect(eql(u8, it.next().?, "d")); + testing.expect(eql(u8, it.next().?, "e")); + testing.expect(it.next() == null); } pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { @@ -628,8 +796,8 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool } test "mem.startsWith" { - assert(startsWith(u8, "Bob", "Bo")); - assert(!startsWith(u8, "Needle in haystack", "haystack")); + testing.expect(startsWith(u8, "Bob", "Bo")); + testing.expect(!startsWith(u8, "Needle in haystack", "haystack")); } pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { @@ -637,16 +805,17 @@ pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { } test "mem.endsWith" { - assert(endsWith(u8, "Needle in haystack", "haystack")); - assert(!endsWith(u8, "Bob", "Bo")); + testing.expect(endsWith(u8, "Needle in haystack", "haystack")); + testing.expect(!endsWith(u8, "Bob", "Bo")); } -pub const SplitIterator = struct { +pub const TokenIterator = struct { buffer: []const u8, - split_bytes: []const u8, + delimiter_bytes: []const u8, index: usize, - pub fn next(self: *SplitIterator) ?[]const u8 { + /// Returns a slice of the next token, or null if tokenization is complete. + pub fn next(self: *TokenIterator) ?[]const u8 { // move to beginning of token while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} const start = self.index; @@ -662,16 +831,16 @@ pub const SplitIterator = struct { } /// Returns a slice of the remaining bytes. Does not affect iterator state. - pub fn rest(self: *const SplitIterator) []const u8 { + pub fn rest(self: TokenIterator) []const u8 { // move to beginning of token var index: usize = self.index; while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {} return self.buffer[index..]; } - fn isSplitByte(self: *const SplitIterator, byte: u8) bool { - for (self.split_bytes) |split_byte| { - if (byte == split_byte) { + fn isSplitByte(self: TokenIterator, byte: u8) bool { + for (self.delimiter_bytes) |delimiter_byte| { + if (byte == delimiter_byte) { return true; } } @@ -679,48 +848,72 @@ pub const SplitIterator = struct { } }; -/// Naively combines a series of strings with a separator. -/// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { - comptime assert(strings.len >= 1); - var total_strings_len: usize = strings.len; // 1 sep per string - { - comptime var string_i = 0; - inline while (string_i < strings.len) : (string_i += 1) { - const arg = ([]const u8)(strings[string_i]); - total_strings_len += arg.len; - } +pub const SplitIterator = struct { + buffer: []const u8, + index: ?usize, + delimiter: []const u8, + + /// Returns a slice of the next field, or null if splitting is complete. + pub fn next(self: *SplitIterator) ?[]const u8 { + const start = self.index orelse return null; + const end = if (indexOfPos(u8, self.buffer, start, self.delimiter)) |delim_start| blk: { + self.index = delim_start + self.delimiter.len; + break :blk delim_start; + } else blk: { + self.index = null; + break :blk self.buffer.len; + }; + return self.buffer[start..end]; } - const buf = try allocator.alloc(u8, total_strings_len); + /// Returns a slice of the remaining bytes. Does not affect iterator state. + pub fn rest(self: SplitIterator) []const u8 { + const end = self.buffer.len; + const start = self.index orelse end; + return self.buffer[start..end]; + } +}; + +/// 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]; + + const total_len = blk: { + var sum: usize = separator.len * (slices.len - 1); + for (slices) |slice| + sum += slice.len; + break :blk sum; + }; + + const buf = try allocator.alloc(u8, total_len); errdefer allocator.free(buf); - var buf_index: usize = 0; - comptime var string_i = 0; - inline while (true) { - const arg = ([]const u8)(strings[string_i]); - string_i += 1; - copy(u8, buf[buf_index..], arg); - buf_index += arg.len; - if (string_i >= strings.len) break; - if (buf[buf_index - 1] != sep) { - buf[buf_index] = sep; - buf_index += 1; - } + copy(u8, buf, slices[0]); + var buf_index: usize = slices[0].len; + for (slices[1..]) |slice| { + copy(u8, buf[buf_index..], separator); + buf_index += separator.len; + copy(u8, buf[buf_index..], slice); + buf_index += slice.len; } - return allocator.shrink(u8, buf, buf_index); + // No need for shrink since buf is exactly the correct size. + return buf; } test "mem.join" { - assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c")); - assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a")); + 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")); } test "testStringEquality" { - assert(eql(u8, "abcd", "abcd")); - assert(!eql(u8, "abcdef", "abZdef")); - assert(!eql(u8, "abcdefg", "abcdef")); + testing.expect(eql(u8, "abcd", "abcd")); + testing.expect(!eql(u8, "abcdef", "abZdef")); + testing.expect(!eql(u8, "abcdefg", "abcdef")); } test "testReadInt" { @@ -735,12 +928,12 @@ fn testReadIntImpl() void { 0x56, 0x78, }; - assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); - assert(readIntBE(u32, bytes) == 0x12345678); - assert(readIntBE(i32, bytes) == 0x12345678); - assert(readInt(bytes, u32, builtin.Endian.Little) == 0x78563412); - assert(readIntLE(u32, bytes) == 0x78563412); - assert(readIntLE(i32, bytes) == 0x78563412); + testing.expect(readInt(u32, &bytes, builtin.Endian.Big) == 0x12345678); + testing.expect(readIntBig(u32, &bytes) == 0x12345678); + testing.expect(readIntBig(i32, &bytes) == 0x12345678); + testing.expect(readInt(u32, &bytes, builtin.Endian.Little) == 0x78563412); + testing.expect(readIntLittle(u32, &bytes) == 0x78563412); + testing.expect(readIntLittle(i32, &bytes) == 0x78563412); } { const buf = []u8{ @@ -749,8 +942,8 @@ fn testReadIntImpl() void { 0x12, 0x34, }; - const answer = readInt(buf, u64, builtin.Endian.Big); - assert(answer == 0x00001234); + const answer = readInt(u32, &buf, builtin.Endian.Big); + testing.expect(answer == 0x00001234); } { const buf = []u8{ @@ -759,30 +952,42 @@ fn testReadIntImpl() void { 0x00, 0x00, }; - const answer = readInt(buf, u64, builtin.Endian.Little); - assert(answer == 0x00003412); + const answer = readInt(u32, &buf, builtin.Endian.Little); + testing.expect(answer == 0x00003412); } { const bytes = []u8{ 0xff, 0xfe, }; - assert(readIntBE(u16, bytes) == 0xfffe); - assert(readIntBE(i16, bytes) == -0x0002); - assert(readIntLE(u16, bytes) == 0xfeff); - assert(readIntLE(i16, bytes) == -0x0101); + testing.expect(readIntBig(u16, &bytes) == 0xfffe); + testing.expect(readIntBig(i16, &bytes) == -0x0002); + testing.expect(readIntLittle(u16, &bytes) == 0xfeff); + testing.expect(readIntLittle(i16, &bytes) == -0x0101); } } -test "testWriteInt" { +test "std.mem.writeIntSlice" { testWriteIntImpl(); comptime testWriteIntImpl(); } fn testWriteIntImpl() void { var bytes: [8]u8 = undefined; - writeInt(bytes[0..], u64(0x12345678CAFEBABE), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u0, bytes[0..], 0, builtin.Endian.Big); + 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{ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + })); + + writeIntSlice(u64, bytes[0..], 0x12345678CAFEBABE, builtin.Endian.Big); + testing.expect(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, @@ -793,8 +998,8 @@ fn testWriteIntImpl() void { 0xBE, })); - writeInt(bytes[0..], u64(0xBEBAFECA78563412), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u64, bytes[0..], 0xBEBAFECA78563412, builtin.Endian.Little); + testing.expect(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, @@ -805,8 +1010,8 @@ fn testWriteIntImpl() void { 0xBE, })); - writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u32, bytes[0..], 0x12345678, builtin.Endian.Big); + testing.expect(eql(u8, bytes, []u8{ 0x00, 0x00, 0x00, @@ -817,8 +1022,8 @@ fn testWriteIntImpl() void { 0x78, })); - writeInt(bytes[0..], u32(0x78563412), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u32, bytes[0..], 0x78563412, builtin.Endian.Little); + testing.expect(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, @@ -829,8 +1034,8 @@ fn testWriteIntImpl() void { 0x00, })); - writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u16, bytes[0..], 0x1234, builtin.Endian.Big); + testing.expect(eql(u8, bytes, []u8{ 0x00, 0x00, 0x00, @@ -841,8 +1046,8 @@ fn testWriteIntImpl() void { 0x34, })); - writeInt(bytes[0..], u16(0x1234), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ + writeIntSlice(u16, bytes[0..], 0x1234, builtin.Endian.Little); + testing.expect(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, @@ -863,7 +1068,7 @@ pub fn min(comptime T: type, slice: []const T) T { } test "mem.min" { - assert(min(u8, "abcdefg") == 'a'); + testing.expect(min(u8, "abcdefg") == 'a'); } pub fn max(comptime T: type, slice: []const T) T { @@ -875,7 +1080,7 @@ pub fn max(comptime T: type, slice: []const T) T { } test "mem.max" { - assert(max(u8, "abcdefg") == 'g'); + testing.expect(max(u8, "abcdefg") == 'g'); } pub fn swap(comptime T: type, a: *T, b: *T) void { @@ -903,7 +1108,7 @@ test "std.mem.reverse" { }; reverse(i32, arr[0..]); - assert(eql(i32, arr, []i32{ + testing.expect(eql(i32, arr, []i32{ 4, 2, 1, @@ -930,7 +1135,7 @@ test "std.mem.rotate" { }; rotate(i32, arr[0..], 2); - assert(eql(i32, arr, []i32{ + testing.expect(eql(i32, arr, []i32{ 1, 2, 4, @@ -939,29 +1144,52 @@ test "std.mem.rotate" { })); } -// TODO: When https://github.com/ziglang/zig/issues/649 is solved these can be done by -// endian-casting the pointer and then dereferencing - -pub fn endianSwapIfLe(comptime T: type, x: T) T { - return endianSwapIf(builtin.Endian.Little, T, x); +/// Converts a little-endian integer to host endianness. +pub fn littleToNative(comptime T: type, x: T) T { + return switch (builtin.endian) { + builtin.Endian.Little => x, + builtin.Endian.Big => @bswap(T, x), + }; } -pub fn endianSwapIfBe(comptime T: type, x: T) T { - return endianSwapIf(builtin.Endian.Big, T, x); +/// Converts a big-endian integer to host endianness. +pub fn bigToNative(comptime T: type, x: T) T { + return switch (builtin.endian) { + builtin.Endian.Little => @bswap(T, x), + builtin.Endian.Big => x, + }; } -pub fn endianSwapIf(endian: builtin.Endian, comptime T: type, x: T) T { - return if (builtin.endian == endian) endianSwap(T, x) else x; +/// Converts an integer from specified endianness to host endianness. +pub fn toNative(comptime T: type, x: T, endianness_of_x: builtin.Endian) T { + return switch (endianness_of_x) { + builtin.Endian.Little => littleToNative(T, x), + builtin.Endian.Big => bigToNative(T, x), + }; } -pub fn endianSwap(comptime T: type, x: T) T { - var buf: [@sizeOf(T)]u8 = undefined; - mem.writeInt(buf[0..], x, builtin.Endian.Little); - return mem.readInt(buf, T, builtin.Endian.Big); +/// Converts an integer which has host endianness to the desired endianness. +pub fn nativeTo(comptime T: type, x: T, desired_endianness: builtin.Endian) T { + return switch (desired_endianness) { + builtin.Endian.Little => nativeToLittle(T, x), + builtin.Endian.Big => nativeToBig(T, x), + }; } -test "std.mem.endianSwap" { - assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE); +/// Converts an integer which has host endianness to little endian. +pub fn nativeToLittle(comptime T: type, x: T) T { + return switch (builtin.endian) { + builtin.Endian.Little => x, + builtin.Endian.Big => @bswap(T, x), + }; +} + +/// Converts an integer which has host endianness to big endian. +pub fn nativeToBig(comptime T: type, x: T) T { + return switch (builtin.endian) { + builtin.Endian.Little => @bswap(T, x), + builtin.Endian.Big => x, + }; } fn AsBytesReturnType(comptime P: type) type { @@ -989,12 +1217,12 @@ test "std.mem.asBytes" { builtin.Endian.Little => "\xEF\xBE\xAD\xDE", }; - debug.assert(std.mem.eql(u8, asBytes(&deadbeef), deadbeef_bytes)); + testing.expect(std.mem.eql(u8, asBytes(&deadbeef), deadbeef_bytes)); var codeface = u32(0xC0DEFACE); for (asBytes(&codeface).*) |*b| b.* = 0; - debug.assert(codeface == 0); + testing.expect(codeface == 0); const S = packed struct { a: u8, @@ -1009,7 +1237,7 @@ test "std.mem.asBytes" { .c = 0xDE, .d = 0xA1, }; - debug.assert(std.mem.eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); + testing.expect(std.mem.eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); } ///Given any value, returns a copy of its bytes in an array. @@ -1020,14 +1248,14 @@ pub fn toBytes(value: var) [@sizeOf(@typeOf(value))]u8 { test "std.mem.toBytes" { var my_bytes = toBytes(u32(0x12345678)); switch (builtin.endian) { - builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x12\x34\x56\x78")), - builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x78\x56\x34\x12")), + builtin.Endian.Big => testing.expect(std.mem.eql(u8, my_bytes, "\x12\x34\x56\x78")), + builtin.Endian.Little => testing.expect(std.mem.eql(u8, my_bytes, "\x78\x56\x34\x12")), } my_bytes[0] = '\x99'; switch (builtin.endian) { - builtin.Endian.Big => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x34\x56\x78")), - builtin.Endian.Little => debug.assert(std.mem.eql(u8, my_bytes, "\x99\x56\x34\x12")), + builtin.Endian.Big => testing.expect(std.mem.eql(u8, my_bytes, "\x99\x34\x56\x78")), + builtin.Endian.Little => testing.expect(std.mem.eql(u8, my_bytes, "\x99\x56\x34\x12")), } } @@ -1056,17 +1284,17 @@ test "std.mem.bytesAsValue" { builtin.Endian.Little => "\xEF\xBE\xAD\xDE", }; - debug.assert(deadbeef == bytesAsValue(u32, &deadbeef_bytes).*); + testing.expect(deadbeef == bytesAsValue(u32, &deadbeef_bytes).*); var codeface_bytes = switch (builtin.endian) { builtin.Endian.Big => "\xC0\xDE\xFA\xCE", builtin.Endian.Little => "\xCE\xFA\xDE\xC0", }; var codeface = bytesAsValue(u32, &codeface_bytes); - debug.assert(codeface.* == 0xC0DEFACE); + testing.expect(codeface.* == 0xC0DEFACE); codeface.* = 0; for (codeface_bytes) |b| - debug.assert(b == 0); + testing.expect(b == 0); const S = packed struct { a: u8, @@ -1083,7 +1311,7 @@ test "std.mem.bytesAsValue" { }; const inst_bytes = "\xBE\xEF\xDE\xA1"; const inst2 = bytesAsValue(S, &inst_bytes); - debug.assert(meta.eql(inst, inst2.*)); + testing.expect(meta.eql(inst, inst2.*)); } ///Given a pointer to an array of bytes, returns a value of the specified type backed by a @@ -1098,7 +1326,7 @@ test "std.mem.bytesToValue" { }; const deadbeef = bytesToValue(u32, deadbeef_bytes); - debug.assert(deadbeef == u32(0xDEADBEEF)); + testing.expect(deadbeef == u32(0xDEADBEEF)); } fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { @@ -1109,7 +1337,7 @@ 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) { - debug.assert(start + length <= ptr.*.len); + assert(start + length <= ptr.*.len); const ReturnType = SubArrayPtrReturnType(@typeOf(ptr), length); const T = meta.Child(meta.Child(@typeOf(ptr))); @@ -1119,12 +1347,32 @@ pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubA test "std.mem.subArrayPtr" { const a1 = "abcdef"; const sub1 = subArrayPtr(&a1, 2, 3); - debug.assert(std.mem.eql(u8, sub1.*, "cde")); + testing.expect(std.mem.eql(u8, sub1.*, "cde")); var a2 = "abcdef"; var sub2 = subArrayPtr(&a2, 2, 3); - debug.assert(std.mem.eql(u8, sub2, "cde")); + testing.expect(std.mem.eql(u8, sub2, "cde")); sub2[1] = 'X'; - debug.assert(std.mem.eql(u8, a2, "abcXef")); + testing.expect(std.mem.eql(u8, a2, "abcXef")); +} + +/// Round an address up to the nearest aligned address +pub fn alignForward(addr: usize, alignment: usize) usize { + return (addr + alignment - 1) & ~(alignment - 1); +} + +test "std.mem.alignForward" { + testing.expect(alignForward(1, 1) == 1); + testing.expect(alignForward(2, 1) == 2); + testing.expect(alignForward(1, 2) == 2); + testing.expect(alignForward(2, 2) == 2); + testing.expect(alignForward(3, 2) == 4); + testing.expect(alignForward(4, 2) == 4); + testing.expect(alignForward(7, 8) == 8); + testing.expect(alignForward(8, 8) == 8); + testing.expect(alignForward(9, 8) == 16); + testing.expect(alignForward(15, 8) == 16); + testing.expect(alignForward(16, 8) == 16); + testing.expect(alignForward(17, 8) == 24); } diff --git a/std/meta/index.zig b/std/meta/index.zig index 69a3097288..652e2d39ec 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const debug = std.debug; const mem = std.mem; const math = std.math; +const testing = std.testing; pub const trait = @import("trait.zig"); @@ -64,16 +65,16 @@ test "std.meta.tagName" { var u2a = U2{ .C = 0 }; var u2b = U2{ .D = 0 }; - debug.assert(mem.eql(u8, tagName(E1.A), "A")); - debug.assert(mem.eql(u8, tagName(E1.B), "B")); - debug.assert(mem.eql(u8, tagName(E2.C), "C")); - debug.assert(mem.eql(u8, tagName(E2.D), "D")); - debug.assert(mem.eql(u8, tagName(error.E), "E")); - debug.assert(mem.eql(u8, tagName(error.F), "F")); - debug.assert(mem.eql(u8, tagName(u1g), "G")); - debug.assert(mem.eql(u8, tagName(u1h), "H")); - debug.assert(mem.eql(u8, tagName(u2a), "C")); - debug.assert(mem.eql(u8, tagName(u2b), "D")); + testing.expect(mem.eql(u8, tagName(E1.A), "A")); + testing.expect(mem.eql(u8, tagName(E1.B), "B")); + testing.expect(mem.eql(u8, tagName(E2.C), "C")); + testing.expect(mem.eql(u8, tagName(E2.D), "D")); + testing.expect(mem.eql(u8, tagName(error.E), "E")); + testing.expect(mem.eql(u8, tagName(error.F), "F")); + testing.expect(mem.eql(u8, tagName(u1g), "G")); + testing.expect(mem.eql(u8, tagName(u1h), "H")); + testing.expect(mem.eql(u8, tagName(u2a), "C")); + testing.expect(mem.eql(u8, tagName(u2b), "D")); } pub fn stringToEnum(comptime T: type, str: []const u8) ?T { @@ -90,12 +91,12 @@ test "std.meta.stringToEnum" { A, B, }; - debug.assert(E1.A == stringToEnum(E1, "A").?); - debug.assert(E1.B == stringToEnum(E1, "B").?); - debug.assert(null == stringToEnum(E1, "C")); + testing.expect(E1.A == stringToEnum(E1, "A").?); + testing.expect(E1.B == stringToEnum(E1, "B").?); + testing.expect(null == stringToEnum(E1, "C")); } -pub fn bitCount(comptime T: type) u32 { +pub fn bitCount(comptime T: type) comptime_int { return switch (@typeInfo(T)) { TypeId.Int => |info| info.bits, TypeId.Float => |info| info.bits, @@ -104,22 +105,22 @@ pub fn bitCount(comptime T: type) u32 { } test "std.meta.bitCount" { - debug.assert(bitCount(u8) == 8); - debug.assert(bitCount(f32) == 32); + testing.expect(bitCount(u8) == 8); + testing.expect(bitCount(f32) == 32); } -pub fn alignment(comptime T: type) u29 { +pub fn alignment(comptime T: type) comptime_int { //@alignOf works on non-pointer types const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; return @typeInfo(P).Pointer.alignment; } test "std.meta.alignment" { - debug.assert(alignment(u8) == 1); - debug.assert(alignment(*align(1) u8) == 1); - debug.assert(alignment(*align(2) u8) == 2); - debug.assert(alignment([]align(1) u8) == 1); - debug.assert(alignment([]align(2) u8) == 2); + testing.expect(alignment(u8) == 1); + testing.expect(alignment(*align(1) u8) == 1); + testing.expect(alignment(*align(2) u8) == 2); + testing.expect(alignment([]align(1) u8) == 1); + testing.expect(alignment([]align(2) u8) == 2); } pub fn Child(comptime T: type) type { @@ -133,11 +134,11 @@ pub fn Child(comptime T: type) type { } test "std.meta.Child" { - debug.assert(Child([1]u8) == u8); - debug.assert(Child(*u8) == u8); - debug.assert(Child([]u8) == u8); - debug.assert(Child(?u8) == u8); - debug.assert(Child(promise->u8) == u8); + testing.expect(Child([1]u8) == u8); + testing.expect(Child(*u8) == u8); + testing.expect(Child([]u8) == u8); + testing.expect(Child(?u8) == u8); + testing.expect(Child(promise->u8) == u8); } pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { @@ -172,15 +173,15 @@ test "std.meta.containerLayout" { a: u8, }; - debug.assert(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); - debug.assert(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); - debug.assert(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); - debug.assert(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); - debug.assert(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); - debug.assert(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); - debug.assert(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); - debug.assert(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); - debug.assert(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); + testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); + testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); + testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); } pub fn definitions(comptime T: type) []TypeInfo.Definition { @@ -214,8 +215,8 @@ test "std.meta.definitions" { }; inline for (defs) |def| { - debug.assert(def.len == 1); - debug.assert(comptime mem.eql(u8, def[0].name, "a")); + testing.expect(def.len == 1); + testing.expect(comptime mem.eql(u8, def[0].name, "a")); } } @@ -250,8 +251,8 @@ test "std.meta.definitionInfo" { }; inline for (infos) |info| { - debug.assert(comptime mem.eql(u8, info.name, "a")); - debug.assert(!info.is_pub); + testing.expect(comptime mem.eql(u8, info.name, "a")); + testing.expect(!info.is_pub); } } @@ -288,16 +289,16 @@ test "std.meta.fields" { const sf = comptime fields(S1); const uf = comptime fields(U1); - debug.assert(e1f.len == 1); - debug.assert(e2f.len == 1); - debug.assert(sf.len == 1); - debug.assert(uf.len == 1); - debug.assert(mem.eql(u8, e1f[0].name, "A")); - debug.assert(mem.eql(u8, e2f[0].name, "A")); - debug.assert(mem.eql(u8, sf[0].name, "a")); - debug.assert(mem.eql(u8, uf[0].name, "a")); - debug.assert(comptime sf[0].field_type == u8); - debug.assert(comptime uf[0].field_type == u8); + testing.expect(e1f.len == 1); + testing.expect(e2f.len == 1); + testing.expect(sf.len == 1); + testing.expect(uf.len == 1); + testing.expect(mem.eql(u8, e1f[0].name, "A")); + testing.expect(mem.eql(u8, e2f[0].name, "A")); + testing.expect(mem.eql(u8, sf[0].name, "a")); + testing.expect(mem.eql(u8, uf[0].name, "a")); + testing.expect(comptime sf[0].field_type == u8); + testing.expect(comptime uf[0].field_type == u8); } pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) { @@ -332,12 +333,12 @@ test "std.meta.fieldInfo" { const sf = comptime fieldInfo(S1, "a"); const uf = comptime fieldInfo(U1, "a"); - debug.assert(mem.eql(u8, e1f.name, "A")); - debug.assert(mem.eql(u8, e2f.name, "A")); - debug.assert(mem.eql(u8, sf.name, "a")); - debug.assert(mem.eql(u8, uf.name, "a")); - debug.assert(comptime sf.field_type == u8); - debug.assert(comptime uf.field_type == u8); + testing.expect(mem.eql(u8, e1f.name, "A")); + testing.expect(mem.eql(u8, e2f.name, "A")); + testing.expect(mem.eql(u8, sf.name, "a")); + testing.expect(mem.eql(u8, uf.name, "a")); + testing.expect(comptime sf.field_type == u8); + testing.expect(comptime uf.field_type == u8); } pub fn TagType(comptime T: type) type { @@ -358,8 +359,8 @@ test "std.meta.TagType" { D: u16, }; - debug.assert(TagType(E) == u8); - debug.assert(TagType(U) == E); + testing.expect(TagType(E) == u8); + testing.expect(TagType(U) == E); } ///Returns the active tag of a tagged union @@ -380,10 +381,37 @@ test "std.meta.activeTag" { }; var u = U{ .Int = 32 }; - debug.assert(activeTag(u) == UE.Int); + testing.expect(activeTag(u) == UE.Int); u = U{ .Float = 112.9876 }; - debug.assert(activeTag(u) == UE.Float); + testing.expect(activeTag(u) == UE.Float); +} + +///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); + testing.expect(trait.is(builtin.TypeId.Union)(U)); + testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); + + const info = @typeInfo(U).Union; + + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; + } + unreachable; +} + +test "std.meta.TagPayloadType" { + const Event = union(enum) { + Moved: struct { + from: i32, + to: i32, + }, + }; + const MovedEvent = TagPayloadType(Event, Event.Moved); + var e: Event = undefined; + testing.expect(MovedEvent == @typeOf(e.Moved)); } ///Compares two of any type for equality. Containers are compared on a field-by-field basis, @@ -435,10 +463,18 @@ pub fn eql(a: var, b: @typeOf(a)) bool { builtin.TypeId.Pointer => { const info = @typeInfo(T).Pointer; switch (info.size) { - builtin.TypeInfo.Pointer.Size.One, builtin.TypeInfo.Pointer.Size.Many => return a == b, + builtin.TypeInfo.Pointer.Size.One, + builtin.TypeInfo.Pointer.Size.Many, + builtin.TypeInfo.Pointer.Size.C, + => return a == b, builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, } }, + builtin.TypeId.Optional => { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return eql(a.?, b.?); + }, else => return a == b, } } @@ -452,7 +488,7 @@ test "std.meta.eql" { const U = union(enum) { s: S, - f: f32, + f: ?f32, }; const s_1 = S{ @@ -477,19 +513,19 @@ test "std.meta.eql" { const u_2 = U{ .s = s_1 }; const u_3 = U{ .f = 24 }; - debug.assert(eql(s_1, s_3)); - debug.assert(eql(&s_1, &s_1)); - debug.assert(!eql(&s_1, &s_3)); - debug.assert(eql(u_1, u_3)); - debug.assert(!eql(u_1, u_2)); + testing.expect(eql(s_1, s_3)); + testing.expect(eql(&s_1, &s_1)); + testing.expect(!eql(&s_1, &s_3)); + testing.expect(eql(u_1, u_3)); + testing.expect(!eql(u_1, u_2)); var a1 = "abcdef"; var a2 = "abcdef"; var a3 = "ghijkl"; - debug.assert(eql(a1, a2)); - debug.assert(!eql(a1, a3)); - debug.assert(!eql(a1[0..], a2[0..])); + testing.expect(eql(a1, a2)); + testing.expect(!eql(a1, a3)); + testing.expect(!eql(a1[0..], a2[0..])); const EU = struct { fn tst(err: bool) !u8 { @@ -498,9 +534,9 @@ test "std.meta.eql" { } }; - debug.assert(eql(EU.tst(true), EU.tst(true))); - debug.assert(eql(EU.tst(false), EU.tst(false))); - debug.assert(!eql(EU.tst(false), EU.tst(true))); + testing.expect(eql(EU.tst(true), EU.tst(true))); + testing.expect(eql(EU.tst(false), EU.tst(false))); + testing.expect(!eql(EU.tst(false), EU.tst(true))); } test "intToEnum with error return" { @@ -514,9 +550,9 @@ test "intToEnum with error return" { var zero: u8 = 0; var one: u16 = 1; - debug.assert(intToEnum(E1, zero) catch unreachable == E1.A); - debug.assert(intToEnum(E2, one) catch unreachable == E2.B); - debug.assertError(intToEnum(E1, one), error.InvalidEnumTag); + testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); + testing.expect(intToEnum(E2, one) catch unreachable == E2.B); + testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); } pub const IntToEnumError = error{InvalidEnumTag}; diff --git a/std/meta/trait.zig b/std/meta/trait.zig index caf7f1be04..7fca5f4dcd 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -2,6 +2,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const mem = std.mem; const debug = std.debug; +const testing = std.testing; const warn = debug.warn; const meta = @import("index.zig"); @@ -50,8 +51,8 @@ test "std.meta.trait.multiTrait" { hasField("x"), hasField("y"), }); - debug.assert(isVector(Vector2)); - debug.assert(!isVector(u8)); + testing.expect(isVector(Vector2)); + testing.expect(!isVector(u8)); } /// @@ -85,12 +86,12 @@ test "std.meta.trait.hasDef" { const value = u8(16); }; - debug.assert(hasDef("value")(TestStruct)); - debug.assert(!hasDef("value")(TestStructFail)); - debug.assert(!hasDef("value")(*TestStruct)); - debug.assert(!hasDef("value")(**TestStructFail)); - debug.assert(!hasDef("x")(TestStruct)); - debug.assert(!hasDef("value")(u8)); + testing.expect(hasDef("value")(TestStruct)); + testing.expect(!hasDef("value")(TestStructFail)); + testing.expect(!hasDef("value")(*TestStruct)); + testing.expect(!hasDef("value")(**TestStructFail)); + testing.expect(!hasDef("x")(TestStruct)); + testing.expect(!hasDef("value")(u8)); } /// @@ -111,9 +112,9 @@ test "std.meta.trait.hasFn" { pub fn useless() void {} }; - debug.assert(hasFn("useless")(TestStruct)); - debug.assert(!hasFn("append")(TestStruct)); - debug.assert(!hasFn("useless")(u8)); + testing.expect(hasFn("useless")(TestStruct)); + testing.expect(!hasFn("append")(TestStruct)); + testing.expect(!hasFn("useless")(u8)); } /// @@ -143,11 +144,11 @@ test "std.meta.trait.hasField" { value: u32, }; - debug.assert(hasField("value")(TestStruct)); - debug.assert(!hasField("value")(*TestStruct)); - debug.assert(!hasField("x")(TestStruct)); - debug.assert(!hasField("x")(**TestStruct)); - debug.assert(!hasField("value")(u8)); + testing.expect(hasField("value")(TestStruct)); + testing.expect(!hasField("value")(*TestStruct)); + testing.expect(!hasField("x")(TestStruct)); + testing.expect(!hasField("x")(**TestStruct)); + testing.expect(!hasField("value")(u8)); } /// @@ -161,11 +162,11 @@ pub fn is(comptime id: builtin.TypeId) TraitFn { } test "std.meta.trait.is" { - debug.assert(is(builtin.TypeId.Int)(u8)); - debug.assert(!is(builtin.TypeId.Int)(f32)); - debug.assert(is(builtin.TypeId.Pointer)(*u8)); - debug.assert(is(builtin.TypeId.Void)(void)); - debug.assert(!is(builtin.TypeId.Optional)(anyerror)); + testing.expect(is(builtin.TypeId.Int)(u8)); + testing.expect(!is(builtin.TypeId.Int)(f32)); + testing.expect(is(builtin.TypeId.Pointer)(*u8)); + testing.expect(is(builtin.TypeId.Void)(void)); + testing.expect(!is(builtin.TypeId.Optional)(anyerror)); } /// @@ -180,9 +181,9 @@ pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn { } test "std.meta.trait.isPtrTo" { - debug.assert(!isPtrTo(builtin.TypeId.Struct)(struct {})); - debug.assert(isPtrTo(builtin.TypeId.Struct)(*struct {})); - debug.assert(!isPtrTo(builtin.TypeId.Struct)(**struct {})); + testing.expect(!isPtrTo(builtin.TypeId.Struct)(struct {})); + testing.expect(isPtrTo(builtin.TypeId.Struct)(*struct {})); + testing.expect(!isPtrTo(builtin.TypeId.Struct)(**struct {})); } ///////////Strait trait Fns @@ -205,9 +206,9 @@ test "std.meta.trait.isExtern" { const TestExStruct = extern struct {}; const TestStruct = struct {}; - debug.assert(isExtern(TestExStruct)); - debug.assert(!isExtern(TestStruct)); - debug.assert(!isExtern(u8)); + testing.expect(isExtern(TestExStruct)); + testing.expect(!isExtern(TestStruct)); + testing.expect(!isExtern(u8)); } /// @@ -226,9 +227,40 @@ test "std.meta.trait.isPacked" { const TestPStruct = packed struct {}; const TestStruct = struct {}; - debug.assert(isPacked(TestPStruct)); - debug.assert(!isPacked(TestStruct)); - debug.assert(!isPacked(u8)); + testing.expect(isPacked(TestPStruct)); + testing.expect(!isPacked(TestStruct)); + testing.expect(!isPacked(u8)); +} + +/// +pub fn isUnsignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.Int => !@typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isUnsignedInt" { + testing.expect(isUnsignedInt(u32) == true); + testing.expect(isUnsignedInt(comptime_int) == false); + testing.expect(isUnsignedInt(i64) == false); + testing.expect(isUnsignedInt(f64) == false); +} + +/// +pub fn isSignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.ComptimeInt => true, + builtin.TypeId.Int => @typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isSignedInt" { + testing.expect(isSignedInt(u32) == false); + testing.expect(isSignedInt(comptime_int) == true); + testing.expect(isSignedInt(i64) == true); + testing.expect(isSignedInt(f64) == false); } /// @@ -242,9 +274,9 @@ pub fn isSingleItemPtr(comptime T: type) bool { test "std.meta.trait.isSingleItemPtr" { const array = []u8{0} ** 10; - debug.assert(isSingleItemPtr(@typeOf(&array[0]))); - debug.assert(!isSingleItemPtr(@typeOf(array))); - debug.assert(!isSingleItemPtr(@typeOf(array[0..1]))); + testing.expect(isSingleItemPtr(@typeOf(&array[0]))); + testing.expect(!isSingleItemPtr(@typeOf(array))); + testing.expect(!isSingleItemPtr(@typeOf(array[0..1]))); } /// @@ -259,9 +291,9 @@ pub fn isManyItemPtr(comptime T: type) bool { test "std.meta.trait.isManyItemPtr" { const array = []u8{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); - debug.assert(isManyItemPtr(@typeOf(mip))); - debug.assert(!isManyItemPtr(@typeOf(array))); - debug.assert(!isManyItemPtr(@typeOf(array[0..1]))); + testing.expect(isManyItemPtr(@typeOf(mip))); + testing.expect(!isManyItemPtr(@typeOf(array))); + testing.expect(!isManyItemPtr(@typeOf(array[0..1]))); } /// @@ -275,9 +307,9 @@ pub fn isSlice(comptime T: type) bool { test "std.meta.trait.isSlice" { const array = []u8{0} ** 10; - debug.assert(isSlice(@typeOf(array[0..]))); - debug.assert(!isSlice(@typeOf(array))); - debug.assert(!isSlice(@typeOf(&array[0]))); + testing.expect(isSlice(@typeOf(array[0..]))); + testing.expect(!isSlice(@typeOf(array))); + testing.expect(!isSlice(@typeOf(&array[0]))); } /// @@ -297,10 +329,10 @@ test "std.meta.trait.isIndexable" { const array = []u8{0} ** 10; const slice = array[0..]; - debug.assert(isIndexable(@typeOf(array))); - debug.assert(isIndexable(@typeOf(&array))); - debug.assert(isIndexable(@typeOf(slice))); - debug.assert(!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)))); } /// @@ -316,13 +348,13 @@ test "std.meta.trait.isNumber" { number: u8, }; - debug.assert(isNumber(u32)); - debug.assert(isNumber(f32)); - debug.assert(isNumber(u64)); - debug.assert(isNumber(@typeOf(102))); - debug.assert(isNumber(@typeOf(102.123))); - debug.assert(!isNumber([]u8)); - debug.assert(!isNumber(NotANumber)); + 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([]u8)); + testing.expect(!isNumber(NotANumber)); } /// @@ -335,10 +367,10 @@ pub fn isConstPtr(comptime T: type) bool { test "std.meta.trait.isConstPtr" { var t = u8(0); const c = u8(0); - debug.assert(isConstPtr(*const @typeOf(t))); - debug.assert(isConstPtr(@typeOf(&c))); - debug.assert(!isConstPtr(*@typeOf(t))); - debug.assert(!isConstPtr(@typeOf(6))); + testing.expect(isConstPtr(*const @typeOf(t))); + testing.expect(isConstPtr(@typeOf(&c))); + testing.expect(!isConstPtr(*@typeOf(t))); + testing.expect(!isConstPtr(@typeOf(6))); } /// @@ -362,8 +394,8 @@ test "std.meta.trait.isContainer" { B, }; - debug.assert(isContainer(TestStruct)); - debug.assert(isContainer(TestUnion)); - debug.assert(isContainer(TestEnum)); - debug.assert(!isContainer(u8)); + testing.expect(isContainer(TestStruct)); + testing.expect(isContainer(TestUnion)); + testing.expect(isContainer(TestEnum)); + testing.expect(!isContainer(u8)); } diff --git a/std/mutex.zig b/std/mutex.zig index e35bd81bc4..a13b1c06c7 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -2,77 +2,127 @@ const std = @import("index.zig"); const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; -const assert = std.debug.assert; +const testing = std.testing; const SpinLock = std.SpinLock; const linux = std.os.linux; +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 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 -pub const Mutex = struct { - /// 0: unlocked - /// 1: locked, no waiters - /// 2: locked, one or more waiters - linux_lock: @typeOf(linux_lock_init), +/// 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. +pub const Mutex = if (builtin.single_threaded) + struct { + lock: @typeOf(lock_init), - /// TODO better implementation than spin lock - spin_lock: @typeOf(spin_lock_init), + const lock_init = if (std.debug.runtime_safety) false else {}; - const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {}; - const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {}; + pub const Held = struct { + mutex: *Mutex, - pub const Held = struct { - mutex: *Mutex, + pub fn release(self: Held) void { + if (std.debug.runtime_safety) { + self.mutex.lock = false; + } + } + }; + pub fn init() Mutex { + return Mutex{ .lock = lock_init }; + } + pub fn deinit(self: *Mutex) void {} - pub fn release(self: Held) void { - if (builtin.os == builtin.Os.linux) { - const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); + pub fn acquire(self: *Mutex) Held { + if (std.debug.runtime_safety and self.lock) { + @panic("deadlock detected"); + } + return Held{ .mutex = self }; + } + } +else switch (builtin.os) { + builtin.Os.linux => struct { + /// 0: unlocked + /// 1: locked, no waiters + /// 2: locked, one or more waiters + lock: i32, + + 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.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 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, } } - } else { - SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock }); } - } - }; - - pub fn init() Mutex { - return Mutex{ - .linux_lock = linux_lock_init, - .spin_lock = spin_lock_init, }; - } - pub fn acquire(self: *Mutex) Held { - if (builtin.os == builtin.Os.linux) { - var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse + pub fn init() Mutex { + return Mutex{ .lock = 0 }; + } + + pub fn deinit(self: *Mutex) void {} + + 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.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); while (c != 0) { - const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); + 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.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - } else { - _ = self.spin_lock.acquire(); + return Held{ .mutex = self }; } - 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 }); + } + }; + + pub fn init() Mutex { + return Mutex{ .lock = SpinLock.init() }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + _ = self.lock.acquire(); + return Held{ .mutex = self }; + } + }, }; -const Context = struct { +const TestContext = struct { mutex: *Mutex, data: i128, @@ -90,25 +140,32 @@ test "std.Mutex" { var a = &fixed_buffer_allocator.allocator; var mutex = Mutex.init(); - var context = Context{ + defer mutex.deinit(); + + var context = TestContext{ .mutex = &mutex, .data = 0, }; - const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; - for (threads) |*t| { - t.* = try std.os.spawnThread(&context, worker); - } - for (threads) |t| - t.wait(); + if (builtin.single_threaded) { + worker(&context); + testing.expect(context.data == TestContext.incr_count); + } else { + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, worker); + } + for (threads) |t| + t.wait(); - std.debug.assertOrPanic(context.data == thread_count * Context.incr_count); + testing.expect(context.data == thread_count * TestContext.incr_count); + } } -fn worker(ctx: *Context) void { +fn worker(ctx: *TestContext) void { var i: usize = 0; - while (i != Context.incr_count) : (i += 1) { + while (i != TestContext.incr_count) : (i += 1) { const held = ctx.mutex.acquire(); defer held.release(); diff --git a/std/net.zig b/std/net.zig index 006a9d4ac5..968c1f019f 100644 --- a/std/net.zig +++ b/std/net.zig @@ -23,7 +23,7 @@ pub const Address = struct { .os_addr = posix.sockaddr{ .in = posix.sockaddr_in{ .family = posix.AF_INET, - .port = std.mem.endianSwapIfLe(u16, _port), + .port = mem.nativeToBig(u16, _port), .addr = ip4, .zero = []u8{0} ** 8, }, @@ -37,7 +37,7 @@ pub const Address = struct { .os_addr = posix.sockaddr{ .in6 = posix.sockaddr_in6{ .family = posix.AF_INET6, - .port = std.mem.endianSwapIfLe(u16, _port), + .port = mem.nativeToBig(u16, _port), .flowinfo = 0, .addr = ip6.addr, .scope_id = ip6.scope_id, @@ -47,7 +47,7 @@ pub const Address = struct { } pub fn port(self: Address) u16 { - return std.mem.endianSwapIfLe(u16, self.os_addr.in.port); + return mem.bigToNative(u16, self.os_addr.in.port); } pub fn initPosix(addr: posix.sockaddr) Address { @@ -57,12 +57,12 @@ pub const Address = struct { pub fn format(self: *const Address, out_stream: var) !void { switch (self.os_addr.in.family) { posix.AF_INET => { - const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port); + 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); }, posix.AF_INET6 => { - const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in6.port); + 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)"), @@ -193,7 +193,7 @@ pub fn parseIp6(buf: []const u8) !Ip6Addr { } test "std.net.parseIp4" { - assert((try parseIp4("127.0.0.1")) == std.mem.endianSwapIfLe(u32, 0x7f000001)); + assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001)); testParseIp4Fail("256.0.0.1", error.Overflow); testParseIp4Fail("x.0.0.1", error.InvalidCharacter); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index c8865bfacd..1da0b3492b 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -7,7 +7,6 @@ const posix = os.posix; const windows = os.windows; const mem = std.mem; const debug = std.debug; -const assert = debug.assert; const BufMap = std.BufMap; const Buffer = std.Buffer; const builtin = @import("builtin"); @@ -88,7 +87,8 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess { - const child = try allocator.create(ChildProcess{ + const child = try allocator.create(ChildProcess); + child.* = ChildProcess{ .allocator = allocator, .argv = argv, .pid = undefined, @@ -109,7 +109,7 @@ pub const ChildProcess = struct { .stdin_behavior = StdIo.Inherit, .stdout_behavior = StdIo.Inherit, .stderr_behavior = StdIo.Inherit, - }); + }; errdefer allocator.destroy(child); return child; } @@ -573,7 +573,7 @@ pub const ChildProcess = struct { // to match posix semantics const app_name = x: { if (self.cwd) |cwd| { - const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]); + const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] }); defer self.allocator.free(resolved); break :x try cstr.addNullByte(self.allocator, resolved); } else { @@ -594,12 +594,12 @@ pub const ChildProcess = struct { const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); defer self.allocator.free(PATH); - var it = mem.split(PATH, ";"); + var it = mem.tokenize(PATH, ";"); while (it.next()) |search_path| { - const joined_path = try os.path.join(self.allocator, search_path, app_name); + const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name }); defer self.allocator.free(joined_path); - const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name); + const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); defer self.allocator.free(joined_path_w); if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { @@ -609,6 +609,9 @@ pub const ChildProcess = struct { } else { return err; } + } else { + // Every other error would have been returned earlier. + return error.FileNotFound; } }; @@ -807,10 +810,10 @@ const ErrInt = @IntType(false, @sizeOf(anyerror) * 8); fn writeIntFd(fd: i32, value: ErrInt) !void { const stream = &os.File.openHandle(fd).outStream().stream; - stream.writeIntNe(ErrInt, value) catch return error.SystemResources; + stream.writeIntNative(ErrInt, value) catch return error.SystemResources; } fn readIntFd(fd: i32) !ErrInt { const stream = &os.File.openHandle(fd).inStream().stream; - return stream.readIntNe(ErrInt) catch return error.SystemResources; + return stream.readIntNative(ErrInt) catch return error.SystemResources; } diff --git a/std/os/darwin.zig b/std/os/darwin.zig index c64ce807b6..3e883abbab 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -665,7 +665,7 @@ pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { const ptr_result = c.mmap( - @ptrCast(*c_void, address), + @ptrCast(?*c_void, address), length, @bitCast(c_int, @intCast(c_uint, prot)), @bitCast(c_int, c_uint(flags)), diff --git a/std/os/freebsd/index.zig b/std/os/freebsd/index.zig index 75389fc403..48c0ea5b1e 100644 --- a/std/os/freebsd/index.zig +++ b/std/os/freebsd/index.zig @@ -1,16 +1,20 @@ -const assert = @import("../debug.zig").assert; const builtin = @import("builtin"); -const arch = switch (builtin.arch) { - builtin.Arch.x86_64 => @import("x86_64.zig"), - else => @compileError("unsupported arch"), -}; -pub use @import("syscall.zig"); + pub use @import("errno.zig"); const std = @import("../../index.zig"); const c = std.c; + +const assert = std.debug.assert; +const maxInt = std.math.maxInt; pub const Kevent = c.Kevent; +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC = 14; // struct: process entries +pub const KERN_PROC_PATHNAME = 12; // path to executable + pub const PATH_MAX = 1024; pub const STDIN_FILENO = 0; @@ -22,7 +26,22 @@ pub const PROT_READ = 1; pub const PROT_WRITE = 2; pub const PROT_EXEC = 4; -pub const MAP_FAILED = @maxValue(usize); +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 MAP_FAILED = maxInt(usize); pub const MAP_SHARED = 0x0001; pub const MAP_PRIVATE = 0x0002; pub const MAP_FIXED = 0x0010; @@ -94,26 +113,33 @@ pub const SIGLIBRT = 33; pub const SIGRTMIN = 65; pub const SIGRTMAX = 126; -pub const O_RDONLY = 0o0; -pub const O_WRONLY = 0o1; -pub const O_RDWR = 0o2; -pub const O_ACCMODE = 0o3; +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission -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_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0o4010000; +pub const O_SYNC = 0x0080; pub const O_RSYNC = 0o4010000; pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0o400000; -pub const O_CLOEXEC = 0o2000000; +pub const O_NOFOLLOW = 0x0100; +pub const O_CLOEXEC = 0x00100000; -pub const O_ASYNC = 0o20000; -pub const O_DIRECT = 0o40000; +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; @@ -157,7 +183,6 @@ pub const SOCK_SEQPACKET = 5; pub const SOCK_CLOEXEC = 0x10000000; pub const SOCK_NONBLOCK = 0x20000000; -// TODO: From here pub const PROTO_ip = 0o000; pub const PROTO_icmp = 0o001; pub const PROTO_igmp = 0o002; @@ -500,6 +525,10 @@ pub const TIOCGPKT = 0x80045438; pub const TIOCGPTLCK = 0x80045439; pub const TIOCGEXCL = 0x80045440; +pub const sockaddr = c.sockaddr; +pub const sockaddr_in = c.sockaddr_in; +pub const sockaddr_in6 = c.sockaddr_in6; + fn unsigned(s: i32) u32 { return @bitCast(u32, s); } @@ -539,96 +568,117 @@ pub fn getErrno(r: usize) usize { } pub fn dup2(old: i32, new: i32) usize { - return arch.syscall2(SYS_dup2, @intCast(usize, old), @intCast(usize, new)); + return errnoWrap(c.dup2(old, new)); } pub fn chdir(path: [*]const u8) usize { - return arch.syscall1(SYS_chdir, @ptrToInt(path)); + return errnoWrap(c.chdir(path)); } pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return arch.syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); + return errnoWrap(c.execve(path, argv, envp)); } pub fn fork() usize { - return arch.syscall0(SYS_fork); + return errnoWrap(c.fork()); +} + +pub fn access(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.access(path, mode)); } pub fn getcwd(buf: [*]u8, size: usize) usize { - return arch.syscall2(SYS___getcwd, @ptrToInt(buf), size); + return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return arch.syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count); + return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); +} + +pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { + return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); +} + +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { + return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } pub fn isatty(fd: i32) bool { - var wsz: winsize = undefined; - return arch.syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + return c.isatty(fd) != 0; } pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return arch.syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } pub fn mkdir(path: [*]const u8, mode: u32) usize { - return arch.syscall2(SYS_mkdir, @ptrToInt(path), mode); + return errnoWrap(c.mkdir(path, mode)); } -pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize { - return arch.syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @intCast(usize, fd), @bitCast(usize, offset)); +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap( + @ptrCast(?*c_void, address), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); + const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); + return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return arch.syscall2(SYS_munmap, address, length); + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); } -pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return arch.syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); } pub fn rmdir(path: [*]const u8) usize { - return arch.syscall1(SYS_rmdir, @ptrToInt(path)); + return errnoWrap(c.rmdir(path)); } pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return arch.syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); + return errnoWrap(c.symlink(existing, new)); } -pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return arch.syscall4(SYS_pread, @intCast(usize, fd), @ptrToInt(buf), count, offset); +pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); } pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { - return arch.syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset); + return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); } pub fn pipe(fd: *[2]i32) usize { return pipe2(fd, 0); } -pub fn pipe2(fd: *[2]i32, flags: usize) usize { - return arch.syscall2(SYS_pipe2, @ptrToInt(fd), flags); +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); } -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return arch.syscall3(SYS_write, @intCast(usize, fd), @ptrToInt(buf), count); +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); } -pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return arch.syscall4(SYS_pwrite, @intCast(usize, fd), @ptrToInt(buf), count, offset); +pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); } pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { - return arch.syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset); + return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); } pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return arch.syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); + return errnoWrap(c.rename(old, new)); } -pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { - return arch.syscall3(SYS_open, @ptrToInt(path), flags, perm); +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { + return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); } pub fn create(path: [*]const u8, perm: usize) usize { @@ -636,120 +686,114 @@ pub fn create(path: [*]const u8, perm: usize) usize { } pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return arch.syscall4(SYS_openat, @intCast(usize, dirfd), @ptrToInt(path), flags, mode); + return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); } pub fn close(fd: i32) usize { - return arch.syscall1(SYS_close, @intCast(usize, fd)); + return errnoWrap(c.close(fd)); } -pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return arch.syscall3(SYS_lseek, @intCast(usize, fd), @bitCast(usize, offset), ref_pos); +pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { + return errnoWrap(c.lseek(fd, offset, whence)); } -pub fn exit(status: i32) noreturn { - _ = arch.syscall1(SYS_exit, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { - return arch.syscall3(SYS_getrandom, @ptrToInt(buf), count, @intCast(usize, flags)); +pub fn exit(code: i32) noreturn { + c.exit(code); } pub fn kill(pid: i32, sig: i32) usize { - return arch.syscall2(SYS_kill, @bitCast(usize, @intCast(isize, pid)), @intCast(usize, sig)); + return errnoWrap(c.kill(pid, sig)); } pub fn unlink(path: [*]const u8) usize { - return arch.syscall1(SYS_unlink, @ptrToInt(path)); + return errnoWrap(c.unlink(path)); } -pub fn waitpid(pid: i32, status: *i32, options: i32) usize { - return arch.syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); } pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return arch.syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); + return errnoWrap(c.nanosleep(req, rem)); +} + +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_gettime(clk_id, tp)); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_getres(clk_id, tp)); } pub fn setuid(uid: u32) usize { - return arch.syscall1(SYS_setuid, uid); + return errnoWrap(c.setuid(uid)); } pub fn setgid(gid: u32) usize { - return arch.syscall1(SYS_setgid, gid); + return errnoWrap(c.setgid(gid)); } pub fn setreuid(ruid: u32, euid: u32) usize { - return arch.syscall2(SYS_setreuid, ruid, euid); + return errnoWrap(c.setreuid(ruid, euid)); } pub fn setregid(rgid: u32, egid: u32) usize { - return arch.syscall2(SYS_setregid, rgid, egid); + return errnoWrap(c.setregid(rgid, egid)); } -pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { - // TODO: Implement - return 0; -} +const NSIG = 32; -pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { - // TODO: Implement - return 0; -} - -const NSIG = 65; -const sigset_t = [128 / @sizeOf(usize)]usize; -const all_mask = []usize{@maxValue(usize)}; -const app_mask = []usize{0xfffffffc7fffffff}; - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = struct { - // TODO: Adjust to use freebsd struct layout - handler: extern fn (i32) void, - mask: sigset_t, - flags: u32, -}; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, @maxValue(usize)); +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 empty_sigset = []usize{0} ** sigset_t.len; + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; pub fn raise(sig: i32) usize { - // TODO implement, see linux equivalent for what we want to try and do - return 0; + return errnoWrap(c.raise(sig)); } -fn blockAllSignals(set: *sigset_t) void { - // TODO implement +pub const Stat = c.Stat; +pub const dirent = c.dirent; +pub const timespec = c.timespec; + +pub fn fstat(fd: i32, buf: *c.Stat) usize { + return errnoWrap(c.fstat(fd, buf)); } - -fn blockAppSignals(set: *sigset_t) void { - // TODO implement -} - -fn restoreSignals(set: *sigset_t) void { - // TODO implement -} - -pub fn sigaddset(set: *sigset_t, sig: u6) void { - const s = sig - 1; - (*set)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); -} - -pub fn sigismember(set: *const sigset_t, sig: u6) bool { - const s = sig - 1; - return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; -} - -pub const Stat = arch.Stat; -pub const timespec = arch.timespec; - -pub fn fstat(fd: i32, stat_buf: *Stat) usize { - return arch.syscall2(SYS_fstat, @intCast(usize, fd), @ptrToInt(stat_buf)); -} - pub const iovec = extern struct { iov_base: [*]u8, iov_len: usize, @@ -760,10 +804,12 @@ pub const iovec_const = extern struct { iov_len: usize, }; +// TODO avoid libc dependency pub fn kqueue() usize { return errnoWrap(c.kqueue()); } +// TODO avoid libc dependency pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { return errnoWrap(c.kevent( kq, @@ -775,18 +821,23 @@ pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: )); } +// TODO avoid libc dependency pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); } +// TODO avoid libc dependency pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); } +// TODO avoid libc dependency pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); } +// TODO avoid libc dependency + /// Takes the return value from a syscall and formats it back in the way /// that the kernel represents it to libc. Errno was a mistake, let's make /// it go away forever. diff --git a/std/os/freebsd/syscall.zig b/std/os/freebsd/syscall.zig deleted file mode 100644 index 6cdd71de3b..0000000000 --- a/std/os/freebsd/syscall.zig +++ /dev/null @@ -1,493 +0,0 @@ -pub const SYS_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_wait4 = 7; -// 8 is old creat -pub const SYS_link = 9; -pub const SYS_unlink = 10; -// 11 is obsolete execv -pub const SYS_chdir = 12; -pub const SYS_fchdir = 13; -pub const SYS_freebsd11_mknod = 14; -pub const SYS_chmod = 15; -pub const SYS_chown = 16; -pub const SYS_break = 17; -// 18 is freebsd4 getfsstat -// 19 is old lseek -pub const SYS_getpid = 20; -pub const SYS_mount = 21; -pub const SYS_unmount = 22; -pub const SYS_setuid = 23; -pub const SYS_getuid = 24; -pub const SYS_geteuid = 25; -pub const SYS_ptrace = 26; -pub const SYS_recvmsg = 27; -pub const SYS_sendmsg = 28; -pub const SYS_recvfrom = 29; -pub const SYS_accept = 30; -pub const SYS_getpeername = 31; -pub const SYS_getsockname = 32; -pub const SYS_access = 33; -pub const SYS_chflags = 34; -pub const SYS_fchflags = 35; -pub const SYS_sync = 36; -pub const SYS_kill = 37; -// 38 is old stat -pub const SYS_getppid = 39; -// 40 is old lstat -pub const SYS_dup = 41; -pub const SYS_freebsd10_pipe = 42; -pub const SYS_getegid = 43; -pub const SYS_profil = 44; -pub const SYS_ktrace = 45; -// 46 is old sigaction -pub const SYS_getgid = 47; -// 48 is old sigprocmask -pub const SYS_getlogin = 49; -pub const SYS_setlogin = 50; -pub const SYS_acct = 51; -// 52 is old sigpending -pub const SYS_sigaltstack = 53; -pub const SYS_ioctl = 54; -pub const SYS_reboot = 55; -pub const SYS_revoke = 56; -pub const SYS_symlink = 57; -pub const SYS_readlink = 58; -pub const SYS_execve = 59; -pub const SYS_umask = 60; -pub const SYS_chroot = 61; -// 62 is old fstat -// 63 is old getkerninfo -// 64 is old getpagesize -pub const SYS_msync = 65; -pub const SYS_vfork = 66; -// 67 is obsolete vread -// 68 is obsolete vwrite -// 69 is obsolete sbrk (still present on some platforms) -pub const SYS_sstk = 70; -// 71 is old mmap -pub const SYS_vadvise = 72; -pub const SYS_munmap = 73; -pub const SYS_mprotect = 74; -pub const SYS_madvise = 75; -// 76 is obsolete vhangup -// 77 is obsolete vlimit -pub const SYS_mincore = 78; -pub const SYS_getgroups = 79; -pub const SYS_setgroups = 80; -pub const SYS_getpgrp = 81; -pub const SYS_setpgid = 82; -pub const SYS_setitimer = 83; -// 84 is old wait -pub const SYS_swapon = 85; -pub const SYS_getitimer = 86; -// 87 is old gethostname -// 88 is old sethostname -pub const SYS_getdtablesize = 89; -pub const SYS_dup2 = 90; -pub const SYS_fcntl = 92; -pub const SYS_select = 93; -pub const SYS_fsync = 95; -pub const SYS_setpriority = 96; -pub const SYS_socket = 97; -pub const SYS_connect = 98; -// 99 is old accept -pub const SYS_getpriority = 100; -// 101 is old send -// 102 is old recv -// 103 is old sigreturn -pub const SYS_bind = 104; -pub const SYS_setsockopt = 105; -pub const SYS_listen = 106; -// 107 is obsolete vtimes -// 108 is old sigvec -// 109 is old sigblock -// 110 is old sigsetmask -// 111 is old sigsuspend -// 112 is old sigstack -// 113 is old recvmsg -// 114 is old sendmsg -// 115 is obsolete vtrace -pub const SYS_gettimeofday = 116; -pub const SYS_getrusage = 117; -pub const SYS_getsockopt = 118; -pub const SYS_readv = 120; -pub const SYS_writev = 121; -pub const SYS_settimeofday = 122; -pub const SYS_fchown = 123; -pub const SYS_fchmod = 124; -// 125 is old recvfrom -pub const SYS_setreuid = 126; -pub const SYS_setregid = 127; -pub const SYS_rename = 128; -// 129 is old truncate -// 130 is old ftruncate -pub const SYS_flock = 131; -pub const SYS_mkfifo = 132; -pub const SYS_sendto = 133; -pub const SYS_shutdown = 134; -pub const SYS_socketpair = 135; -pub const SYS_mkdir = 136; -pub const SYS_rmdir = 137; -pub const SYS_utimes = 138; -// 139 is obsolete 4.2 sigreturn -pub const SYS_adjtime = 140; -// 141 is old getpeername -// 142 is old gethostid -// 143 is old sethostid -// 144 is old getrlimit -// 145 is old setrlimit -// 146 is old killpg -pub const SYS_setsid = 147; -pub const SYS_quotactl = 148; -// 149 is old quota -// 150 is old getsockname -pub const SYS_nlm_syscall = 154; -pub const SYS_nfssvc = 155; -// 156 is old getdirentries -// 157 is freebsd4 statfs -// 158 is freebsd4 fstatfs -pub const SYS_lgetfh = 160; -pub const SYS_getfh = 161; -// 162 is freebsd4 getdomainname -// 163 is freebsd4 setdomainname -// 164 is freebsd4 uname -pub const SYS_sysarch = 165; -pub const SYS_rtprio = 166; -pub const SYS_semsys = 169; -pub const SYS_msgsys = 170; -pub const SYS_shmsys = 171; -// 173 is freebsd6 pread -// 174 is freebsd6 pwrite -pub const SYS_setfib = 175; -pub const SYS_ntp_adjtime = 176; -pub const SYS_setgid = 181; -pub const SYS_setegid = 182; -pub const SYS_seteuid = 183; -// 184 is obsolete lfs_bmapv -// 185 is obsolete lfs_markv -// 186 is obsolete lfs_segclean -// 187 is obsolete lfs_segwait -pub const SYS_freebsd11_stat = 188; -pub const SYS_freebsd11_fstat = 189; -pub const SYS_freebsd11_lstat = 190; -pub const SYS_pathconf = 191; -pub const SYS_fpathconf = 192; -pub const SYS_getrlimit = 194; -pub const SYS_setrlimit = 195; -pub const SYS_freebsd11_getdirentries = 196; -// 197 is freebsd6 mmap -pub const SYS___syscall = 198; -// 199 is freebsd6 lseek -// 200 is freebsd6 truncate -// 201 is freebsd6 ftruncate -pub const SYS___sysctl = 202; -pub const SYS_mlock = 203; -pub const SYS_munlock = 204; -pub const SYS_undelete = 205; -pub const SYS_futimes = 206; -pub const SYS_getpgid = 207; -pub const SYS_poll = 209; -pub const SYS_freebsd7___semctl = 220; -pub const SYS_semget = 221; -pub const SYS_semop = 222; -pub const SYS_freebsd7_msgctl = 224; -pub const SYS_msgget = 225; -pub const SYS_msgsnd = 226; -pub const SYS_msgrcv = 227; -pub const SYS_shmat = 228; -pub const SYS_freebsd7_shmctl = 229; -pub const SYS_shmdt = 230; -pub const SYS_shmget = 231; -pub const SYS_clock_gettime = 232; -pub const SYS_clock_settime = 233; -pub const SYS_clock_getres = 234; -pub const SYS_ktimer_create = 235; -pub const SYS_ktimer_delete = 236; -pub const SYS_ktimer_settime = 237; -pub const SYS_ktimer_gettime = 238; -pub const SYS_ktimer_getoverrun = 239; -pub const SYS_nanosleep = 240; -pub const SYS_ffclock_getcounter = 241; -pub const SYS_ffclock_setestimate = 242; -pub const SYS_ffclock_getestimate = 243; -pub const SYS_clock_nanosleep = 244; -pub const SYS_clock_getcpuclockid2 = 247; -pub const SYS_ntp_gettime = 248; -pub const SYS_minherit = 250; -pub const SYS_rfork = 251; -// 252 is obsolete openbsd_poll -pub const SYS_issetugid = 253; -pub const SYS_lchown = 254; -pub const SYS_aio_read = 255; -pub const SYS_aio_write = 256; -pub const SYS_lio_listio = 257; -pub const SYS_freebsd11_getdents = 272; -pub const SYS_lchmod = 274; -// 275 is obsolete netbsd_lchown -pub const SYS_lutimes = 276; -// 277 is obsolete netbsd_msync -pub const SYS_freebsd11_nstat = 278; -pub const SYS_freebsd11_nfstat = 279; -pub const SYS_freebsd11_nlstat = 280; -pub const SYS_preadv = 289; -pub const SYS_pwritev = 290; -// 297 is freebsd4 fhstatfs -pub const SYS_fhopen = 298; -pub const SYS_freebsd11_fhstat = 299; -pub const SYS_modnext = 300; -pub const SYS_modstat = 301; -pub const SYS_modfnext = 302; -pub const SYS_modfind = 303; -pub const SYS_kldload = 304; -pub const SYS_kldunload = 305; -pub const SYS_kldfind = 306; -pub const SYS_kldnext = 307; -pub const SYS_kldstat = 308; -pub const SYS_kldfirstmod = 309; -pub const SYS_getsid = 310; -pub const SYS_setresuid = 311; -pub const SYS_setresgid = 312; -// 313 is obsolete signanosleep -pub const SYS_aio_return = 314; -pub const SYS_aio_suspend = 315; -pub const SYS_aio_cancel = 316; -pub const SYS_aio_error = 317; -// 318 is freebsd6 aio_read -// 319 is freebsd6 aio_write -// 320 is freebsd6 lio_listio -pub const SYS_yield = 321; -// 322 is obsolete thr_sleep -// 323 is obsolete thr_wakeup -pub const SYS_mlockall = 324; -pub const SYS_munlockall = 325; -pub const SYS___getcwd = 326; -pub const SYS_sched_setparam = 327; -pub const SYS_sched_getparam = 328; -pub const SYS_sched_setscheduler = 329; -pub const SYS_sched_getscheduler = 330; -pub const SYS_sched_yield = 331; -pub const SYS_sched_get_priority_max = 332; -pub const SYS_sched_get_priority_min = 333; -pub const SYS_sched_rr_get_interval = 334; -pub const SYS_utrace = 335; -// 336 is freebsd4 sendfile -pub const SYS_kldsym = 337; -pub const SYS_jail = 338; -pub const SYS_nnpfs_syscall = 339; -pub const SYS_sigprocmask = 340; -pub const SYS_sigsuspend = 341; -// 342 is freebsd4 sigaction -pub const SYS_sigpending = 343; -// 344 is freebsd4 sigreturn -pub const SYS_sigtimedwait = 345; -pub const SYS_sigwaitinfo = 346; -pub const SYS___acl_get_file = 347; -pub const SYS___acl_set_file = 348; -pub const SYS___acl_get_fd = 349; -pub const SYS___acl_set_fd = 350; -pub const SYS___acl_delete_file = 351; -pub const SYS___acl_delete_fd = 352; -pub const SYS___acl_aclcheck_file = 353; -pub const SYS___acl_aclcheck_fd = 354; -pub const SYS_extattrctl = 355; -pub const SYS_extattr_set_file = 356; -pub const SYS_extattr_get_file = 357; -pub const SYS_extattr_delete_file = 358; -pub const SYS_aio_waitcomplete = 359; -pub const SYS_getresuid = 360; -pub const SYS_getresgid = 361; -pub const SYS_kqueue = 362; -pub const SYS_freebsd11_kevent = 363; -// 364 is obsolete __cap_get_proc -// 365 is obsolete __cap_set_proc -// 366 is obsolete __cap_get_fd -// 367 is obsolete __cap_get_file -// 368 is obsolete __cap_set_fd -// 369 is obsolete __cap_set_file -pub const SYS_extattr_set_fd = 371; -pub const SYS_extattr_get_fd = 372; -pub const SYS_extattr_delete_fd = 373; -pub const SYS___setugid = 374; -pub const SYS_eaccess = 376; -pub const SYS_afs3_syscall = 377; -pub const SYS_nmount = 378; -// 379 is obsolete kse_exit -// 380 is obsolete kse_wakeup -// 381 is obsolete kse_create -// 382 is obsolete kse_thr_interrupt -// 383 is obsolete kse_release -pub const SYS___mac_get_proc = 384; -pub const SYS___mac_set_proc = 385; -pub const SYS___mac_get_fd = 386; -pub const SYS___mac_get_file = 387; -pub const SYS___mac_set_fd = 388; -pub const SYS___mac_set_file = 389; -pub const SYS_kenv = 390; -pub const SYS_lchflags = 391; -pub const SYS_uuidgen = 392; -pub const SYS_sendfile = 393; -pub const SYS_mac_syscall = 394; -pub const SYS_freebsd11_getfsstat = 395; -pub const SYS_freebsd11_statfs = 396; -pub const SYS_freebsd11_fstatfs = 397; -pub const SYS_freebsd11_fhstatfs = 398; -pub const SYS_ksem_close = 400; -pub const SYS_ksem_post = 401; -pub const SYS_ksem_wait = 402; -pub const SYS_ksem_trywait = 403; -pub const SYS_ksem_init = 404; -pub const SYS_ksem_open = 405; -pub const SYS_ksem_unlink = 406; -pub const SYS_ksem_getvalue = 407; -pub const SYS_ksem_destroy = 408; -pub const SYS___mac_get_pid = 409; -pub const SYS___mac_get_link = 410; -pub const SYS___mac_set_link = 411; -pub const SYS_extattr_set_link = 412; -pub const SYS_extattr_get_link = 413; -pub const SYS_extattr_delete_link = 414; -pub const SYS___mac_execve = 415; -pub const SYS_sigaction = 416; -pub const SYS_sigreturn = 417; -pub const SYS_getcontext = 421; -pub const SYS_setcontext = 422; -pub const SYS_swapcontext = 423; -pub const SYS_swapoff = 424; -pub const SYS___acl_get_link = 425; -pub const SYS___acl_set_link = 426; -pub const SYS___acl_delete_link = 427; -pub const SYS___acl_aclcheck_link = 428; -pub const SYS_sigwait = 429; -pub const SYS_thr_create = 430; -pub const SYS_thr_exit = 431; -pub const SYS_thr_self = 432; -pub const SYS_thr_kill = 433; -pub const SYS_jail_attach = 436; -pub const SYS_extattr_list_fd = 437; -pub const SYS_extattr_list_file = 438; -pub const SYS_extattr_list_link = 439; -// 440 is obsolete kse_switchin -pub const SYS_ksem_timedwait = 441; -pub const SYS_thr_suspend = 442; -pub const SYS_thr_wake = 443; -pub const SYS_kldunloadf = 444; -pub const SYS_audit = 445; -pub const SYS_auditon = 446; -pub const SYS_getauid = 447; -pub const SYS_setauid = 448; -pub const SYS_getaudit = 449; -pub const SYS_setaudit = 450; -pub const SYS_getaudit_addr = 451; -pub const SYS_setaudit_addr = 452; -pub const SYS_auditctl = 453; -pub const SYS__umtx_op = 454; -pub const SYS_thr_new = 455; -pub const SYS_sigqueue = 456; -pub const SYS_kmq_open = 457; -pub const SYS_kmq_setattr = 458; -pub const SYS_kmq_timedreceive = 459; -pub const SYS_kmq_timedsend = 460; -pub const SYS_kmq_notify = 461; -pub const SYS_kmq_unlink = 462; -pub const SYS_abort2 = 463; -pub const SYS_thr_set_name = 464; -pub const SYS_aio_fsync = 465; -pub const SYS_rtprio_thread = 466; -pub const SYS_sctp_peeloff = 471; -pub const SYS_sctp_generic_sendmsg = 472; -pub const SYS_sctp_generic_sendmsg_iov = 473; -pub const SYS_sctp_generic_recvmsg = 474; -pub const SYS_pread = 475; -pub const SYS_pwrite = 476; -pub const SYS_mmap = 477; -pub const SYS_lseek = 478; -pub const SYS_truncate = 479; -pub const SYS_ftruncate = 480; -pub const SYS_thr_kill2 = 481; -pub const SYS_shm_open = 482; -pub const SYS_shm_unlink = 483; -pub const SYS_cpuset = 484; -pub const SYS_cpuset_setid = 485; -pub const SYS_cpuset_getid = 486; -pub const SYS_cpuset_getaffinity = 487; -pub const SYS_cpuset_setaffinity = 488; -pub const SYS_faccessat = 489; -pub const SYS_fchmodat = 490; -pub const SYS_fchownat = 491; -pub const SYS_fexecve = 492; -pub const SYS_freebsd11_fstatat = 493; -pub const SYS_futimesat = 494; -pub const SYS_linkat = 495; -pub const SYS_mkdirat = 496; -pub const SYS_mkfifoat = 497; -pub const SYS_freebsd11_mknodat = 498; -pub const SYS_openat = 499; -pub const SYS_readlinkat = 500; -pub const SYS_renameat = 501; -pub const SYS_symlinkat = 502; -pub const SYS_unlinkat = 503; -pub const SYS_posix_openpt = 504; -pub const SYS_gssd_syscall = 505; -pub const SYS_jail_get = 506; -pub const SYS_jail_set = 507; -pub const SYS_jail_remove = 508; -pub const SYS_closefrom = 509; -pub const SYS___semctl = 510; -pub const SYS_msgctl = 511; -pub const SYS_shmctl = 512; -pub const SYS_lpathconf = 513; -// 514 is obsolete cap_new -pub const SYS___cap_rights_get = 515; -pub const SYS_cap_enter = 516; -pub const SYS_cap_getmode = 517; -pub const SYS_pdfork = 518; -pub const SYS_pdkill = 519; -pub const SYS_pdgetpid = 520; -pub const SYS_pselect = 522; -pub const SYS_getloginclass = 523; -pub const SYS_setloginclass = 524; -pub const SYS_rctl_get_racct = 525; -pub const SYS_rctl_get_rules = 526; -pub const SYS_rctl_get_limits = 527; -pub const SYS_rctl_add_rule = 528; -pub const SYS_rctl_remove_rule = 529; -pub const SYS_posix_fallocate = 530; -pub const SYS_posix_fadvise = 531; -pub const SYS_wait6 = 532; -pub const SYS_cap_rights_limit = 533; -pub const SYS_cap_ioctls_limit = 534; -pub const SYS_cap_ioctls_get = 535; -pub const SYS_cap_fcntls_limit = 536; -pub const SYS_cap_fcntls_get = 537; -pub const SYS_bindat = 538; -pub const SYS_connectat = 539; -pub const SYS_chflagsat = 540; -pub const SYS_accept4 = 541; -pub const SYS_pipe2 = 542; -pub const SYS_aio_mlock = 543; -pub const SYS_procctl = 544; -pub const SYS_ppoll = 545; -pub const SYS_futimens = 546; -pub const SYS_utimensat = 547; -// 548 is obsolete numa_getaffinity -// 549 is obsolete numa_setaffinity -pub const SYS_fdatasync = 550; -pub const SYS_fstat = 551; -pub const SYS_fstatat = 552; -pub const SYS_fhstat = 553; -pub const SYS_getdirentries = 554; -pub const SYS_statfs = 555; -pub const SYS_fstatfs = 556; -pub const SYS_getfsstat = 557; -pub const SYS_fhstatfs = 558; -pub const SYS_mknodat = 559; -pub const SYS_kevent = 560; -pub const SYS_cpuset_getdomain = 561; -pub const SYS_cpuset_setdomain = 562; -pub const SYS_getrandom = 563; -pub const SYS_MAXSYSCALL = 564; diff --git a/std/os/freebsd/x86_64.zig b/std/os/freebsd/x86_64.zig deleted file mode 100644 index 20a6710596..0000000000 --- a/std/os/freebsd/x86_64.zig +++ /dev/null @@ -1,136 +0,0 @@ -const freebsd = @import("index.zig"); -const socklen_t = freebsd.socklen_t; -const iovec = freebsd.iovec; - -pub const SYS_sbrk = 69; - -pub fn syscall0(number: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number) - : "rcx", "r11" - ); -} - -pub fn syscall1(number: usize, arg1: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1) - : "rcx", "r11" - ); -} - -pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2) - : "rcx", "r11" - ); -} - -pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3) - : "rcx", "r11" - ); -} - -pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4) - : "rcx", "r11" - ); -} - -pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4), - [arg5] "{r8}" (arg5) - : "rcx", "r11" - ); -} - -pub fn syscall6( - number: usize, - arg1: usize, - arg2: usize, - arg3: usize, - arg4: usize, - arg5: usize, - arg6: usize, -) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize) - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4), - [arg5] "{r8}" (arg5), - [arg6] "{r9}" (arg6) - : "rcx", "r11" - ); -} - -pub nakedcc fn restore_rt() void { - asm volatile ("syscall" - : - : [number] "{rax}" (usize(SYS_rt_sigreturn)) - : "rcx", "r11" - ); -} - -pub const msghdr = extern struct { - msg_name: &u8, - msg_namelen: socklen_t, - msg_iov: &iovec, - msg_iovlen: i32, - __pad1: i32, - msg_control: &u8, - msg_controllen: socklen_t, - __pad2: socklen_t, - msg_flags: i32, -}; - -/// Renamed to Stat to not conflict with the stat function. -pub const Stat = extern struct { - dev: u64, - ino: u64, - nlink: usize, - - mode: u32, - uid: u32, - gid: u32, - __pad0: u32, - rdev: u64, - size: i64, - blksize: isize, - blocks: i64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - __unused: [3]isize, -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig index ae133bb4b1..f5e0b78eec 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/os/get_app_data_dir.zig @@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD error.OutOfMemory => return error.OutOfMemory, }; defer allocator.free(global_dir); - return os.path.join(allocator, global_dir, appname); + return os.path.join(allocator, [][]const u8{ global_dir, appname }); }, os.windows.E_OUTOFMEMORY => return error.OutOfMemory, else => return error.AppDataDirUnavailable, @@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, home_dir, "Library", "Application Support", appname); + return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); }, builtin.Os.linux, builtin.Os.freebsd => { const home_dir = os.getEnvPosix("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, home_dir, ".local", "share", appname); + return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname }); }, else => @compileError("Unsupported OS"), } @@ -67,4 +67,3 @@ test "std.os.getAppDataDir" { // We can't actually validate the result _ = getAppDataDir(allocator, "zig") catch return; } - diff --git a/std/os/index.zig b/std/os/index.zig index 15be08c689..f52c12c2b6 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) { }; const os = @This(); +comptime { + assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir +} + test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin.zig"); @@ -18,6 +22,7 @@ test "std.os" { _ = @import("test.zig"); _ = @import("time.zig"); _ = @import("windows/index.zig"); + _ = @import("uefi.zig"); _ = @import("get_app_data_dir.zig"); } @@ -26,6 +31,8 @@ pub const darwin = @import("darwin.zig"); pub const linux = @import("linux/index.zig"); pub const freebsd = @import("freebsd/index.zig"); pub const zen = @import("zen.zig"); +pub const uefi = @import("uefi.zig"); + pub const posix = switch (builtin.os) { Os.linux => linux, Os.macosx, Os.ios => darwin, @@ -33,6 +40,7 @@ pub const posix = switch (builtin.os) { Os.zen => zen, else => @compileError("Unsupported OS"), }; + pub const net = @import("net.zig"); pub const ChildProcess = @import("child_process.zig").ChildProcess; @@ -83,6 +91,7 @@ pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirErro const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const c = std.c; @@ -103,7 +112,7 @@ const math = std.math; /// library implementation. pub fn getRandomBytes(buf: []u8) !void { switch (builtin.os) { - Os.linux, Os.freebsd => while (true) { + Os.linux => while (true) { // TODO check libc version and potentially call c.getrandom. // See #397 const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); @@ -116,7 +125,7 @@ pub fn getRandomBytes(buf: []u8) !void { else => return unexpectedErrorPosix(errno), } }, - Os.macosx, Os.ios => return getRandomBytesDevURandom(buf), + Os.macosx, Os.ios, Os.freebsd => return getRandomBytesDevURandom(buf), Os.windows => { // Call RtlGenRandom() instead of CryptGetRandom() on Windows // https://github.com/rust-lang-nursery/rand/issues/111 @@ -164,7 +173,7 @@ test "os.getRandomBytes" { try getRandomBytes(buf_b[0..]); // Check if random (not 100% conclusive) - assert(!mem.eql(u8, buf_a, buf_b)); + testing.expect(!mem.eql(u8, buf_a, buf_b)); } /// Raises a signal in the current kernel thread, ending its execution. @@ -187,6 +196,10 @@ pub fn abort() noreturn { } windows.ExitProcess(3); }, + Os.uefi => { + // TODO there's gotta be a better thing to do here than loop forever + while (true) {} + }, else => @compileError("Unsupported OS"), } } @@ -459,6 +472,7 @@ pub const PosixOpenError = error{ NoSpaceLeft, NotDir, PathAlreadyExists, + DeviceBusy, /// See https://github.com/ziglang/zig/issues/1396 Unexpected, @@ -497,6 +511,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { posix.ENOTDIR => return PosixOpenError.NotDir, posix.EPERM => return PosixOpenError.AccessDenied, posix.EEXIST => return PosixOpenError.PathAlreadyExists, + posix.EBUSY => return PosixOpenError.DeviceBusy, else => return unexpectedErrorPosix(err), } } @@ -598,7 +613,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: // +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.split(PATH, ":"); + var it = mem.tokenize(PATH, ":"); var seen_eacces = false; var err: usize = undefined; while (it.next()) |search_path| { @@ -682,12 +697,7 @@ pub fn getBaseAddress() usize { return base; } const phdr = linuxGetAuxVal(std.elf.AT_PHDR); - const ElfHeader = switch (@sizeOf(usize)) { - 4 => std.elf.Elf32_Ehdr, - 8 => std.elf.Elf64_Ehdr, - else => @compileError("Unsupported architecture"), - }; - return phdr - @sizeOf(ElfHeader); + return phdr - @sizeOf(std.elf.Ehdr); }, builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header), builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), @@ -702,8 +712,8 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { errdefer result.deinit(); if (is_windows) { - const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory; - defer assert(windows.FreeEnvironmentStringsA(ptr) != 0); + const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; + defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); var i: usize = 0; while (true) { @@ -712,17 +722,21 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { const key_start = i; while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} - const key = ptr[key_start..i]; + const key_w = ptr[key_start..i]; + const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); + errdefer allocator.free(key); if (ptr[i] == '=') i += 1; const value_start = i; while (ptr[i] != 0) : (i += 1) {} - const value = ptr[value_start..i]; + const value_w = ptr[value_start..i]; + const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); + errdefer allocator.free(value); i += 1; // skip over null byte - try result.set(key, value); + try result.setMove(key, value); } } else { for (posix_environ_raw) |ptr| { @@ -740,6 +754,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { } } +test "os.getEnvMap" { + var env = try getEnvMap(std.debug.global_allocator); + defer env.deinit(); +} + /// TODO make this go through libc when we have it pub fn getEnvPosix(key: []const u8) ?[]const u8 { for (posix_environ_raw) |ptr| { @@ -760,21 +779,24 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { pub const GetEnvVarOwnedError = error{ OutOfMemory, EnvironmentVariableNotFound, + + /// See https://github.com/ziglang/zig/issues/1774 + InvalidUtf8, }; /// 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 (is_windows) { - const key_with_null = try cstr.addNullByte(allocator, key); + const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); defer allocator.free(key_with_null); - var buf = try allocator.alloc(u8, 256); - errdefer allocator.free(buf); + var buf = try allocator.alloc(u16, 256); + defer allocator.free(buf); while (true) { const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; - const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len); + const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); if (result == 0) { const err = windows.GetLastError(); @@ -788,11 +810,16 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned } if (result > buf.len) { - buf = try allocator.realloc(u8, buf, result); + buf = try allocator.realloc(u16, buf, result); continue; } - return allocator.shrink(u8, buf, result); + return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { + error.DanglingSurrogateHalf => return error.InvalidUtf8, + error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.OutOfMemory => return error.OutOfMemory, + }; } } else { const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; @@ -800,6 +827,11 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned } } +test "os.getEnvVarOwned" { + var ga = debug.global_allocator; + testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); +} + /// Caller must free the returned memory. pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { var buf: [MAX_PATH_BYTES]u8 = undefined; @@ -1253,7 +1285,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void { /// already exists and is a directory. /// 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, 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; @@ -1380,6 +1412,7 @@ const DeleteTreeError = error{ FileSystem, FileBusy, DirNotEmpty, + DeviceBusy, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -1441,6 +1474,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! error.Unexpected, error.InvalidUtf8, error.BadPathName, + error.DeviceBusy, => return err, }; defer dir.close(); @@ -1523,6 +1557,7 @@ pub const Dir = struct { OutOfMemory, InvalidUtf8, BadPathName, + DeviceBusy, /// See https://github.com/ziglang/zig/issues/1396 Unexpected, @@ -1731,8 +1766,57 @@ pub const Dir = struct { } fn nextFreebsd(self: *Dir) !?Entry { - //self.handle.buf = try self.allocator.alloc(u8, page_size); - @compileError("TODO implement dirs for FreeBSD"); + 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, page_size); + } + + while (true) { + const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) return null; + self.handle.index = 0; + self.handle.end_index = result; + break; + } + } + const freebsd_entry = @ptrCast(*align(1) posix.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) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + posix.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } } }; @@ -2136,9 +2220,9 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { const arg = it.next(debug.global_allocator).? catch unreachable; - assert(mem.eql(u8, arg, expected_arg)); + testing.expectEqualSlices(u8, expected_arg, arg); } - assert(it.next(debug.global_allocator) == null); + testing.expect(it.next(debug.global_allocator) == null); } // TODO make this a build variable that you can set @@ -2220,7 +2304,19 @@ pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { switch (builtin.os) { Os.linux => return readLink(out_buffer, "/proc/self/exe"), - Os.freebsd => return readLink(out_buffer, "/proc/curproc/file"), + Os.freebsd => { + var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; + var out_len: usize = out_buffer.len; + const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); + + if (err == 0) return mem.toSlice(u8, out_buffer); + + return switch (err) { + posix.EFAULT => error.BadAdress, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; + }, Os.windows => { var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; const utf16le_slice = try selfExePathW(&utf16le_buf); @@ -2811,14 +2907,15 @@ pub const Thread = struct { pub const Data = if (use_pthreads) struct { handle: Thread.Handle, - stack_addr: usize, - stack_len: usize, + mmap_addr: usize, + mmap_len: usize, } else switch (builtin.os) { builtin.Os.linux => struct { handle: Thread.Handle, - stack_addr: usize, - stack_len: usize, + mmap_addr: usize, + mmap_len: usize, + tls_end_addr: usize, }, builtin.Os.windows => struct { handle: Thread.Handle, @@ -2858,7 +2955,7 @@ pub const Thread = struct { posix.EDEADLK => unreachable, else => unreachable, } - assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); } else switch (builtin.os) { builtin.Os.linux => { while (true) { @@ -2872,7 +2969,7 @@ pub const Thread = struct { else => unreachable, } } - assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); }, builtin.Os.windows => { assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); @@ -2911,11 +3008,15 @@ pub const SpawnThreadError = error{ Unexpected, }; +pub var linux_tls_phdr: ?*std.elf.Phdr = null; +pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is + /// caller must call wait on the returned thread /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { + if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/ziglang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; @@ -2949,7 +3050,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; - const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{ + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.* = WinThread.OuterContext{ .thread = Thread{ .data = Thread.Data{ .heap_handle = heap_handle, @@ -2958,7 +3060,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread }, }, .inner = context, - }) catch unreachable; + }; const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { @@ -2998,42 +3100,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; - const mmap_len = default_stack_size; - const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); - if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); + var stack_end_offset: usize = undefined; + var thread_start_offset: usize = undefined; + var context_start_offset: usize = undefined; + var tls_start_offset: usize = undefined; + const mmap_len = blk: { + // First in memory will be the stack, which grows downwards. + var l: usize = mem.alignForward(default_stack_size, os.page_size); + stack_end_offset = l; + // Above the stack, so that it can be in the same mmap call, put the Thread object. + l = mem.alignForward(l, @alignOf(Thread)); + thread_start_offset = l; + l += @sizeOf(Thread); + // Next, the Context object. + if (@sizeOf(Context) != 0) { + l = mem.alignForward(l, @alignOf(Context)); + context_start_offset = l; + l += @sizeOf(Context); + } + // Finally, the Thread Local Storage, if any. + if (!Thread.use_pthreads) { + if (linux_tls_phdr) |tls_phdr| { + l = mem.alignForward(l, tls_phdr.p_align); + tls_start_offset = l; + l += tls_phdr.p_memsz; + } + } + break :blk l; + }; + const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; + errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); + + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); + thread_ptr.data.mmap_addr = mmap_addr; + thread_ptr.data.mmap_len = mmap_len; - var stack_end: usize = stack_addr + mmap_len; var arg: usize = undefined; if (@sizeOf(Context) != 0) { - stack_end -= @sizeOf(Context); - stack_end -= stack_end % @alignOf(Context); - assert(stack_end >= stack_addr); - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end)); + arg = mmap_addr + context_start_offset; + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); context_ptr.* = context; - arg = stack_end; } - stack_end -= @sizeOf(Thread); - stack_end -= stack_end % @alignOf(Thread); - assert(stack_end >= stack_addr); - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end)); - - thread_ptr.data.stack_addr = stack_addr; - thread_ptr.data.stack_len = mmap_len; - - if (builtin.os == builtin.Os.windows) { - // use windows API directly - @compileError("TODO support spawnThread for Windows"); - } else if (Thread.use_pthreads) { + if (Thread.use_pthreads) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; defer assert(c.pthread_attr_destroy(&attr) == 0); - // align to page - stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { @@ -3044,10 +3160,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread else => return unexpectedErrorPosix(@intCast(usize, err)), } } else if (builtin.os == builtin.Os.linux) { - // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly - const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; - const newtls: usize = 0; - const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); + var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | + posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | + posix.CLONE_DETACHED; + var newtls: usize = undefined; + if (linux_tls_phdr) |tls_phdr| { + @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz); + thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; + newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); + flags |= posix.CLONE_SETTLS; + } + const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); const err = posix.getErrno(rc); switch (err) { 0 => return thread_ptr, diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 4de26012c7..40fb7823d2 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -1,19 +1,19 @@ const std = @import("../../index.zig"); const builtin = @import("builtin"); const linux = std.os.linux; -const assert = std.debug.assert; +const expect = std.testing.expect; test "getpid" { - assert(linux.getpid() != 0); + expect(linux.getpid() != 0); } test "timer" { const epoll_fd = linux.epoll_create(); var err = linux.getErrno(epoll_fd); - assert(err == 0); + expect(err == 0); const timer_fd = linux.timerfd_create(linux.CLOCK_MONOTONIC, 0); - assert(linux.getErrno(timer_fd) == 0); + expect(linux.getErrno(timer_fd) == 0); const time_interval = linux.timespec{ .tv_sec = 0, @@ -26,7 +26,7 @@ test "timer" { }; err = linux.timerfd_settime(@intCast(i32, timer_fd), 0, &new_time, null); - assert(err == 0); + expect(err == 0); var event = linux.epoll_event{ .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET, @@ -34,7 +34,7 @@ test "timer" { }; err = linux.epoll_ctl(@intCast(i32, epoll_fd), linux.EPOLL_CTL_ADD, @intCast(i32, timer_fd), &event); - assert(err == 0); + expect(err == 0); const events_one: linux.epoll_event = undefined; var events = []linux.epoll_event{events_one} ** 8; diff --git a/std/os/path.zig b/std/os/path.zig index af767b0dca..5ba345fceb 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const Os = builtin.Os; const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const mem = std.mem; const fmt = std.fmt; const Allocator = mem.Allocator; @@ -33,40 +34,103 @@ 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]; + + const total_len = blk: { + var sum: usize = paths[0].len; + var i: usize = 1; + while (i < paths.len) : (i += 1) { + const prev_path = paths[i - 1]; + const this_path = paths[i]; + const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); + const this_sep = (this_path.len != 0 and this_path[0] == separator); + sum += @boolToInt(!prev_sep and !this_sep); + sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len; + } + break :blk sum; + }; + + const buf = try allocator.alloc(u8, total_len); + errdefer allocator.free(buf); + + mem.copy(u8, buf, paths[0]); + var buf_index: usize = paths[0].len; + var i: usize = 1; + while (i < paths.len) : (i += 1) { + const prev_path = paths[i - 1]; + const this_path = paths[i]; + const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); + const this_sep = (this_path.len != 0 and this_path[0] == separator); + if (!prev_sep and !this_sep) { + buf[buf_index] = separator; + buf_index += 1; + } + const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path; + mem.copy(u8, buf[buf_index..], adjusted_path); + buf_index += adjusted_path.len; + } + + // No need for shrink since buf is exactly the correct size. + return buf; +} + +pub const join = if (is_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. -pub fn join(allocator: *Allocator, paths: ...) ![]u8 { - if (is_windows) { - return joinWindows(allocator, paths); - } else { - return joinPosix(allocator, paths); - } +pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { + return joinSep(allocator, sep_windows, paths); } -pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 { - return mem.join(allocator, sep_windows, paths); +/// Naively combines a series of paths with the native path seperator. +/// Allocates memory for the result, which must be freed by the caller. +pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { + return joinSep(allocator, sep_posix, paths); } -pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 { - return mem.join(allocator, sep_posix, paths); +fn testJoinWindows(paths: []const []const u8, expected: []const u8) void { + var buf: [1024]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + const actual = joinWindows(a, paths) catch @panic("fail"); + testing.expectEqualSlices(u8, expected, actual); +} + +fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { + var buf: [1024]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + const actual = joinPosix(a, paths) catch @panic("fail"); + testing.expectEqualSlices(u8, expected, actual); } test "os.path.join" { - assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c")); - assert(mem.eql(u8, try joinWindows(debug.global_allocator, "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"); - assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c")); - assert(mem.eql(u8, try joinWindows(debug.global_allocator, "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"); - assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); + testJoinWindows( + [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, + "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", + ); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c")); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c")); + testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c"); + testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c"); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); + testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); + testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), "/home/andy/dev/zig/build/lib/zig/std/io.zig")); + testJoinPosix( + [][]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"); } pub fn isAbsolute(path: []const u8) bool { @@ -130,11 +194,11 @@ test "os.path.isAbsolutePosix" { } fn testIsAbsoluteWindows(path: []const u8, expected_result: bool) void { - assert(isAbsoluteWindows(path) == expected_result); + testing.expectEqual(expected_result, isAbsoluteWindows(path)); } fn testIsAbsolutePosix(path: []const u8, expected_result: bool) void { - assert(isAbsolutePosix(path) == expected_result); + testing.expectEqual(expected_result, isAbsolutePosix(path)); } pub const WindowsPath = struct { @@ -184,7 +248,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { return relative_path; } - var it = mem.split(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{ @@ -202,7 +266,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { return relative_path; } - var it = mem.split(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{ @@ -218,33 +282,33 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { test "os.path.windowsParsePath" { { const parsed = windowsParsePath("//a/b"); - assert(parsed.is_abs); - assert(parsed.kind == WindowsPath.Kind.NetworkShare); - assert(mem.eql(u8, parsed.disk_designator, "//a/b")); + testing.expect(parsed.is_abs); + testing.expect(parsed.kind == WindowsPath.Kind.NetworkShare); + testing.expect(mem.eql(u8, parsed.disk_designator, "//a/b")); } { const parsed = windowsParsePath("\\\\a\\b"); - assert(parsed.is_abs); - assert(parsed.kind == WindowsPath.Kind.NetworkShare); - assert(mem.eql(u8, parsed.disk_designator, "\\\\a\\b")); + testing.expect(parsed.is_abs); + testing.expect(parsed.kind == WindowsPath.Kind.NetworkShare); + testing.expect(mem.eql(u8, parsed.disk_designator, "\\\\a\\b")); } { const parsed = windowsParsePath("\\\\a\\"); - assert(!parsed.is_abs); - assert(parsed.kind == WindowsPath.Kind.None); - assert(mem.eql(u8, parsed.disk_designator, "")); + testing.expect(!parsed.is_abs); + testing.expect(parsed.kind == WindowsPath.Kind.None); + testing.expect(mem.eql(u8, parsed.disk_designator, "")); } { const parsed = windowsParsePath("/usr/local"); - assert(parsed.is_abs); - assert(parsed.kind == WindowsPath.Kind.None); - assert(mem.eql(u8, parsed.disk_designator, "")); + testing.expect(parsed.is_abs); + testing.expect(parsed.kind == WindowsPath.Kind.None); + testing.expect(mem.eql(u8, parsed.disk_designator, "")); } { const parsed = windowsParsePath("c:../"); - assert(!parsed.is_abs); - assert(parsed.kind == WindowsPath.Kind.Drive); - assert(mem.eql(u8, parsed.disk_designator, "c:")); + testing.expect(!parsed.is_abs); + testing.expect(parsed.kind == WindowsPath.Kind.Drive); + testing.expect(mem.eql(u8, parsed.disk_designator, "c:")); } } @@ -264,8 +328,8 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool { const sep1 = ns1[0]; const sep2 = ns2[0]; - var it1 = mem.split(ns1, []u8{sep1}); - var it2 = mem.split(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().?); @@ -285,8 +349,8 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 const sep1 = p1[0]; const sep2 = p2[0]; - var it1 = mem.split(p1, []u8{sep1}); - var it2 = mem.split(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().?); @@ -312,18 +376,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { return true; } -/// Converts the command line arguments into a slice and calls `resolveSlice`. -pub fn resolve(allocator: *Allocator, args: ...) ![]u8 { - var paths: [args.len][]const u8 = undefined; - comptime var arg_i = 0; - inline while (arg_i < args.len) : (arg_i += 1) { - paths[arg_i] = args[arg_i]; - } - return resolveSlice(allocator, paths); -} - /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. -pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { +pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (is_windows) { return resolveWindows(allocator, paths); } else { @@ -337,6 +391,8 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// If all paths are relative it uses the current working directory as a starting point. /// Each drive has its own current working directory. /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. +/// Note: all usage of this function should be audited due to the existence of symlinks. +/// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(is_windows); // resolveWindows called on non windows can't use getCwd @@ -416,7 +472,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, WindowsPath.Kind.NetworkShare => { result = try allocator.alloc(u8, max_size); - var it = mem.split(paths[first_index], "/\\"); + var it = mem.tokenize(paths[first_index], "/\\"); const server_name = it.next().?; const other_name = it.next().?; @@ -483,7 +539,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (!correct_disk_designator) { continue; } - var it = mem.split(p[parsed.disk_designator.len..], "/\\"); + var it = mem.tokenize(p[parsed.disk_designator.len..], "/\\"); while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; @@ -516,6 +572,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// It resolves "." and "..". /// The result does not have a trailing path separator. /// If all paths are relative it uses the current working directory as a starting point. +/// Note: all usage of this function should be audited due to the existence of symlinks. +/// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(!is_windows); // resolvePosix called on windows can't use getCwd @@ -550,7 +608,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { errdefer allocator.free(result); for (paths[first_index..]) |p, i| { - var it = mem.split(p, "/"); + var it = mem.tokenize(p, "/"); while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; @@ -585,10 +643,10 @@ test "os.path.resolve" { if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { cwd[0] = asciiUpper(cwd[0]); } - assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); + testing.expect(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); } else { - assert(mem.eql(u8, testResolvePosix([][]const u8{ "a/b/c/", "../../.." }), cwd)); - assert(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)); } } @@ -598,48 +656,54 @@ test "os.path.resolveWindows" { 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, parsed_cwd.disk_designator, "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", + }); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); } - assert(mem.eql(u8, result, expected)); + testing.expect(mem.eql(u8, result, expected)); } { const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" }); - const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig"); + const expected = try join(debug.global_allocator, [][]const u8{ + cwd, + "usr\\local\\lib\\zig", + }); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); } - assert(mem.eql(u8, result, expected)); + testing.expect(mem.eql(u8, result, expected)); } } - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }), "C:\\hi\\ok")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }), "C:\\blah\\a")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/blah\\blah", "d:/games", "C:../a" }), "C:\\blah\\a")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/ignore", "d:\\a/b\\c/d", "\\e.exe" }), "D:\\e.exe")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/ignore", "c:/some/file" }), "C:\\some\\file")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "d:/ignore", "d:some/dir//" }), "D:\\ignore\\some\\dir")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "//server/share", "..", "relative\\" }), "\\\\server\\share\\relative")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//" }), "C:\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//dir" }), "C:\\dir")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//server/share" }), "\\\\server\\share\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//server//share" }), "\\\\server\\share\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "///some//dir" }), "C:\\some\\dir")); - assert(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 "os.path.resolvePosix" { - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c" }), "/a/b/c")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b/c", "..", "../" }), "/a")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/", "..", ".." }), "/")); - assert(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")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/var/lib", "../", "file/" }), "/var/file")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/var/lib", "/../", "file/" }), "/file")); - assert(mem.eql(u8, testResolvePosix([][]const u8{ "/some/dir", ".", "/absolute/" }), "/absolute")); - assert(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 { @@ -770,17 +834,17 @@ test "os.path.dirnameWindows" { fn testDirnamePosix(input: []const u8, expected_output: ?[]const u8) void { if (dirnamePosix(input)) |output| { - assert(mem.eql(u8, output, expected_output.?)); + testing.expect(mem.eql(u8, output, expected_output.?)); } else { - assert(expected_output == null); + testing.expect(expected_output == null); } } fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void { if (dirnameWindows(input)) |output| { - assert(mem.eql(u8, output, expected_output.?)); + testing.expect(mem.eql(u8, output, expected_output.?)); } else { - assert(expected_output == null); + testing.expect(expected_output == null); } } @@ -885,15 +949,15 @@ test "os.path.basename" { } fn testBasename(input: []const u8, expected_output: []const u8) void { - assert(mem.eql(u8, basename(input), expected_output)); + testing.expectEqualSlices(u8, expected_output, basename(input)); } fn testBasenamePosix(input: []const u8, expected_output: []const u8) void { - assert(mem.eql(u8, basenamePosix(input), expected_output)); + testing.expectEqualSlices(u8, expected_output, basenamePosix(input)); } fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { - assert(mem.eql(u8, basenameWindows(input), expected_output)); + testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); } /// Returns the relative path from `from` to `to`. If `from` and `to` each @@ -937,8 +1001,8 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) return resolved_to; } - var from_it = mem.split(resolved_from, "/\\"); - var to_it = mem.split(resolved_to, "/\\"); + var from_it = mem.tokenize(resolved_from, "/\\"); + var to_it = mem.tokenize(resolved_to, "/\\"); while (true) { const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); @@ -967,7 +1031,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) // shave off the trailing slash result_index -= 1; - var rest_it = mem.split(to_rest, "/\\"); + var rest_it = mem.tokenize(to_rest, "/\\"); while (rest_it.next()) |to_component| { result[result_index] = '\\'; result_index += 1; @@ -988,8 +1052,8 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![ const resolved_to = try resolvePosix(allocator, [][]const u8{to}); defer allocator.free(resolved_to); - var from_it = mem.split(resolved_from, "/"); - var to_it = mem.split(resolved_to, "/"); + var from_it = mem.tokenize(resolved_from, "/"); + var to_it = mem.tokenize(resolved_to, "/"); while (true) { const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); @@ -1068,12 +1132,12 @@ test "os.path.relative" { fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) void { const result = relativePosix(debug.global_allocator, from, to) catch unreachable; - assert(mem.eql(u8, result, expected_output)); + testing.expectEqualSlices(u8, expected_output, result); } fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) void { const result = relativeWindows(debug.global_allocator, from, to) catch unreachable; - assert(mem.eql(u8, result, expected_output)); + testing.expectEqualSlices(u8, expected_output, result); } pub const RealError = error{ @@ -1093,6 +1157,7 @@ pub const RealError = error{ NoSpaceLeft, FileSystem, BadPathName, + DeviceBusy, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -1161,7 +1226,7 @@ pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealErro const pathname_w = try windows_util.cStrToPrefixedFileW(pathname); return realW(out_buffer, pathname_w); }, - Os.macosx, Os.ios => { + Os.freebsd, Os.macosx, Os.ios => { // TODO instead of calling the libc function here, port the implementation to Zig const err = posix.getErrno(posix.realpath(pathname, out_buffer)); switch (err) { @@ -1188,15 +1253,6 @@ pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealErro return os.readLinkC(out_buffer, proc_path.ptr); }, - Os.freebsd => { // XXX requires fdescfs - const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0); - defer os.close(fd); - - var buf: ["/dev/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = fmt.bufPrint(buf[0..], "/dev/fd/{}\x00", fd) catch unreachable; - - return os.readLinkC(out_buffer, proc_path.ptr); - }, else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)), } } @@ -1228,5 +1284,5 @@ pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { test "os.path.real" { // at least call it so it gets compiled var buf: [os.MAX_PATH_BYTES]u8 = undefined; - std.debug.assertError(real(&buf, "definitely_bogus_does_not_exist1234"), error.FileNotFound); + testing.expectError(error.FileNotFound, real(&buf, "definitely_bogus_does_not_exist1234")); } diff --git a/std/os/test.zig b/std/os/test.zig index 5142920687..b2a4d96651 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -1,6 +1,6 @@ const std = @import("../index.zig"); const os = std.os; -const assert = std.debug.assert; +const expect = std.testing.expect; const io = std.io; const mem = std.mem; @@ -18,7 +18,7 @@ test "makePath, put some files in it, deleteTree" { if (os.Dir.open(a, "os_test_tmp")) |dir| { @panic("expected error"); } else |err| { - assert(err == error.FileNotFound); + expect(err == error.FileNotFound); } } @@ -27,7 +27,7 @@ test "access file" { if (os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt")) |ok| { @panic("expected error"); } else |err| { - assert(err == error.FileNotFound); + expect(err == error.FileNotFound); } try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", ""); @@ -40,19 +40,23 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void { } test "std.os.Thread.getCurrentId" { + if (builtin.single_threaded) return error.SkipZigTest; + var thread_current_id: os.Thread.Id = undefined; const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); const thread_id = thread.handle(); thread.wait(); switch (builtin.os) { - builtin.Os.windows => assert(os.Thread.getCurrentId() != thread_current_id), + builtin.Os.windows => expect(os.Thread.getCurrentId() != thread_current_id), else => { - assert(thread_current_id == thread_id); + expect(thread_current_id == thread_id); }, } } test "spawn threads" { + if (builtin.single_threaded) return error.SkipZigTest; + var shared_ctx: i32 = 1; const thread1 = try std.os.spawnThread({}, start1); @@ -65,7 +69,7 @@ test "spawn threads" { thread3.wait(); thread4.wait(); - assert(shared_ctx == 4); + expect(shared_ctx == 4); } fn start1(ctx: void) u8 { @@ -79,7 +83,7 @@ fn start2(ctx: *i32) u8 { test "cpu count" { const cpu_count = try std.os.cpuCount(a); - assert(cpu_count >= 1); + expect(cpu_count >= 1); } test "AtomicFile" { @@ -97,7 +101,23 @@ test "AtomicFile" { try af.finish(); } const content = try io.readFileAlloc(allocator, test_out_file); - assert(mem.eql(u8, content, test_content)); + expect(mem.eql(u8, content, test_content)); try os.deleteFile(test_out_file); } + +test "thread local storage" { + if (builtin.single_threaded) return error.SkipZigTest; + const thread1 = try std.os.spawnThread({}, testTls); + const thread2 = try std.os.spawnThread({}, testTls); + testTls({}); + thread1.wait(); + thread2.wait(); +} + +threadlocal var x: i32 = 1234; +fn testTls(context: void) void { + if (x != 1234) @panic("bad start value"); + x += 1; + if (x != 1235) @panic("bad end value"); +} diff --git a/std/os/time.zig b/std/os/time.zig index d9fe046a55..638fb41ff1 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -2,6 +2,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const debug = std.debug; +const testing = std.testing; const windows = std.os.windows; const linux = std.os.linux; @@ -13,7 +14,7 @@ pub const epoch = @import("epoch.zig"); /// Sleep for the specified duration pub fn sleep(nanoseconds: u64) void { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, Os.macosx, Os.ios, Os.freebsd => { const s = nanoseconds / ns_per_s; const ns = nanoseconds % ns_per_s; posixSleep(@intCast(u63, s), @intCast(u63, ns)); @@ -61,7 +62,7 @@ pub fn timestamp() u64 { /// Get the posix timestamp, UTC, in milliseconds pub const milliTimestamp = switch (builtin.os) { Os.windows => milliTimestampWindows, - Os.linux => milliTimestampPosix, + Os.linux, Os.freebsd => milliTimestampPosix, Os.macosx, Os.ios => milliTimestampDarwin, else => @compileError("Unsupported OS"), }; @@ -79,14 +80,12 @@ fn milliTimestampWindows() u64 { } fn milliTimestampDarwin() u64 { - //Sources suggest MacOS 10.12 has support for - // posix clock_gettime. var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - const sec_ms = @intCast(u64, tv.tv_sec) * ms_per_s; - const usec_ms = @divFloor(@intCast(u64, tv.tv_usec), us_per_s / ms_per_s); - return u64(sec_ms) + u64(usec_ms); + const sec_ms = tv.tv_sec * ms_per_s; + const usec_ms = @divFloor(tv.tv_usec, us_per_s / ms_per_s); + return @intCast(u64, sec_ms + usec_ms); } fn milliTimestampPosix() u64 { @@ -179,7 +178,7 @@ pub const Timer = struct { debug.assert(err != windows.FALSE); self.start_time = @intCast(u64, start_time); }, - Os.linux => { + Os.linux, Os.freebsd => { //On Linux, seccomp can do arbitrary things to our ability to call // syscalls, including return any errno value it wants and // inconsistently throwing errors. Since we can't account for @@ -215,7 +214,7 @@ pub const Timer = struct { var clock = clockNative() - self.start_time; return switch (builtin.os) { Os.windows => @divFloor(clock * ns_per_s, self.frequency), - Os.linux => clock, + Os.linux, Os.freebsd => clock, Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), else => @compileError("Unsupported OS"), }; @@ -236,7 +235,7 @@ pub const Timer = struct { const clockNative = switch (builtin.os) { Os.windows => clockWindows, - Os.linux => clockLinux, + Os.linux, Os.freebsd => clockLinux, Os.macosx, Os.ios => clockDarwin, else => @compileError("Unsupported OS"), }; @@ -272,7 +271,7 @@ test "os.time.timestamp" { sleep(ns_per_ms); const time_1 = milliTimestamp(); const interval = time_1 - time_0; - debug.assert(interval > 0 and interval < margin); + testing.expect(interval > 0 and interval < margin); } test "os.time.Timer" { @@ -282,11 +281,11 @@ test "os.time.Timer" { var timer = try Timer.start(); sleep(10 * ns_per_ms); const time_0 = timer.read(); - debug.assert(time_0 > 0 and time_0 < margin); + testing.expect(time_0 > 0 and time_0 < margin); const time_1 = timer.lap(); - debug.assert(time_1 >= time_0); + testing.expect(time_1 >= time_0); timer.reset(); - debug.assert(timer.read() < time_1); + testing.expect(timer.read() < time_1); } diff --git a/std/os/uefi.zig b/std/os/uefi.zig new file mode 100644 index 0000000000..8ed60d9c9b --- /dev/null +++ b/std/os/uefi.zig @@ -0,0 +1,2 @@ +// TODO this is where the extern declarations go. For example, see +// inc/efilib.h in gnu-efi-code diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 3d6ee67113..8e9ed8b8db 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -49,6 +49,10 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const ULONG = u32; +pub const LONG = i32; +pub const ULONGLONG = u64; +pub const LONGLONG = i64; pub const TRUE = 1; pub const FALSE = 0; @@ -379,3 +383,17 @@ pub const COORD = extern struct { }; pub const CREATE_UNICODE_ENVIRONMENT = 1024; + +pub const TLS_OUT_OF_INDEXES = 4294967295; +pub const IMAGE_TLS_DIRECTORY = extern struct { + StartAddressOfRawData: usize, + EndAddressOfRawData: usize, + AddressOfIndex: usize, + AddressOfCallBacks: usize, + SizeOfZeroFill: u32, + Characteristics: u32, +}; +pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; +pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; + +pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7eec5faba9..ce8443de02 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -50,7 +50,7 @@ pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFi 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" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsW(penv: [*]u16) BOOL; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; @@ -63,9 +63,9 @@ pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lp pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD; -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsW() ?[*]u16; -pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; +pub extern "kernel32" stdcallcc fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) DWORD; pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; @@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; +pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD; + +pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL; + pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; pub extern "kernel32" stdcallcc fn WriteFile( @@ -220,3 +224,50 @@ pub const FOREGROUND_BLUE = 1; pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; + +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 const LIST_ENTRY = extern struct { + Flink: *LIST_ENTRY, + Blink: *LIST_ENTRY, +}; + +pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { + Type: WORD, + CreatorBackTraceIndex: WORD, + CriticalSection: *RTL_CRITICAL_SECTION, + ProcessLocksList: LIST_ENTRY, + EntryCount: DWORD, + ContentionCount: DWORD, + Flags: DWORD, + CreatorBackTraceIndexHigh: WORD, + SpareWORD: WORD, +}; + +pub const RTL_CRITICAL_SECTION = extern struct { + DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, + LockCount: LONG, + RecursionCount: LONG, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR, +}; + +pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; + +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; + +pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; + +pub const RTL_RUN_ONCE = extern struct { + Ptr: ?*c_void, +}; + +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; diff --git a/std/os/windows/tls.zig b/std/os/windows/tls.zig new file mode 100644 index 0000000000..9e62a7c5c6 --- /dev/null +++ b/std/os/windows/tls.zig @@ -0,0 +1,36 @@ +const std = @import("../../index.zig"); + +export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; +export var _tls_start: u8 linksection(".tls") = 0; +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; + +// 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. +//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY { +// .StartAddressOfRawData = @ptrToInt(&_tls_start), +// .EndAddressOfRawData = @ptrToInt(&_tls_end), +// .AddressOfIndex = @ptrToInt(&_tls_index), +// .AddressOfCallBacks = @ptrToInt(__xl_a), +// .SizeOfZeroFill = 0, +// .Characteristics = 0, +//}; +// This is the workaround because we can't do @ptrToInt at comptime like that. +pub const IMAGE_TLS_DIRECTORY = extern struct { + StartAddressOfRawData: *c_void, + EndAddressOfRawData: *c_void, + AddressOfIndex: *c_void, + AddressOfCallBacks: *c_void, + SizeOfZeroFill: u32, + Characteristics: u32, +}; +export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY { + .StartAddressOfRawData = &_tls_start, + .EndAddressOfRawData = &_tls_end, + .AddressOfIndex = &_tls_index, + .AddressOfCallBacks = &__xl_a, + .SizeOfZeroFill = 0, + .Characteristics = 0, +}; diff --git a/std/pdb.zig b/std/pdb.zig index 2c5df3e597..b6e48ad2f7 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -9,6 +9,10 @@ const coff = std.coff; const ArrayList = std.ArrayList; +// Note: most of this is based on information gathered from LLVM source code, +// documentation and/or contributors. + + // https://llvm.org/docs/PDB/DbiStream.html#stream-header pub const DbiStreamHeader = packed struct { VersionSignature: i32, @@ -345,6 +349,10 @@ pub const RecordPrefix = packed struct { RecordKind: SymbolKind, }; +/// The following variable length array appears immediately after the header. +/// The structure definition follows. +/// LineBlockFragmentHeader Blocks[] +/// Each `LineBlockFragmentHeader` as specified below. pub const LineFragmentHeader = packed struct { /// Code offset of line contribution. RelocOffset: u32, @@ -386,7 +394,11 @@ pub const LineNumberEntry = packed struct { /// TODO runtime crash when I make the actual type of Flags this const Flags = packed struct { + /// Start line number Start: u24, + + /// Delta of lines to the end of the expression. Still unclear. + // TODO figure out the point of this field. End: u7, IsStatement: bool, }; @@ -508,11 +520,11 @@ const Msf = struct { allocator, ); - const stream_count = try self.directory.stream.readIntLe(u32); + const stream_count = try self.directory.stream.readIntLittle(u32); const stream_sizes = try allocator.alloc(u32, stream_count); for (stream_sizes) |*s| { - const size = try self.directory.stream.readIntLe(u32); + const size = try self.directory.stream.readIntLittle(u32); s.* = blockCountFromSize(size, superblock.BlockSize); } @@ -603,7 +615,7 @@ const MsfStream = struct { var i: u32 = 0; while (i < block_count) : (i += 1) { - stream.blocks[i] = try in.readIntLe(u32); + stream.blocks[i] = try in.readIntLittle(u32); } return stream; diff --git a/std/rand/index.zig b/std/rand/index.zig index 97101bc3b7..12dd763aee 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -5,7 +5,7 @@ // ``` // var buf: [8]u8 = undefined; // try std.os.getRandomBytes(buf[0..]); -// const seed = mem.readIntLE(u64, buf[0..8]); +// const seed = mem.readIntSliceLittle(u64, buf[0..8]); // // var r = DefaultPrng.init(seed); // @@ -17,6 +17,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; +const expect = std.testing.expect; const mem = std.mem; const math = std.math; const ziggurat = @import("ziggurat.zig"); @@ -52,7 +53,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.readIntLE(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); } @@ -69,6 +70,7 @@ pub const Random = struct { return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than)); } } + /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. /// This function assumes that the underlying ::fillFn produces evenly distributed values. /// Within this assumption, the runtime of this function is exponentially distributed. @@ -123,6 +125,7 @@ pub const Random = struct { } return r.uintLessThanBiased(T, at_most + 1); } + /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. /// See ::uintLessThan, which this function uses in most cases, /// for commentary on the runtime of this function. @@ -151,6 +154,7 @@ pub const Random = struct { return at_least + r.uintLessThanBiased(T, less_than - at_least); } } + /// Returns an evenly distributed random integer `at_least <= i < less_than`. /// See ::uintLessThan, which this function uses in most cases, /// for commentary on the runtime of this function. @@ -185,6 +189,7 @@ pub const Random = struct { return at_least + r.uintAtMostBiased(T, at_most - at_least); } } + /// Returns an evenly distributed random integer `at_least <= i <= at_most`. /// See ::uintLessThan, which this function uses in most cases, /// for commentary on the runtime of this function. @@ -312,43 +317,43 @@ test "Random int" { fn testRandomInt() void { var r = SequentialPrng.init(); - assert(r.random.int(u0) == 0); + expect(r.random.int(u0) == 0); r.next_value = 0; - assert(r.random.int(u1) == 0); - assert(r.random.int(u1) == 1); - assert(r.random.int(u2) == 2); - assert(r.random.int(u2) == 3); - assert(r.random.int(u2) == 0); + expect(r.random.int(u1) == 0); + expect(r.random.int(u1) == 1); + expect(r.random.int(u2) == 2); + expect(r.random.int(u2) == 3); + expect(r.random.int(u2) == 0); r.next_value = 0xff; - assert(r.random.int(u8) == 0xff); + expect(r.random.int(u8) == 0xff); r.next_value = 0x11; - assert(r.random.int(u8) == 0x11); + expect(r.random.int(u8) == 0x11); r.next_value = 0xff; - assert(r.random.int(u32) == 0xffffffff); + expect(r.random.int(u32) == 0xffffffff); r.next_value = 0x11; - assert(r.random.int(u32) == 0x11111111); + expect(r.random.int(u32) == 0x11111111); r.next_value = 0xff; - assert(r.random.int(i32) == -1); + expect(r.random.int(i32) == -1); r.next_value = 0x11; - assert(r.random.int(i32) == 0x11111111); + expect(r.random.int(i32) == 0x11111111); r.next_value = 0xff; - assert(r.random.int(i8) == -1); + expect(r.random.int(i8) == -1); r.next_value = 0x11; - assert(r.random.int(i8) == 0x11); + expect(r.random.int(i8) == 0x11); r.next_value = 0xff; - assert(r.random.int(u33) == 0x1ffffffff); + expect(r.random.int(u33) == 0x1ffffffff); r.next_value = 0xff; - assert(r.random.int(i1) == -1); + expect(r.random.int(i1) == -1); r.next_value = 0xff; - assert(r.random.int(i2) == -1); + expect(r.random.int(i2) == -1); r.next_value = 0xff; - assert(r.random.int(i33) == -1); + expect(r.random.int(i33) == -1); } test "Random boolean" { @@ -357,10 +362,10 @@ test "Random boolean" { } fn testRandomBoolean() void { var r = SequentialPrng.init(); - assert(r.random.boolean() == false); - assert(r.random.boolean() == true); - assert(r.random.boolean() == false); - assert(r.random.boolean() == true); + expect(r.random.boolean() == false); + expect(r.random.boolean() == true); + expect(r.random.boolean() == false); + expect(r.random.boolean() == true); } test "Random intLessThan" { @@ -371,36 +376,36 @@ test "Random intLessThan" { fn testRandomIntLessThan() void { var r = SequentialPrng.init(); r.next_value = 0xff; - assert(r.random.uintLessThan(u8, 4) == 3); - assert(r.next_value == 0); - assert(r.random.uintLessThan(u8, 4) == 0); - assert(r.next_value == 1); + expect(r.random.uintLessThan(u8, 4) == 3); + expect(r.next_value == 0); + expect(r.random.uintLessThan(u8, 4) == 0); + expect(r.next_value == 1); r.next_value = 0; - assert(r.random.uintLessThan(u64, 32) == 0); + expect(r.random.uintLessThan(u64, 32) == 0); // trigger the bias rejection code path r.next_value = 0; - assert(r.random.uintLessThan(u8, 3) == 0); + expect(r.random.uintLessThan(u8, 3) == 0); // verify we incremented twice - assert(r.next_value == 2); + expect(r.next_value == 2); r.next_value = 0xff; - assert(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); + expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); r.next_value = 0xff; - assert(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); + expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); r.next_value = 0xff; - assert(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); + expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); r.next_value = 0xff; - assert(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); + expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); r.next_value = 0xff; - assert(r.random.intRangeLessThan(i8, -0x80, 0) == -1); + expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); r.next_value = 0xff; - assert(r.random.intRangeLessThan(i3, -4, 0) == -1); + expect(r.random.intRangeLessThan(i3, -4, 0) == -1); r.next_value = 0xff; - assert(r.random.intRangeLessThan(i3, -2, 2) == 1); + expect(r.random.intRangeLessThan(i3, -2, 2) == 1); } test "Random intAtMost" { @@ -411,34 +416,34 @@ test "Random intAtMost" { fn testRandomIntAtMost() void { var r = SequentialPrng.init(); r.next_value = 0xff; - assert(r.random.uintAtMost(u8, 3) == 3); - assert(r.next_value == 0); - assert(r.random.uintAtMost(u8, 3) == 0); + expect(r.random.uintAtMost(u8, 3) == 3); + expect(r.next_value == 0); + expect(r.random.uintAtMost(u8, 3) == 0); // trigger the bias rejection code path r.next_value = 0; - assert(r.random.uintAtMost(u8, 2) == 0); + expect(r.random.uintAtMost(u8, 2) == 0); // verify we incremented twice - assert(r.next_value == 2); + expect(r.next_value == 2); r.next_value = 0xff; - assert(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); + expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); r.next_value = 0xff; - assert(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); + expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); r.next_value = 0xff; - assert(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); + expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); r.next_value = 0xff; - assert(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); + expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); r.next_value = 0xff; - assert(r.random.intRangeAtMost(i8, -0x80, -1) == -1); + expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); r.next_value = 0xff; - assert(r.random.intRangeAtMost(i3, -4, -1) == -1); + expect(r.random.intRangeAtMost(i3, -4, -1) == -1); r.next_value = 0xff; - assert(r.random.intRangeAtMost(i3, -2, 1) == 1); + expect(r.random.intRangeAtMost(i3, -2, 1) == 1); - assert(r.random.uintAtMost(u0, 0) == 0); + expect(r.random.uintAtMost(u0, 0) == 0); } test "Random Biased" { @@ -446,30 +451,30 @@ test "Random Biased" { // Not thoroughly checking the logic here. // Just want to execute all the paths with different types. - assert(r.random.uintLessThanBiased(u1, 1) == 0); - assert(r.random.uintLessThanBiased(u32, 10) < 10); - assert(r.random.uintLessThanBiased(u64, 20) < 20); + expect(r.random.uintLessThanBiased(u1, 1) == 0); + expect(r.random.uintLessThanBiased(u32, 10) < 10); + expect(r.random.uintLessThanBiased(u64, 20) < 20); - assert(r.random.uintAtMostBiased(u0, 0) == 0); - assert(r.random.uintAtMostBiased(u1, 0) <= 0); - assert(r.random.uintAtMostBiased(u32, 10) <= 10); - assert(r.random.uintAtMostBiased(u64, 20) <= 20); + expect(r.random.uintAtMostBiased(u0, 0) == 0); + expect(r.random.uintAtMostBiased(u1, 0) <= 0); + expect(r.random.uintAtMostBiased(u32, 10) <= 10); + expect(r.random.uintAtMostBiased(u64, 20) <= 20); - assert(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); - assert(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); - assert(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); - assert(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); - assert(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); - assert(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); + expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); + expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); + expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); + expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); + expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); + expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); // uncomment for broken module error: - //assert(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); - assert(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); - assert(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); - assert(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); - assert(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); - assert(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); - assert(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); + //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); + expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); + expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); + expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); + expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); + expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); + expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); } // Generator to extend 64-bit seed values into longer sequences. @@ -506,7 +511,7 @@ test "splitmix64 sequence" { }; for (seq) |s| { - std.debug.assert(s == r.next()); + expect(s == r.next()); } } @@ -599,7 +604,7 @@ test "pcg sequence" { }; for (seq) |s| { - std.debug.assert(s == r.next()); + expect(s == r.next()); } } @@ -708,7 +713,7 @@ test "xoroshiro sequence" { }; for (seq1) |s| { - std.debug.assert(s == r.next()); + expect(s == r.next()); } r.jump(); @@ -723,7 +728,7 @@ test "xoroshiro sequence" { }; for (seq2) |s| { - std.debug.assert(s == r.next()); + expect(s == r.next()); } } @@ -926,7 +931,7 @@ test "isaac64 sequence" { }; for (seq) |s| { - std.debug.assert(s == r.next()); + expect(s == r.next()); } } @@ -937,12 +942,12 @@ test "Random float" { var i: usize = 0; while (i < 1000) : (i += 1) { const val1 = prng.random.float(f32); - std.debug.assert(val1 >= 0.0); - std.debug.assert(val1 < 1.0); + expect(val1 >= 0.0); + expect(val1 < 1.0); const val2 = prng.random.float(f64); - std.debug.assert(val2 >= 0.0); - std.debug.assert(val2 < 1.0); + expect(val2 >= 0.0); + expect(val2 < 1.0); } } @@ -956,12 +961,12 @@ test "Random shuffle" { while (i < 1000) : (i += 1) { prng.random.shuffle(u8, seq[0..]); seen[seq[0]] = true; - std.debug.assert(sumArray(seq[0..]) == 10); + expect(sumArray(seq[0..]) == 10); } // we should see every entry at the head at least once for (seen) |e| { - std.debug.assert(e == true); + expect(e == true); } } diff --git a/std/rb.zig b/std/rb.zig index beb7f3aae9..27e60dca6a 100644 --- a/std/rb.zig +++ b/std/rb.zig @@ -1,5 +1,6 @@ const std = @import("index.zig"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; // For mem.Compare const Color = enum(u1) { @@ -533,13 +534,13 @@ test "rb" { _ = tree.insert(&ns[8].node); _ = tree.insert(&ns[9].node); tree.remove(&ns[3].node); - assert(tree.insert(&dup.node) == &ns[7].node); + testing.expect(tree.insert(&dup.node) == &ns[7].node); try tree.replace(&ns[7].node, &dup.node); var num: *testNumber = undefined; num = testGetNumber(tree.first().?); while (num.node.next() != null) { - assert(testGetNumber(num.node.next().?).value > num.value); + testing.expect(testGetNumber(num.node.next().?).value > num.value); num = testGetNumber(num.node.next().?); } } diff --git a/std/segmented_list.zig b/std/segmented_list.zig index d786e0becd..26b7fa48f5 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -1,5 +1,6 @@ const std = @import("index.zig"); const assert = std.debug.assert; +const testing = std.testing; const Allocator = std.mem.Allocator; // Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box @@ -352,14 +353,14 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { var i: usize = 0; while (i < 100) : (i += 1) { try list.push(@intCast(i32, i + 1)); - assert(list.len == i + 1); + testing.expect(list.len == i + 1); } } { var i: usize = 0; while (i < 100) : (i += 1) { - assert(list.at(i).* == @intCast(i32, i + 1)); + testing.expect(list.at(i).* == @intCast(i32, i + 1)); } } @@ -368,35 +369,35 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { var x: i32 = 0; while (it.next()) |item| { x += 1; - assert(item.* == x); + testing.expect(item.* == x); } - assert(x == 100); + testing.expect(x == 100); while (it.prev()) |item| : (x -= 1) { - assert(item.* == x); + testing.expect(item.* == x); } - assert(x == 0); + testing.expect(x == 0); } - assert(list.pop().? == 100); - assert(list.len == 99); + testing.expect(list.pop().? == 100); + testing.expect(list.len == 99); try list.pushMany([]i32{ 1, 2, 3, }); - assert(list.len == 102); - assert(list.pop().? == 3); - assert(list.pop().? == 2); - assert(list.pop().? == 1); - assert(list.len == 99); + 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([]const i32{}); - assert(list.len == 99); + testing.expect(list.len == 99); var i: i32 = 99; while (list.pop()) |item| : (i -= 1) { - assert(item == i); + testing.expect(item == i); list.shrinkCapacity(list.len); } } diff --git a/std/sort.zig b/std/sort.zig index f29f51b7cf..86a6724fee 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -1,5 +1,6 @@ const std = @import("index.zig"); const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; const math = std.math; const builtin = @import("builtin"); @@ -1031,8 +1032,8 @@ fn testStableSort() void { for (cases) |*case| { insertionSort(IdAndValue, (case.*)[0..], cmpByValue); for (case.*) |item, i| { - assert(item.id == expected[i].id); - assert(item.value == expected[i].value); + testing.expect(item.id == expected[i].id); + testing.expect(item.value == expected[i].value); } } } @@ -1077,7 +1078,7 @@ test "std.sort" { const slice = buf[0..case[0].len]; mem.copy(u8, slice, case[0]); sort(u8, slice, asc(u8)); - assert(mem.eql(u8, slice, case[1])); + testing.expect(mem.eql(u8, slice, case[1])); } const i32cases = [][]const []const i32{ @@ -1112,7 +1113,7 @@ test "std.sort" { const slice = buf[0..case[0].len]; mem.copy(i32, slice, case[0]); sort(i32, slice, asc(i32)); - assert(mem.eql(i32, slice, case[1])); + testing.expect(mem.eql(i32, slice, case[1])); } } @@ -1149,7 +1150,7 @@ test "std.sort descending" { const slice = buf[0..case[0].len]; mem.copy(i32, slice, case[0]); sort(i32, slice, desc(i32)); - assert(mem.eql(i32, slice, case[1])); + testing.expect(mem.eql(i32, slice, case[1])); } } @@ -1157,7 +1158,7 @@ test "another sort case" { var arr = []i32{ 5, 3, 1, 2, 4 }; sort(i32, arr[0..], asc(i32)); - assert(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" { @@ -1185,9 +1186,9 @@ fn fuzzTest(rng: *std.rand.Random) void { var index: usize = 1; while (index < array.len) : (index += 1) { if (array[index].value == array[index - 1].value) { - assert(array[index].id > array[index - 1].id); + testing.expect(array[index].id > array[index - 1].id); } else { - assert(array[index].value > array[index - 1].value); + testing.expect(array[index].value > array[index - 1].value); } } } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index a15be317ab..6dcc90b372 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -4,6 +4,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; var argc_ptr: [*]usize = undefined; @@ -20,17 +21,10 @@ comptime { nakedcc fn _start() noreturn { switch (builtin.arch) { - builtin.Arch.x86_64 => switch (builtin.os) { - builtin.Os.freebsd => { - argc_ptr = asm ("lea (%%rdi), %[argc]" - : [argc] "=r" (-> [*]usize) - ); - }, - else => { - argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> [*]usize) - ); - }, + builtin.Arch.x86_64 => { + argc_ptr = asm ("lea (%%rsp), %[argc]" + : [argc] "=r" (-> [*]usize) + ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" @@ -51,7 +45,9 @@ nakedcc fn _start() noreturn { extern fn WinMainCRTStartup() noreturn { @setAlignStack(16); - + if (!builtin.single_threaded) { + _ = @import("../os/windows/tls.zig"); + } std.os.windows.ExitProcess(callMain()); } @@ -68,9 +64,23 @@ fn posixCallMainAndExit() noreturn { while (envp_optional[envp_count]) |_| : (envp_count += 1) {} const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); - std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv); - std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size); + // Scan auxiliary vector. + const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); + std.os.linux_elf_aux_maybe = auxv; + var i: usize = 0; + var at_phdr: usize = 0; + var at_phnum: usize = 0; + var at_phent: usize = 0; + while (auxv[i].a_un.a_val != 0) : (i += 1) { + switch (auxv[i].a_type) { + std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size), + std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, + std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, + std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val, + else => {}, + } + } + if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent); } std.os.posix.exit(callMainWithArgs(argc, argv, envp)); @@ -123,3 +133,41 @@ inline fn callMain() u8 { else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"), } } + +var tls_end_addr: usize = undefined; +const main_thread_tls_align = 32; +var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64; + +fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void { + var phdr_addr = at_phdr; + var n = at_phnum; + var base: usize = 0; + while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) { + const phdr = @intToPtr(*std.elf.Phdr, phdr_addr); + // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917 + switch (phdr.p_type) { + std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr, + std.elf.PT_TLS => std.os.linux_tls_phdr = phdr, + else => continue, + } + } + const tls_phdr = std.os.linux_tls_phdr orelse return; + std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr); + assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage + assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough + @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz); + tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz; + linuxSetThreadArea(@ptrToInt(&tls_end_addr)); +} + +fn linuxSetThreadArea(addr: usize) void { + switch (builtin.arch) { + builtin.Arch.x86_64 => { + const ARCH_SET_FS = 0x1002; + const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr); + // acrh_prctl is documented to never fail + assert(rc == 0); + }, + else => @compileError("Unsupported architecture"), + } +} diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig new file mode 100644 index 0000000000..09413b2328 --- /dev/null +++ b/std/special/compiler_rt/addXf3.zig @@ -0,0 +1,191 @@ +// Ported from: +// +// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc + +const std = @import("std"); +const builtin = @import("builtin"); +const compiler_rt = @import("index.zig"); + +pub extern fn __addtf3(a: f128, b: f128) f128 { + return addXf3(f128, a, b); +} + +pub extern fn __subtf3(a: f128, b: f128) f128 { + const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); + return addXf3(f128, a, neg_b); +} + +inline fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { + const Z = @IntType(false, T.bit_count); + const significandBits = std.math.floatMantissaBits(T); + const implicitBit = Z(1) << significandBits; + + const shift = @clz(significand.*) - @clz(implicitBit); + significand.* <<= @intCast(u7, shift); + return 1 - shift; +} + +inline fn addXf3(comptime T: type, a: T, b: T) T { + const Z = @IntType(false, T.bit_count); + + const typeWidth = T.bit_count; + const significandBits = std.math.floatMantissaBits(T); + const exponentBits = std.math.floatExponentBits(T); + + const signBit = (Z(1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + const exponentBias = (maxExponent >> 1); + + const implicitBit = (Z(1) << significandBits); + const quietBit = implicitBit >> 1; + const significandMask = implicitBit - 1; + + const absMask = signBit - 1; + const exponentMask = absMask ^ significandMask; + const qnanRep = exponentMask | quietBit; + + var aRep = @bitCast(Z, a); + var bRep = @bitCast(Z, b); + const aAbs = aRep & absMask; + const bAbs = bRep & absMask; + + const negative = (aRep & signBit) != 0; + const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; + const significand = (aAbs & significandMask) | implicitBit; + + 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)) + { + // NaN + anything = qNaN + if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit); + // anything + NaN = qNaN + if (bAbs > infRep) return @bitCast(T, @bitCast(Z, b) | quietBit); + + if (aAbs == infRep) { + // +/-infinity + -/+infinity = qNaN + if ((@bitCast(Z, a) ^ @bitCast(Z, b)) == signBit) { + return @bitCast(T, qnanRep); + } + // +/-infinity + anything remaining = +/- infinity + else { + return a; + } + } + + // anything remaining + +/-infinity = +/-infinity + if (bAbs == infRep) return b; + + // zero + anything = anything + if (aAbs == 0) { + // but we need to get the sign right for zero + zero + if (bAbs == 0) { + return @bitCast(T, @bitCast(Z, a) & @bitCast(Z, b)); + } else { + return b; + } + } + + // anything + zero = anything + if (bAbs == 0) return a; + } + + // Swap a and b if necessary so that a has the larger absolute value. + if (bAbs > aAbs) { + const temp = aRep; + aRep = bRep; + bRep = temp; + } + + // Extract the exponent and significand from the (possibly swapped) a and b. + var aExponent = @intCast(i32, (aRep >> significandBits) & maxExponent); + var bExponent = @intCast(i32, (bRep >> significandBits) & maxExponent); + var aSignificand = aRep & significandMask; + var bSignificand = bRep & significandMask; + + // Normalize any denormals, and adjust the exponent accordingly. + if (aExponent == 0) aExponent = normalize(T, &aSignificand); + if (bExponent == 0) bExponent = normalize(T, &bSignificand); + + // The sign of the result is the sign of the larger operand, a. If they + // have opposite signs, we are performing a subtraction; otherwise addition. + const resultSign = aRep & signBit; + const subtraction = (aRep ^ bRep) & signBit != 0; + + // Shift the significands to give us round, guard and sticky, and or in the + // implicit significand bit. (If we fell through from the denormal path it + // was already set by normalize( ), but setting it twice won't hurt + // anything.) + aSignificand = (aSignificand | implicitBit) << 3; + bSignificand = (bSignificand | implicitBit) << 3; + + // Shift the significand of b by the difference in exponents, with a sticky + // bottom bit to get rounding correct. + const @"align" = @intCast(Z, aExponent - bExponent); + if (@"align" != 0) { + if (@"align" < typeWidth) { + const sticky = if (bSignificand << @intCast(u7, typeWidth - @"align") != 0) Z(1) else 0; + bSignificand = (bSignificand >> @truncate(u7, @"align")) | sticky; + } else { + bSignificand = 1; // sticky; b is known to be non-zero. + } + } + if (subtraction) { + aSignificand -= bSignificand; + // If a == -b, return +zero. + if (aSignificand == 0) return @bitCast(T, Z(0)); + + // If partial cancellation occured, we need to left-shift the result + // and adjust the exponent: + if (aSignificand < implicitBit << 3) { + const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3)); + aSignificand <<= @intCast(u7, shift); + aExponent -= shift; + } + } else { // addition + aSignificand += bSignificand; + + // If the addition carried up, we need to right-shift the result and + // adjust the exponent: + if (aSignificand & (implicitBit << 4) != 0) { + const sticky = aSignificand & 1; + aSignificand = aSignificand >> 1 | sticky; + aExponent += 1; + } + } + + // If we have overflowed the type, return +/- infinity: + if (aExponent >= maxExponent) return @bitCast(T, infRep | resultSign); + + if (aExponent <= 0) { + // 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(u7, typeWidth - shift) != 0) Z(1) else 0; + aSignificand = aSignificand >> @intCast(u7, shift | sticky); + aExponent = 0; + } + + // Low three bits are round, guard, and sticky. + const roundGuardSticky = aSignificand & 0x7; + + // Shift the significand into place, and mask off the implicit bit. + var result = (aSignificand >> 3) & significandMask; + + // Insert the exponent and sign. + result |= @intCast(Z, aExponent) << significandBits; + result |= resultSign; + + // Final rounding. The result may overflow to infinity, but that is the + // correct result in that case. + if (roundGuardSticky > 0x4) result += 1; + if (roundGuardSticky == 0x4) result += result & 1; + + return @bitCast(T, result); +} + +test "import addXf3" { + _ = @import("addXf3_test.zig"); +} diff --git a/std/special/compiler_rt/addXf3_test.zig b/std/special/compiler_rt/addXf3_test.zig new file mode 100644 index 0000000000..099b737976 --- /dev/null +++ b/std/special/compiler_rt/addXf3_test.zig @@ -0,0 +1,85 @@ +// Ported from: +// +// 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 __addtf3 = @import("addXf3.zig").__addtf3; + +fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { + const x = __addtf3(a, b); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expected_hi and lo == expected_lo) { + return; + } + // test other possible NaN representation (signal NaN) + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__addtf3 test failure"); +} + +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); + + // inf + inf = inf + test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0); + + // inf + any = inf + test__addtf3(inf128, 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0); + + // any + any + test__addtf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); +} + +const __subtf3 = @import("addXf3.zig").__subtf3; + +fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { + const x = __subtf3(a, b); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expected_hi and lo == expected_lo) { + return; + } + // test other possible NaN representation (signal NaN) + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__subtf3 test failure"); +} + +test "subtf3" { + // qNaN - any = qNaN + 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); + + // inf - any = inf + test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); + + // any + any + test__subtf3(0x1.234567829a3bcdef5678ade36734p+5, 0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x40041b8af1915166, 0xa44a7bca780a166c); +} diff --git a/std/special/compiler_rt/divti3_test.zig b/std/special/compiler_rt/divti3_test.zig index eef5a9b812..e1c1babae7 100644 --- a/std/special/compiler_rt/divti3_test.zig +++ b/std/special/compiler_rt/divti3_test.zig @@ -1,9 +1,9 @@ const __divti3 = @import("divti3.zig").__divti3; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__divti3(a: i128, b: i128, expected: i128) void { const x = __divti3(a, b); - assert(x == expected); + testing.expect(x == expected); } test "divti3" { diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig index 9969607011..050a799823 100644 --- a/std/special/compiler_rt/extendXfYf2_test.zig +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -1,7 +1,6 @@ const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; -const assert = @import("std").debug.assert; fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { const x = __extenddftf2(a); diff --git a/std/special/compiler_rt/fixdfdi.zig b/std/special/compiler_rt/fixdfdi.zig new file mode 100644 index 0000000000..c108fd15aa --- /dev/null +++ b/std/special/compiler_rt/fixdfdi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixdfdi(a: f64) i64 { + @setRuntimeSafety(builtin.is_test); + return fixint(f64, i64, a); +} + +test "import fixdfdi" { + _ = @import("fixdfdi_test.zig"); +} diff --git a/std/special/compiler_rt/fixdfdi_test.zig b/std/special/compiler_rt/fixdfdi_test.zig new file mode 100644 index 0000000000..a55245210b --- /dev/null +++ b/std/special/compiler_rt/fixdfdi_test.zig @@ -0,0 +1,66 @@ +const __fixdfdi = @import("fixdfdi.zig").__fixdfdi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixdfdi" { + //warn("\n"); + + test__fixdfdi(-math.f64_max, math.minInt(i64)); + + test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + test__fixdfdi(-0x1.0000000000000p+127, -0x8000000000000000); + test__fixdfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + test__fixdfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + test__fixdfdi(-0x1.0000000000001p+63, -0x8000000000000000); + test__fixdfdi(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixdfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + test__fixdfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + test__fixdfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + test__fixdfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + test__fixdfdi(-2.01, -2); + test__fixdfdi(-2.0, -2); + test__fixdfdi(-1.99, -1); + test__fixdfdi(-1.0, -1); + test__fixdfdi(-0.99, 0); + test__fixdfdi(-0.5, 0); + test__fixdfdi(-math.f64_min, 0); + test__fixdfdi(0.0, 0); + test__fixdfdi(math.f64_min, 0); + test__fixdfdi(0.5, 0); + test__fixdfdi(0.99, 0); + test__fixdfdi(1.0, 1); + test__fixdfdi(1.5, 1); + test__fixdfdi(1.99, 1); + test__fixdfdi(2.0, 2); + test__fixdfdi(2.01, 2); + + test__fixdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + test__fixdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + test__fixdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + test__fixdfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + test__fixdfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + test__fixdfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + test__fixdfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + test__fixdfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + test__fixdfdi(math.f64_max, math.maxInt(i64)); +} diff --git a/std/special/compiler_rt/fixdfsi.zig b/std/special/compiler_rt/fixdfsi.zig new file mode 100644 index 0000000000..83a17b2b0d --- /dev/null +++ b/std/special/compiler_rt/fixdfsi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixdfsi(a: f64) i32 { + @setRuntimeSafety(builtin.is_test); + return fixint(f64, i32, a); +} + +test "import fixdfsi" { + _ = @import("fixdfsi_test.zig"); +} diff --git a/std/special/compiler_rt/fixdfsi_test.zig b/std/special/compiler_rt/fixdfsi_test.zig new file mode 100644 index 0000000000..5cb9bef1d9 --- /dev/null +++ b/std/special/compiler_rt/fixdfsi_test.zig @@ -0,0 +1,74 @@ +const __fixdfsi = @import("fixdfsi.zig").__fixdfsi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixdfsi" { + //warn("\n"); + + test__fixdfsi(-math.f64_max, math.minInt(i32)); + + test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + test__fixdfsi(-0x1.0000000000000p+127, -0x80000000); + test__fixdfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + test__fixdfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + test__fixdfsi(-0x1.0000000000001p+63, -0x80000000); + test__fixdfsi(-0x1.0000000000000p+63, -0x80000000); + test__fixdfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + test__fixdfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + test__fixdfsi(-0x1.FFFFFEp+62, -0x80000000); + test__fixdfsi(-0x1.FFFFFCp+62, -0x80000000); + + test__fixdfsi(-0x1.000000p+31, -0x80000000); + test__fixdfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + test__fixdfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + + test__fixdfsi(-2.01, -2); + test__fixdfsi(-2.0, -2); + test__fixdfsi(-1.99, -1); + test__fixdfsi(-1.0, -1); + test__fixdfsi(-0.99, 0); + test__fixdfsi(-0.5, 0); + test__fixdfsi(-math.f64_min, 0); + test__fixdfsi(0.0, 0); + test__fixdfsi(math.f64_min, 0); + test__fixdfsi(0.5, 0); + test__fixdfsi(0.99, 0); + test__fixdfsi(1.0, 1); + test__fixdfsi(1.5, 1); + test__fixdfsi(1.99, 1); + test__fixdfsi(2.0, 2); + test__fixdfsi(2.01, 2); + + test__fixdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + test__fixdfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + test__fixdfsi(0x1.000000p+31, 0x7FFFFFFF); + + test__fixdfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + test__fixdfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + test__fixdfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + test__fixdfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + test__fixdfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + test__fixdfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + test__fixdfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + test__fixdfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + test__fixdfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + test__fixdfsi(math.f64_max, math.maxInt(i32)); +} diff --git a/std/special/compiler_rt/fixdfti.zig b/std/special/compiler_rt/fixdfti.zig new file mode 100644 index 0000000000..e30f885cf6 --- /dev/null +++ b/std/special/compiler_rt/fixdfti.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixdfti(a: f64) i128 { + @setRuntimeSafety(builtin.is_test); + return fixint(f64, i128, a); +} + +test "import fixdfti" { + _ = @import("fixdfti_test.zig"); +} diff --git a/std/special/compiler_rt/fixdfti_test.zig b/std/special/compiler_rt/fixdfti_test.zig new file mode 100644 index 0000000000..5fbcd206ed --- /dev/null +++ b/std/special/compiler_rt/fixdfti_test.zig @@ -0,0 +1,66 @@ +const __fixdfti = @import("fixdfti.zig").__fixdfti; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixdfti" { + //warn("\n"); + + test__fixdfti(-math.f64_max, math.minInt(i128)); + + test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + test__fixdfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + test__fixdfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + test__fixdfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + test__fixdfti(-0x1.0000000000001p+63, -0x8000000000000800); + test__fixdfti(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixdfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + test__fixdfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + test__fixdfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + test__fixdfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + test__fixdfti(-2.01, -2); + test__fixdfti(-2.0, -2); + test__fixdfti(-1.99, -1); + test__fixdfti(-1.0, -1); + test__fixdfti(-0.99, 0); + test__fixdfti(-0.5, 0); + test__fixdfti(-math.f64_min, 0); + test__fixdfti(0.0, 0); + test__fixdfti(math.f64_min, 0); + test__fixdfti(0.5, 0); + test__fixdfti(0.99, 0); + test__fixdfti(1.0, 1); + test__fixdfti(1.5, 1); + test__fixdfti(1.99, 1); + test__fixdfti(2.0, 2); + test__fixdfti(2.01, 2); + + test__fixdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + test__fixdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + test__fixdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + test__fixdfti(0x1.0000000000000p+63, 0x8000000000000000); + test__fixdfti(0x1.0000000000001p+63, 0x8000000000000800); + + test__fixdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + test__fixdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + test__fixdfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + test__fixdfti(math.f64_max, math.maxInt(i128)); +} diff --git a/std/special/compiler_rt/fixint.zig b/std/special/compiler_rt/fixint.zig new file mode 100644 index 0000000000..fd31798cc2 --- /dev/null +++ b/std/special/compiler_rt/fixint.zig @@ -0,0 +1,74 @@ +const is_test = @import("builtin").is_test; +const std = @import("std"); +const math = std.math; +const Log2Int = std.math.Log2Int; +const maxInt = std.math.maxInt; +const minInt = std.math.minInt; + +const DBG = false; + +pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { + @setRuntimeSafety(is_test); + + const rep_t = switch (fp_t) { + f32 => u32, + f64 => u64, + f128 => u128, + else => unreachable, + }; + const significandBits = switch (fp_t) { + f32 => 23, + f64 => 52, + f128 => 112, + else => unreachable, + }; + + const typeWidth = rep_t.bit_count; + const exponentBits = (typeWidth - significandBits - 1); + const signBit = (rep_t(1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + const exponentBias = (maxExponent >> 1); + + const implicitBit = (rep_t(1) << significandBits); + const significandMask = (implicitBit - 1); + + // Break a into sign, exponent, significand + const aRep: rep_t = @bitCast(rep_t, a); + const absMask = signBit - 1; + const aAbs: rep_t = aRep & absMask; + + const negative = (aRep & signBit) != 0; + const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; + const significand: rep_t = (aAbs & significandMask) | implicitBit; + + // If exponent is negative, the uint_result is zero. + if (exponent < 0) return 0; + + // The unsigned result needs to be large enough to handle an fixint_t or rep_t + const fixuint_t = @IntType(false, fixint_t.bit_count); + const UintResultType = if (fixint_t.bit_count > rep_t.bit_count) fixuint_t else rep_t; + var uint_result: UintResultType = undefined; + + // 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)); + } + + // If 0 <= exponent < significandBits, right shift else left shift + if (exponent < significandBits) { + uint_result = @intCast(UintResultType, significand) >> @intCast(Log2Int(UintResultType), significandBits - exponent); + } else { + uint_result = @intCast(UintResultType, significand) << @intCast(Log2Int(UintResultType), exponent - significandBits); + } + + // Cast to final signed result + if (negative) { + return if (uint_result >= -math.minInt(fixint_t)) math.minInt(fixint_t) else -@intCast(fixint_t, uint_result); + } else { + return if (uint_result >= math.maxInt(fixint_t)) math.maxInt(fixint_t) else @intCast(fixint_t, uint_result); + } +} + +test "import fixint" { + _ = @import("fixint_test.zig"); +} diff --git a/std/special/compiler_rt/fixint_test.zig b/std/special/compiler_rt/fixint_test.zig new file mode 100644 index 0000000000..0b0936d1e2 --- /dev/null +++ b/std/special/compiler_rt/fixint_test.zig @@ -0,0 +1,152 @@ +const is_test = @import("builtin").is_test; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +const warn = std.debug.warn; + +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); + testing.expect(x == expected); +} + +test "fixint.i1" { + test__fixint(f32, i1, -math.inf_f32, -1); + test__fixint(f32, i1, -math.f32_max, -1); + test__fixint(f32, i1, -2.0, -1); + test__fixint(f32, i1, -1.1, -1); + test__fixint(f32, i1, -1.0, -1); + test__fixint(f32, i1, -0.9, 0); + test__fixint(f32, i1, -0.1, 0); + test__fixint(f32, i1, -math.f32_min, 0); + test__fixint(f32, i1, -0.0, 0); + test__fixint(f32, i1, 0.0, 0); + test__fixint(f32, i1, math.f32_min, 0); + test__fixint(f32, i1, 0.1, 0); + test__fixint(f32, i1, 0.9, 0); + test__fixint(f32, i1, 1.0, 0); + test__fixint(f32, i1, 2.0, 0); + test__fixint(f32, i1, math.f32_max, 0); + test__fixint(f32, i1, math.inf_f32, 0); +} + +test "fixint.i2" { + test__fixint(f32, i2, -math.inf_f32, -2); + test__fixint(f32, i2, -math.f32_max, -2); + test__fixint(f32, i2, -2.0, -2); + test__fixint(f32, i2, -1.9, -1); + test__fixint(f32, i2, -1.1, -1); + test__fixint(f32, i2, -1.0, -1); + test__fixint(f32, i2, -0.9, 0); + test__fixint(f32, i2, -0.1, 0); + test__fixint(f32, i2, -math.f32_min, 0); + test__fixint(f32, i2, -0.0, 0); + test__fixint(f32, i2, 0.0, 0); + test__fixint(f32, i2, math.f32_min, 0); + test__fixint(f32, i2, 0.1, 0); + test__fixint(f32, i2, 0.9, 0); + test__fixint(f32, i2, 1.0, 1); + test__fixint(f32, i2, 2.0, 1); + test__fixint(f32, i2, math.f32_max, 1); + test__fixint(f32, i2, math.inf_f32, 1); +} + +test "fixint.i3" { + test__fixint(f32, i3, -math.inf_f32, -4); + test__fixint(f32, i3, -math.f32_max, -4); + test__fixint(f32, i3, -4.0, -4); + test__fixint(f32, i3, -3.0, -3); + test__fixint(f32, i3, -2.0, -2); + test__fixint(f32, i3, -1.9, -1); + test__fixint(f32, i3, -1.1, -1); + test__fixint(f32, i3, -1.0, -1); + test__fixint(f32, i3, -0.9, 0); + test__fixint(f32, i3, -0.1, 0); + test__fixint(f32, i3, -math.f32_min, 0); + test__fixint(f32, i3, -0.0, 0); + test__fixint(f32, i3, 0.0, 0); + test__fixint(f32, i3, math.f32_min, 0); + test__fixint(f32, i3, 0.1, 0); + test__fixint(f32, i3, 0.9, 0); + test__fixint(f32, i3, 1.0, 1); + test__fixint(f32, i3, 2.0, 2); + test__fixint(f32, i3, 3.0, 3); + test__fixint(f32, i3, 4.0, 3); + test__fixint(f32, i3, math.f32_max, 3); + test__fixint(f32, i3, math.inf_f32, 3); +} + +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, -2.0, -2); + test__fixint(f64, i32, -1.9, -1); + test__fixint(f64, i32, -1.1, -1); + test__fixint(f64, i32, -1.0, -1); + test__fixint(f64, i32, -0.9, 0); + test__fixint(f64, i32, -0.1, 0); + test__fixint(f64, i32, -math.f32_min, 0); + test__fixint(f64, i32, -0.0, 0); + test__fixint(f64, i32, 0.0, 0); + test__fixint(f64, i32, math.f32_min, 0); + 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, math.f64_max, math.maxInt(i32)); + test__fixint(f64, i32, math.inf_f64, math.maxInt(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, -2.0, -2); + test__fixint(f64, i64, -1.9, -1); + test__fixint(f64, i64, -1.1, -1); + test__fixint(f64, i64, -1.0, -1); + test__fixint(f64, i64, -0.9, 0); + test__fixint(f64, i64, -0.1, 0); + test__fixint(f64, i64, -math.f32_min, 0); + test__fixint(f64, i64, -0.0, 0); + test__fixint(f64, i64, 0.0, 0); + test__fixint(f64, i64, math.f32_min, 0); + 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, math.f64_max, math.maxInt(i64)); + test__fixint(f64, i64, math.inf_f64, math.maxInt(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, -2.0, -2); + test__fixint(f64, i128, -1.9, -1); + test__fixint(f64, i128, -1.1, -1); + test__fixint(f64, i128, -1.0, -1); + test__fixint(f64, i128, -0.9, 0); + test__fixint(f64, i128, -0.1, 0); + test__fixint(f64, i128, -math.f32_min, 0); + test__fixint(f64, i128, -0.0, 0); + test__fixint(f64, i128, 0.0, 0); + test__fixint(f64, i128, math.f32_min, 0); + 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, math.f64_max, math.maxInt(i128)); + test__fixint(f64, i128, math.inf_f64, math.maxInt(i128)); +} diff --git a/std/special/compiler_rt/fixsfdi.zig b/std/special/compiler_rt/fixsfdi.zig new file mode 100644 index 0000000000..ffa81d13ab --- /dev/null +++ b/std/special/compiler_rt/fixsfdi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixsfdi(a: f32) i64 { + @setRuntimeSafety(builtin.is_test); + return fixint(f32, i64, a); +} + +test "import fixsfdi" { + _ = @import("fixsfdi_test.zig"); +} diff --git a/std/special/compiler_rt/fixsfdi_test.zig b/std/special/compiler_rt/fixsfdi_test.zig new file mode 100644 index 0000000000..276670421b --- /dev/null +++ b/std/special/compiler_rt/fixsfdi_test.zig @@ -0,0 +1,68 @@ +const __fixsfdi = @import("fixsfdi.zig").__fixsfdi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixsfdi" { + //warn("\n"); + + test__fixsfdi(-math.f32_max, math.minInt(i64)); + + test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + test__fixsfdi(-0x1.0000000000000p+127, -0x8000000000000000); + test__fixsfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + test__fixsfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + test__fixsfdi(-0x1.0000000000001p+63, -0x8000000000000000); + test__fixsfdi(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixsfdi(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + test__fixsfdi(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + test__fixsfdi(-0x1.FFFFFFp+62, -0x8000000000000000); + test__fixsfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + test__fixsfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + test__fixsfdi(-2.01, -2); + test__fixsfdi(-2.0, -2); + test__fixsfdi(-1.99, -1); + test__fixsfdi(-1.0, -1); + test__fixsfdi(-0.99, 0); + test__fixsfdi(-0.5, 0); + test__fixsfdi(-math.f32_min, 0); + test__fixsfdi(0.0, 0); + test__fixsfdi(math.f32_min, 0); + test__fixsfdi(0.5, 0); + test__fixsfdi(0.99, 0); + test__fixsfdi(1.0, 1); + test__fixsfdi(1.5, 1); + test__fixsfdi(1.99, 1); + test__fixsfdi(2.0, 2); + test__fixsfdi(2.01, 2); + + test__fixsfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixsfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + test__fixsfdi(0x1.FFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + + test__fixsfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + test__fixsfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + test__fixsfdi(math.f64_max, math.maxInt(i64)); +} diff --git a/std/special/compiler_rt/fixsfsi.zig b/std/special/compiler_rt/fixsfsi.zig new file mode 100644 index 0000000000..9a94b4395b --- /dev/null +++ b/std/special/compiler_rt/fixsfsi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixsfsi(a: f32) i32 { + @setRuntimeSafety(builtin.is_test); + return fixint(f32, i32, a); +} + +test "import fixsfsi" { + _ = @import("fixsfsi_test.zig"); +} diff --git a/std/special/compiler_rt/fixsfsi_test.zig b/std/special/compiler_rt/fixsfsi_test.zig new file mode 100644 index 0000000000..a05c15e536 --- /dev/null +++ b/std/special/compiler_rt/fixsfsi_test.zig @@ -0,0 +1,76 @@ +const __fixsfsi = @import("fixsfsi.zig").__fixsfsi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixsfsi" { + //warn("\n"); + + test__fixsfsi(-math.f32_max, math.minInt(i32)); + + test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + test__fixsfsi(-0x1.0000000000000p+127, -0x80000000); + test__fixsfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + test__fixsfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + test__fixsfsi(-0x1.0000000000001p+63, -0x80000000); + test__fixsfsi(-0x1.0000000000000p+63, -0x80000000); + test__fixsfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + test__fixsfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + test__fixsfsi(-0x1.FFFFFEp+62, -0x80000000); + test__fixsfsi(-0x1.FFFFFCp+62, -0x80000000); + + test__fixsfsi(-0x1.000000p+31, -0x80000000); + test__fixsfsi(-0x1.FFFFFFp+30, -0x80000000); + test__fixsfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + test__fixsfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + test__fixsfsi(-2.01, -2); + test__fixsfsi(-2.0, -2); + test__fixsfsi(-1.99, -1); + test__fixsfsi(-1.0, -1); + test__fixsfsi(-0.99, 0); + test__fixsfsi(-0.5, 0); + test__fixsfsi(-math.f32_min, 0); + test__fixsfsi(0.0, 0); + test__fixsfsi(math.f32_min, 0); + test__fixsfsi(0.5, 0); + test__fixsfsi(0.99, 0); + test__fixsfsi(1.0, 1); + test__fixsfsi(1.5, 1); + test__fixsfsi(1.99, 1); + test__fixsfsi(2.0, 2); + test__fixsfsi(2.01, 2); + + test__fixsfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + test__fixsfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + test__fixsfsi(0x1.FFFFFFp+30, 0x7FFFFFFF); + test__fixsfsi(0x1.000000p+31, 0x7FFFFFFF); + + test__fixsfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + test__fixsfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + test__fixsfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + test__fixsfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + test__fixsfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + test__fixsfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + test__fixsfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + test__fixsfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + test__fixsfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + test__fixsfsi(math.f32_max, math.maxInt(i32)); +} diff --git a/std/special/compiler_rt/fixsfti.zig b/std/special/compiler_rt/fixsfti.zig new file mode 100644 index 0000000000..806a1678aa --- /dev/null +++ b/std/special/compiler_rt/fixsfti.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixsfti(a: f32) i128 { + @setRuntimeSafety(builtin.is_test); + return fixint(f32, i128, a); +} + +test "import fixsfti" { + _ = @import("fixsfti_test.zig"); +} diff --git a/std/special/compiler_rt/fixsfti_test.zig b/std/special/compiler_rt/fixsfti_test.zig new file mode 100644 index 0000000000..32410e2dee --- /dev/null +++ b/std/special/compiler_rt/fixsfti_test.zig @@ -0,0 +1,84 @@ +const __fixsfti = @import("fixsfti.zig").__fixsfti; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixsfti" { + //warn("\n"); + + test__fixsfti(-math.f32_max, math.minInt(i128)); + + test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + test__fixsfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + test__fixsfti(-0x1.FFFFFFFFFFFFFp+126, -0x80000000000000000000000000000000); + test__fixsfti(-0x1.FFFFFFFFFFFFEp+126, -0x80000000000000000000000000000000); + test__fixsfti(-0x1.FFFFFF0000000p+126, -0x80000000000000000000000000000000); + test__fixsfti(-0x1.FFFFFE0000000p+126, -0x7FFFFF80000000000000000000000000); + test__fixsfti(-0x1.FFFFFC0000000p+126, -0x7FFFFF00000000000000000000000000); + + test__fixsfti(-0x1.0000000000001p+63, -0x8000000000000000); + test__fixsfti(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixsfti(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + test__fixsfti(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + test__fixsfti(-0x1.FFFFFFp+62, -0x8000000000000000); + test__fixsfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + test__fixsfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + test__fixsfti(-0x1.000000p+31, -0x80000000); + test__fixsfti(-0x1.FFFFFFp+30, -0x80000000); + test__fixsfti(-0x1.FFFFFEp+30, -0x7FFFFF80); + test__fixsfti(-0x1.FFFFFCp+30, -0x7FFFFF00); + + test__fixsfti(-2.01, -2); + test__fixsfti(-2.0, -2); + test__fixsfti(-1.99, -1); + test__fixsfti(-1.0, -1); + test__fixsfti(-0.99, 0); + test__fixsfti(-0.5, 0); + test__fixsfti(-math.f32_min, 0); + test__fixsfti(0.0, 0); + test__fixsfti(math.f32_min, 0); + test__fixsfti(0.5, 0); + test__fixsfti(0.99, 0); + test__fixsfti(1.0, 1); + test__fixsfti(1.5, 1); + test__fixsfti(1.99, 1); + test__fixsfti(2.0, 2); + test__fixsfti(2.01, 2); + + test__fixsfti(0x1.FFFFFCp+30, 0x7FFFFF00); + test__fixsfti(0x1.FFFFFEp+30, 0x7FFFFF80); + test__fixsfti(0x1.FFFFFFp+30, 0x80000000); + test__fixsfti(0x1.000000p+31, 0x80000000); + + test__fixsfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixsfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + test__fixsfti(0x1.FFFFFFp+62, 0x8000000000000000); + + test__fixsfti(0x1.FFFFFFFFFFFFEp+62, 0x8000000000000000); + test__fixsfti(0x1.FFFFFFFFFFFFFp+62, 0x8000000000000000); + test__fixsfti(0x1.0000000000000p+63, 0x8000000000000000); + test__fixsfti(0x1.0000000000001p+63, 0x8000000000000000); + + test__fixsfti(0x1.FFFFFC0000000p+126, 0x7FFFFF00000000000000000000000000); + test__fixsfti(0x1.FFFFFE0000000p+126, 0x7FFFFF80000000000000000000000000); + test__fixsfti(0x1.FFFFFF0000000p+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixsfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixsfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixsfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + test__fixsfti(math.f32_max, math.maxInt(i128)); +} diff --git a/std/special/compiler_rt/fixtfdi.zig b/std/special/compiler_rt/fixtfdi.zig new file mode 100644 index 0000000000..8d99231b74 --- /dev/null +++ b/std/special/compiler_rt/fixtfdi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixtfdi(a: f128) i64 { + @setRuntimeSafety(builtin.is_test); + return fixint(f128, i64, a); +} + +test "import fixtfdi" { + _ = @import("fixtfdi_test.zig"); +} diff --git a/std/special/compiler_rt/fixtfdi_test.zig b/std/special/compiler_rt/fixtfdi_test.zig new file mode 100644 index 0000000000..a31bcb7d6f --- /dev/null +++ b/std/special/compiler_rt/fixtfdi_test.zig @@ -0,0 +1,76 @@ +const __fixtfdi = @import("fixtfdi.zig").__fixtfdi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixtfdi" { + //warn("\n"); + + test__fixtfdi(-math.f128_max, math.minInt(i64)); + + test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + test__fixtfdi(-0x1.0000000000000p+127, -0x8000000000000000); + test__fixtfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + test__fixtfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + test__fixtfdi(-0x1.0000000000001p+63, -0x8000000000000000); + test__fixtfdi(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixtfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + test__fixtfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + test__fixtfdi(-0x1.FFFFFEp+62, -0x7FFFFF8000000000); + test__fixtfdi(-0x1.FFFFFCp+62, -0x7FFFFF0000000000); + + test__fixtfdi(-0x1.000000p+31, -0x80000000); + test__fixtfdi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + test__fixtfdi(-0x1.FFFFFEp+30, -0x7FFFFF80); + test__fixtfdi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + test__fixtfdi(-2.01, -2); + test__fixtfdi(-2.0, -2); + test__fixtfdi(-1.99, -1); + test__fixtfdi(-1.0, -1); + test__fixtfdi(-0.99, 0); + test__fixtfdi(-0.5, 0); + test__fixtfdi(-math.f64_min, 0); + test__fixtfdi(0.0, 0); + test__fixtfdi(math.f64_min, 0); + test__fixtfdi(0.5, 0); + test__fixtfdi(0.99, 0); + test__fixtfdi(1.0, 1); + test__fixtfdi(1.5, 1); + test__fixtfdi(1.99, 1); + test__fixtfdi(2.0, 2); + test__fixtfdi(2.01, 2); + + test__fixtfdi(0x1.FFFFFCp+30, 0x7FFFFF00); + test__fixtfdi(0x1.FFFFFEp+30, 0x7FFFFF80); + test__fixtfdi(0x1.FFFFFFp+30, 0x7FFFFFC0); + test__fixtfdi(0x1.000000p+31, 0x80000000); + + test__fixtfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixtfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + test__fixtfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + test__fixtfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + test__fixtfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + test__fixtfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + test__fixtfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + test__fixtfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + test__fixtfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + test__fixtfdi(math.f128_max, math.maxInt(i64)); +} diff --git a/std/special/compiler_rt/fixtfsi.zig b/std/special/compiler_rt/fixtfsi.zig new file mode 100644 index 0000000000..f3f83634b2 --- /dev/null +++ b/std/special/compiler_rt/fixtfsi.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixtfsi(a: f128) i32 { + @setRuntimeSafety(builtin.is_test); + return fixint(f128, i32, a); +} + +test "import fixtfsi" { + _ = @import("fixtfsi_test.zig"); +} diff --git a/std/special/compiler_rt/fixtfsi_test.zig b/std/special/compiler_rt/fixtfsi_test.zig new file mode 100644 index 0000000000..7b37e4e09f --- /dev/null +++ b/std/special/compiler_rt/fixtfsi_test.zig @@ -0,0 +1,76 @@ +const __fixtfsi = @import("fixtfsi.zig").__fixtfsi; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixtfsi" { + //warn("\n"); + + test__fixtfsi(-math.f128_max, math.minInt(i32)); + + test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + test__fixtfsi(-0x1.0000000000000p+127, -0x80000000); + test__fixtfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + test__fixtfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + test__fixtfsi(-0x1.0000000000001p+63, -0x80000000); + test__fixtfsi(-0x1.0000000000000p+63, -0x80000000); + test__fixtfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + test__fixtfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + test__fixtfsi(-0x1.FFFFFEp+62, -0x80000000); + test__fixtfsi(-0x1.FFFFFCp+62, -0x80000000); + + test__fixtfsi(-0x1.000000p+31, -0x80000000); + test__fixtfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + test__fixtfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + test__fixtfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + test__fixtfsi(-2.01, -2); + test__fixtfsi(-2.0, -2); + test__fixtfsi(-1.99, -1); + test__fixtfsi(-1.0, -1); + test__fixtfsi(-0.99, 0); + test__fixtfsi(-0.5, 0); + test__fixtfsi(-math.f32_min, 0); + test__fixtfsi(0.0, 0); + test__fixtfsi(math.f32_min, 0); + test__fixtfsi(0.5, 0); + test__fixtfsi(0.99, 0); + test__fixtfsi(1.0, 1); + test__fixtfsi(1.5, 1); + test__fixtfsi(1.99, 1); + test__fixtfsi(2.0, 2); + test__fixtfsi(2.01, 2); + + test__fixtfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + test__fixtfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + test__fixtfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + test__fixtfsi(0x1.000000p+31, 0x7FFFFFFF); + + test__fixtfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + test__fixtfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + test__fixtfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + test__fixtfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + test__fixtfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + test__fixtfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + test__fixtfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + test__fixtfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + test__fixtfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + test__fixtfsi(math.f128_max, math.maxInt(i32)); +} diff --git a/std/special/compiler_rt/fixtfti.zig b/std/special/compiler_rt/fixtfti.zig new file mode 100644 index 0000000000..07d38f2c3b --- /dev/null +++ b/std/special/compiler_rt/fixtfti.zig @@ -0,0 +1,11 @@ +const fixint = @import("fixint.zig").fixint; +const builtin = @import("builtin"); + +pub extern fn __fixtfti(a: f128) i128 { + @setRuntimeSafety(builtin.is_test); + return fixint(f128, i128, a); +} + +test "import fixtfti" { + _ = @import("fixtfti_test.zig"); +} diff --git a/std/special/compiler_rt/fixtfti_test.zig b/std/special/compiler_rt/fixtfti_test.zig new file mode 100644 index 0000000000..be461a1c91 --- /dev/null +++ b/std/special/compiler_rt/fixtfti_test.zig @@ -0,0 +1,66 @@ +const __fixtfti = @import("fixtfti.zig").__fixtfti; +const std = @import("std"); +const math = std.math; +const testing = std.testing; +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)); + testing.expect(x == expected); +} + +test "fixtfti" { + //warn("\n"); + + test__fixtfti(-math.f128_max, math.minInt(i128)); + + test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + test__fixtfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + test__fixtfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + test__fixtfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + test__fixtfti(-0x1.0000000000001p+63, -0x8000000000000800); + test__fixtfti(-0x1.0000000000000p+63, -0x8000000000000000); + test__fixtfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + test__fixtfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + test__fixtfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + test__fixtfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + test__fixtfti(-2.01, -2); + test__fixtfti(-2.0, -2); + test__fixtfti(-1.99, -1); + test__fixtfti(-1.0, -1); + test__fixtfti(-0.99, 0); + test__fixtfti(-0.5, 0); + test__fixtfti(-math.f128_min, 0); + test__fixtfti(0.0, 0); + test__fixtfti(math.f128_min, 0); + test__fixtfti(0.5, 0); + test__fixtfti(0.99, 0); + test__fixtfti(1.0, 1); + test__fixtfti(1.5, 1); + test__fixtfti(1.99, 1); + test__fixtfti(2.0, 2); + test__fixtfti(2.01, 2); + + test__fixtfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + test__fixtfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + test__fixtfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + test__fixtfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + test__fixtfti(0x1.0000000000000p+63, 0x8000000000000000); + test__fixtfti(0x1.0000000000001p+63, 0x8000000000000800); + + test__fixtfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + test__fixtfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + test__fixtfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + test__fixtfti(math.f128_max, math.maxInt(i128)); +} diff --git a/std/special/compiler_rt/fixunsdfdi_test.zig b/std/special/compiler_rt/fixunsdfdi_test.zig index e59d09f8de..67eeb70520 100644 --- a/std/special/compiler_rt/fixunsdfdi_test.zig +++ b/std/special/compiler_rt/fixunsdfdi_test.zig @@ -1,9 +1,9 @@ const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunsdfdi(a: f64, expected: u64) void { const x = __fixunsdfdi(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunsdfdi" { diff --git a/std/special/compiler_rt/fixunsdfsi_test.zig b/std/special/compiler_rt/fixunsdfsi_test.zig index db6e32e23d..c006473fb9 100644 --- a/std/special/compiler_rt/fixunsdfsi_test.zig +++ b/std/special/compiler_rt/fixunsdfsi_test.zig @@ -1,9 +1,9 @@ const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunsdfsi(a: f64, expected: u32) void { const x = __fixunsdfsi(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunsdfsi" { diff --git a/std/special/compiler_rt/fixunsdfti_test.zig b/std/special/compiler_rt/fixunsdfti_test.zig index 7f7b083d19..8241900692 100644 --- a/std/special/compiler_rt/fixunsdfti_test.zig +++ b/std/special/compiler_rt/fixunsdfti_test.zig @@ -1,9 +1,9 @@ const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunsdfti(a: f64, expected: u128) void { const x = __fixunsdfti(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunsdfti" { diff --git a/std/special/compiler_rt/fixunssfdi_test.zig b/std/special/compiler_rt/fixunssfdi_test.zig index e4e6c1736d..e2089822d2 100644 --- a/std/special/compiler_rt/fixunssfdi_test.zig +++ b/std/special/compiler_rt/fixunssfdi_test.zig @@ -1,9 +1,9 @@ const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunssfdi(a: f32, expected: u64) void { const x = __fixunssfdi(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunssfdi" { diff --git a/std/special/compiler_rt/fixunssfsi_test.zig b/std/special/compiler_rt/fixunssfsi_test.zig index 614c648dfe..4aee84d2d2 100644 --- a/std/special/compiler_rt/fixunssfsi_test.zig +++ b/std/special/compiler_rt/fixunssfsi_test.zig @@ -1,9 +1,9 @@ const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunssfsi(a: f32, expected: u32) void { const x = __fixunssfsi(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunssfsi" { diff --git a/std/special/compiler_rt/fixunssfti_test.zig b/std/special/compiler_rt/fixunssfti_test.zig index 43ad527f53..4cb27cbb8a 100644 --- a/std/special/compiler_rt/fixunssfti_test.zig +++ b/std/special/compiler_rt/fixunssfti_test.zig @@ -1,9 +1,9 @@ const __fixunssfti = @import("fixunssfti.zig").__fixunssfti; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunssfti(a: f32, expected: u128) void { const x = __fixunssfti(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunssfti" { diff --git a/std/special/compiler_rt/fixunstfdi_test.zig b/std/special/compiler_rt/fixunstfdi_test.zig index 6b1b9b7bd2..0d47641c09 100644 --- a/std/special/compiler_rt/fixunstfdi_test.zig +++ b/std/special/compiler_rt/fixunstfdi_test.zig @@ -1,9 +1,9 @@ const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunstfdi(a: f128, expected: u64) void { const x = __fixunstfdi(a); - assert(x == expected); + testing.expect(x == expected); } test "fixunstfdi" { diff --git a/std/special/compiler_rt/fixunstfsi_test.zig b/std/special/compiler_rt/fixunstfsi_test.zig index f47fcb3c86..e709636912 100644 --- a/std/special/compiler_rt/fixunstfsi_test.zig +++ b/std/special/compiler_rt/fixunstfsi_test.zig @@ -1,9 +1,9 @@ const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunstfsi(a: f128, expected: u32) void { const x = __fixunstfsi(a); - assert(x == expected); + testing.expect(x == expected); } const inf128 = @bitCast(f128, u128(0x7fff0000000000000000000000000000)); diff --git a/std/special/compiler_rt/fixunstfti_test.zig b/std/special/compiler_rt/fixunstfti_test.zig index 9128ac6c08..833e4779dd 100644 --- a/std/special/compiler_rt/fixunstfti_test.zig +++ b/std/special/compiler_rt/fixunstfti_test.zig @@ -1,9 +1,9 @@ const __fixunstfti = @import("fixunstfti.zig").__fixunstfti; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__fixunstfti(a: f128, expected: u128) void { const x = __fixunstfti(a); - assert(x == expected); + testing.expect(x == expected); } const inf128 = @bitCast(f128, u128(0x7fff0000000000000000000000000000)); diff --git a/std/special/compiler_rt/floattidf_test.zig b/std/special/compiler_rt/floattidf_test.zig index 25dc595052..4914342c31 100644 --- a/std/special/compiler_rt/floattidf_test.zig +++ b/std/special/compiler_rt/floattidf_test.zig @@ -1,9 +1,9 @@ const __floattidf = @import("floattidf.zig").__floattidf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floattidf(a: i128, expected: f64) void { const x = __floattidf(a); - assert(x == expected); + testing.expect(x == expected); } test "floattidf" { diff --git a/std/special/compiler_rt/floattisf_test.zig b/std/special/compiler_rt/floattisf_test.zig index ecb8eac60a..a6aa115307 100644 --- a/std/special/compiler_rt/floattisf_test.zig +++ b/std/special/compiler_rt/floattisf_test.zig @@ -1,9 +1,9 @@ const __floattisf = @import("floattisf.zig").__floattisf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floattisf(a: i128, expected: f32) void { const x = __floattisf(a); - assert(x == expected); + testing.expect(x == expected); } test "floattisf" { diff --git a/std/special/compiler_rt/floattitf_test.zig b/std/special/compiler_rt/floattitf_test.zig index da2ccc8b35..53e3e48bdb 100644 --- a/std/special/compiler_rt/floattitf_test.zig +++ b/std/special/compiler_rt/floattitf_test.zig @@ -1,9 +1,9 @@ const __floattitf = @import("floattitf.zig").__floattitf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floattitf(a: i128, expected: f128) void { const x = __floattitf(a); - assert(x == expected); + testing.expect(x == expected); } test "floattitf" { diff --git a/std/special/compiler_rt/floatunditf_test.zig b/std/special/compiler_rt/floatunditf_test.zig index 8533c75070..5b4e195870 100644 --- a/std/special/compiler_rt/floatunditf_test.zig +++ b/std/special/compiler_rt/floatunditf_test.zig @@ -1,5 +1,4 @@ const __floatunditf = @import("floatunditf.zig").__floatunditf; -const assert = @import("std").debug.assert; fn test__floatunditf(a: u128, expected_hi: u64, expected_lo: u64) void { const x = __floatunditf(a); diff --git a/std/special/compiler_rt/floatunsitf_test.zig b/std/special/compiler_rt/floatunsitf_test.zig index 06f54cde03..52e4786903 100644 --- a/std/special/compiler_rt/floatunsitf_test.zig +++ b/std/special/compiler_rt/floatunsitf_test.zig @@ -1,5 +1,4 @@ const __floatunsitf = @import("floatunsitf.zig").__floatunsitf; -const assert = @import("std").debug.assert; fn test__floatunsitf(a: u64, expected_hi: u64, expected_lo: u64) void { const x = __floatunsitf(a); diff --git a/std/special/compiler_rt/floatuntidf_test.zig b/std/special/compiler_rt/floatuntidf_test.zig index e2c79378e2..974f3e4be3 100644 --- a/std/special/compiler_rt/floatuntidf_test.zig +++ b/std/special/compiler_rt/floatuntidf_test.zig @@ -1,9 +1,9 @@ const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floatuntidf(a: u128, expected: f64) void { const x = __floatuntidf(a); - assert(x == expected); + testing.expect(x == expected); } test "floatuntidf" { diff --git a/std/special/compiler_rt/floatuntisf_test.zig b/std/special/compiler_rt/floatuntisf_test.zig index 7f84c1f963..3a97807066 100644 --- a/std/special/compiler_rt/floatuntisf_test.zig +++ b/std/special/compiler_rt/floatuntisf_test.zig @@ -1,9 +1,9 @@ const __floatuntisf = @import("floatuntisf.zig").__floatuntisf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floatuntisf(a: u128, expected: f32) void { const x = __floatuntisf(a); - assert(x == expected); + testing.expect(x == expected); } test "floatuntisf" { diff --git a/std/special/compiler_rt/floatuntitf_test.zig b/std/special/compiler_rt/floatuntitf_test.zig index 8e67fee108..09f3eabb3e 100644 --- a/std/special/compiler_rt/floatuntitf_test.zig +++ b/std/special/compiler_rt/floatuntitf_test.zig @@ -1,9 +1,9 @@ const __floatuntitf = @import("floatuntitf.zig").__floatuntitf; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__floatuntitf(a: u128, expected: f128) void { const x = __floatuntitf(a); - assert(x == expected); + testing.expect(x == expected); } test "floatuntitf" { diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index b2add4e3fa..6715df1805 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -21,6 +21,9 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__addtf3", @import("addXf3.zig").__addtf3, linkage); + @export("__subtf3", @import("addXf3.zig").__subtf3, linkage); + @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); @@ -37,6 +40,7 @@ comptime { @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); + @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); @@ -52,6 +56,16 @@ comptime { @export("__fixunstfdi", @import("fixunstfdi.zig").__fixunstfdi, linkage); @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage); + @export("__fixdfdi", @import("fixdfdi.zig").__fixdfdi, linkage); + @export("__fixdfsi", @import("fixdfsi.zig").__fixdfsi, linkage); + @export("__fixdfti", @import("fixdfti.zig").__fixdfti, linkage); + @export("__fixsfdi", @import("fixsfdi.zig").__fixsfdi, linkage); + @export("__fixsfsi", @import("fixsfsi.zig").__fixsfsi, linkage); + @export("__fixsfti", @import("fixsfti.zig").__fixsfti, linkage); + @export("__fixtfdi", @import("fixtfdi.zig").__fixtfdi, linkage); + @export("__fixtfsi", @import("fixtfsi.zig").__fixtfsi, linkage); + @export("__fixtfti", @import("fixtfti.zig").__fixtfti, linkage); + @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage); @export("__udivsi3", __udivsi3, linkage); @@ -100,6 +114,7 @@ comptime { const std = @import("std"); const assert = std.debug.assert; +const testing = std.testing; const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; @@ -407,7 +422,7 @@ test "test_umoddi3" { fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { const r = __umoddi3(a, b); - assert(r == expected_r); + testing.expect(r == expected_r); } test "test_udivsi3" { @@ -1081,5 +1096,5 @@ test "test_udivsi3" { fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { const q: u32 = __udivsi3(a, b); - assert(q == expected_q); + testing.expect(q == expected_q); } diff --git a/std/special/compiler_rt/muloti4_test.zig b/std/special/compiler_rt/muloti4_test.zig index 6b3671323f..00144a8839 100644 --- a/std/special/compiler_rt/muloti4_test.zig +++ b/std/special/compiler_rt/muloti4_test.zig @@ -1,10 +1,10 @@ const __muloti4 = @import("muloti4.zig").__muloti4; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__muloti4(a: i128, b: i128, expected: i128, expected_overflow: c_int) void { var overflow: c_int = undefined; const x = __muloti4(a, b, &overflow); - assert(overflow == expected_overflow and (expected_overflow != 0 or x == expected)); + testing.expect(overflow == expected_overflow and (expected_overflow != 0 or x == expected)); } test "muloti4" { diff --git a/std/special/compiler_rt/multi3_test.zig b/std/special/compiler_rt/multi3_test.zig index 413ff20a79..92c580e20f 100644 --- a/std/special/compiler_rt/multi3_test.zig +++ b/std/special/compiler_rt/multi3_test.zig @@ -1,9 +1,9 @@ const __multi3 = @import("multi3.zig").__multi3; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__multi3(a: i128, b: i128, expected: i128) void { const x = __multi3(a, b); - assert(x == expected); + testing.expect(x == expected); } test "multi3" { diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig index 5cb2f61568..b385090a93 100644 --- a/std/special/compiler_rt/truncXfYf2.zig +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -4,6 +4,10 @@ pub extern fn __truncsfhf2(a: f32) u16 { return @bitCast(u16, truncXfYf2(f16, f32, a)); } +pub extern fn __truncdfhf2(a: f64) u16 { + return @bitCast(u16, truncXfYf2(f16, f64, a)); +} + pub extern fn __trunctfsf2(a: f128) f32 { return truncXfYf2(f32, f128, a); } diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig index c4bf2db733..429372c3f1 100644 --- a/std/special/compiler_rt/truncXfYf2_test.zig +++ b/std/special/compiler_rt/truncXfYf2_test.zig @@ -63,6 +63,74 @@ test "truncsfhf2" { test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero } +const __truncdfhf2 = @import("truncXfYf2.zig").__truncdfhf2; + +fn test__truncdfhf2(a: f64, expected: u16) void { + const rep = @bitCast(u16, __truncdfhf2(a)); + + if (rep == expected) { + return; + } + // test other possible NaN representation(signal NaN) + else if (expected == 0x7e00) { + if ((rep & 0x7c00) == 0x7c00 and (rep & 0x3ff) > 0) { + return; + } + } + + @panic("__truncdfhf2 test failure"); +} + +fn test__truncdfhf2_raw(a: u64, expected: u16) void { + const actual = __truncdfhf2(@bitCast(f64, a)); + + if (actual == expected) { + return; + } + + @panic("__truncdfhf2 test failure"); +} + +test "truncdfhf2" { + test__truncdfhf2_raw(0x7ff8000000000000, 0x7e00); // qNaN + test__truncdfhf2_raw(0x7ff0000000008000, 0x7e00); // NaN + + test__truncdfhf2_raw(0x7ff0000000000000, 0x7c00); //inf + test__truncdfhf2_raw(0xfff0000000000000, 0xfc00); // -inf + + test__truncdfhf2(0.0, 0x0); // zero + test__truncdfhf2_raw(0x80000000 << 32, 0x8000); // -zero + + test__truncdfhf2(3.1415926535, 0x4248); + test__truncdfhf2(-3.1415926535, 0xc248); + + test__truncdfhf2(0x1.987124876876324p+1000, 0x7c00); + test__truncdfhf2(0x1.987124876876324p+12, 0x6e62); + test__truncdfhf2(0x1.0p+0, 0x3c00); + test__truncdfhf2(0x1.0p-14, 0x0400); + + // denormal + test__truncdfhf2(0x1.0p-20, 0x0010); + test__truncdfhf2(0x1.0p-24, 0x0001); + test__truncdfhf2(-0x1.0p-24, 0x8001); + test__truncdfhf2(0x1.5p-25, 0x0001); + + // and back to zero + test__truncdfhf2(0x1.0p-25, 0x0000); + test__truncdfhf2(-0x1.0p-25, 0x8000); + + // max (precise) + test__truncdfhf2(65504.0, 0x7bff); + + // max (rounded) + test__truncdfhf2(65519.0, 0x7bff); + + // max (to +inf) + test__truncdfhf2(65520.0, 0x7c00); + test__truncdfhf2(-65520.0, 0xfc00); + test__truncdfhf2(65536.0, 0x7c00); +} + const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; fn test__trunctfsf2(a: f128, expected: u32) void { diff --git a/std/special/compiler_rt/udivmoddi4_test.zig b/std/special/compiler_rt/udivmoddi4_test.zig index 34b9dda1ea..5e6924f290 100644 --- a/std/special/compiler_rt/udivmoddi4_test.zig +++ b/std/special/compiler_rt/udivmoddi4_test.zig @@ -1,13 +1,13 @@ // Disable formatting to avoid unnecessary source repository bloat. // zig fmt: off const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__udivmoddi4(a: u64, b: u64, expected_q: u64, expected_r: u64) void { var r: u64 = undefined; const q = __udivmoddi4(a, b, &r); - assert(q == expected_q); - assert(r == expected_r); + testing.expect(q == expected_q); + testing.expect(r == expected_r); } test "udivmoddi4" { diff --git a/std/special/compiler_rt/udivmodti4_test.zig b/std/special/compiler_rt/udivmodti4_test.zig index f6b370c26e..0c7880f346 100644 --- a/std/special/compiler_rt/udivmodti4_test.zig +++ b/std/special/compiler_rt/udivmodti4_test.zig @@ -1,13 +1,13 @@ // Disable formatting to avoid unnecessary source repository bloat. // zig fmt: off const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; -const assert = @import("std").debug.assert; +const testing = @import("std").testing; fn test__udivmodti4(a: u128, b: u128, expected_q: u128, expected_r: u128) void { var r: u128 = undefined; const q = __udivmodti4(a, b, &r); - assert(q == expected_q); - assert(r == expected_r); + testing.expect(q == expected_q); + testing.expect(r == expected_r); } test "udivmodti4" { diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig new file mode 100644 index 0000000000..46ced0e136 --- /dev/null +++ b/std/special/fmt_runner.zig @@ -0,0 +1,260 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const os = std.os; +const io = std.io; +const mem = std.mem; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const ast = std.zig.ast; + +const arg = @import("fmt/arg.zig"); +const self_hosted_main = @import("fmt/main.zig"); +const Args = arg.Args; +const Flag = arg.Flag; +const errmsg = @import("fmt/errmsg.zig"); + +var stderr_file: os.File = undefined; +var stderr: *io.OutStream(os.File.WriteError) = undefined; +var stdout: *io.OutStream(os.File.WriteError) = undefined; + +// This brings `zig fmt` to stage 1. +pub fn main() !void { + // Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` 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 direct_allocator = std.heap.DirectAllocator.init(); + var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator); + const allocator = &arena.allocator; + + var stdout_file = try std.io.getStdOut(); + var stdout_out_stream = stdout_file.outStream(); + stdout = &stdout_out_stream.stream; + + stderr_file = try std.io.getStdErr(); + var stderr_out_stream = stderr_file.outStream(); + stderr = &stderr_out_stream.stream; + const args = try std.os.argsAlloc(allocator); + + var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args); + defer flags.deinit(); + + if (flags.present("help")) { + try stdout.write(self_hosted_main.usage_fmt); + os.exit(0); + } + + 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) { + try stderr.write("cannot use --stdin with positional arguments\n"); + os.exit(1); + } + + var stdin_file = try 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); + + var tree = std.zig.parse(allocator, source_code) catch |err| { + try stderr.print("error parsing stdin: {}\n", err); + os.exit(1); + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(allocator, parse_error, &tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + os.exit(1); + } + if (flags.present("check")) { + const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree); + const code = if (anything_changed) u8(1) else u8(0); + os.exit(code); + } + + _ = try std.zig.render(allocator, stdout, &tree); + return; + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + + var fmt = Fmt{ + .seen = Fmt.SeenMap.init(allocator), + .any_error = false, + .color = color, + .allocator = allocator, + }; + + const check_mode = flags.present("check"); + + for (flags.positionals.toSliceConst()) |file_path| { + try fmtPath(&fmt, file_path, check_mode); + } + if (fmt.any_error) { + os.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, +} || os.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; + + 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 std.os.Dir.open(fmt.allocator, file_path); + defer dir.close(); + + while (try dir.next()) |entry| { + if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name }); + try fmtPath(fmt, full_path, check_mode); + } + } + return; + }, + else => { + // TODO lock stderr printing + try stderr.print("unable to open '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }, + }; + defer fmt.allocator.free(source_code); + + var 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; + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + 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); + fmt.any_error = true; + } + } else { + // TODO make this evented + const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); + defer baf.destroy(); + + const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + try baf.finish(); + } + } +} + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: errmsg.Color, + allocator: *mem.Allocator, + + const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); +}; + +fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree, + path: []const u8, file: os.File, color: errmsg.Color,) !void +{ + const color_on = switch (color) { + errmsg.Color.Auto => file.isTty(), + errmsg.Color.On => true, + errmsg.Color.Off => false, + }; + const lok_token = parse_error.loc(); + const span = errmsg.Span{ + .first = lok_token, + .last = lok_token, + }; + + const first_token = tree.tokens.at(span.first); + const last_token = tree.tokens.at(span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + + var text_buf = try std.Buffer.initSize(allocator, 0); + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + const text = text_buf.toOwnedSlice(); + + const stream = &file.outStream().stream; + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + text, + ); + return; + } + + try stream.print( + "{}:{}:{}: error: {}\n{}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + 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"); +} diff --git a/std/special/init-lib/src/main.zig b/std/special/init-lib/src/main.zig index 27fdeb2030..747bb08573 100644 --- a/std/special/init-lib/src/main.zig +++ b/std/special/init-lib/src/main.zig @@ -1,10 +1,10 @@ const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; +const testing = std.testing; export fn add(a: i32, b: i32) i32 { return a + b; } test "basic add functionality" { - assertOrPanic(add(3, 7) == 10); + testing.expect(add(3, 7) == 10); } diff --git a/std/special/panic.zig b/std/special/panic.zig index ca1caea73c..bd3ad971e0 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -13,6 +13,10 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn builtin.Os.freestanding, builtin.Os.zen => { while (true) {} }, + builtin.Os.uefi => { + // TODO look into using the debug info and logging helpful messages + std.os.abort(); + }, else => { const first_trace_addr = @ptrToInt(@returnAddress()); std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig new file mode 100644 index 0000000000..16bcd7adaf --- /dev/null +++ b/std/statically_initialized_mutex.zig @@ -0,0 +1,111 @@ +const std = @import("index.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.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.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 { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.DeleteCriticalSection(&self.lock); + } + + pub fn acquire(self: *StaticallyInitializedMutex) Held { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.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 direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.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.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); + + expect(context.data == thread_count * TestContext.incr_count); + } +} diff --git a/std/testing.zig b/std/testing.zig new file mode 100644 index 0000000000..a47c5984a0 --- /dev/null +++ b/std/testing.zig @@ -0,0 +1,147 @@ +const builtin = @import("builtin"); +const TypeId = builtin.TypeId; +const std = @import("index.zig"); + +/// This function is intended to be used only in tests. It prints diagnostics to stderr +/// and then aborts when actual_error_union is not expected_error. +pub fn expectError(expected_error: anyerror, actual_error_union: var) void { + // TODO remove the workaround here for https://github.com/ziglang/zig/issues/1936 + 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); + } + } else |actual_error| { + if (expected_error != actual_error) { + std.debug.panic("expected error.{}, found error.{}", @errorName(expected_error), @errorName(actual_error)); + } + } +} + +/// This function is intended to be used only in tests. When the two values are not +/// 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))) { + TypeId.NoReturn, + TypeId.BoundFn, + TypeId.ArgTuple, + TypeId.Opaque, + => @compileError("value of type " ++ @typeName(@typeOf(actual)) ++ " encountered"), + + TypeId.Undefined, + TypeId.Null, + TypeId.Void, + => return, + + TypeId.Type, + TypeId.Bool, + TypeId.Int, + TypeId.Float, + TypeId.ComptimeFloat, + TypeId.ComptimeInt, + TypeId.Enum, + TypeId.Namespace, + TypeId.Fn, + TypeId.Promise, + TypeId.Vector, + TypeId.ErrorSet, + => { + if (actual != expected) { + std.debug.panic("expected {}, found {}", expected, actual); + } + }, + + TypeId.Pointer => |pointer| { + switch (pointer.size) { + builtin.TypeInfo.Pointer.Size.One, + builtin.TypeInfo.Pointer.Size.Many, + => { + if (actual != expected) { + 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); + } + if (actual.len != expected.len) { + std.debug.panic("expected slice len {}, found {}", expected.len, actual.len); + } + }, + } + }, + + TypeId.Array => |array| expectEqualSlices(array.child, &expected, &actual), + + TypeId.Struct => { + @compileError("TODO implement testing.expectEqual for structs"); + }, + + TypeId.Union => |union_info| { + if (union_info.tag_type == null) { + @compileError("Unable to compare untagged union values"); + } + @compileError("TODO implement testing.expectEqual for tagged unions"); + }, + + TypeId.Optional => { + if (expected) |expected_payload| { + if (actual) |actual_payload| { + expectEqual(expected_payload, actual_payload); + } else { + std.debug.panic("expected {}, found null", expected_payload); + } + } else { + if (actual) |actual_payload| { + std.debug.panic("expected null, found {}", actual_payload); + } + } + }, + + TypeId.ErrorUnion => { + if (expected) |expected_payload| { + if (actual) |actual_payload| { + expectEqual(expected_payload, actual_payload); + } else |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); + } else |actual_err| { + expectEqual(expected_err, actual_err); + } + } + }, + } +} + +/// 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. +pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void { + // TODO better printing of the difference + // If the arrays are small enough we could print the whole thing + // 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); + } + 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]); + } + } +} + +/// This function is intended to be used only in tests. When `ok` is false, the test fails. +/// A message is printed to stderr and then abort is called. +pub fn expect(ok: bool) void { + if (!ok) @panic("test failure"); +} diff --git a/std/unicode.zig b/std/unicode.zig index fcb748401f..fccdf513b9 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,7 +1,7 @@ const std = @import("./index.zig"); const builtin = @import("builtin"); -const debug = std.debug; const assert = std.debug.assert; +const testing = std.testing; const mem = std.mem; /// Returns how many bytes the UTF-8 representation would require @@ -32,7 +32,7 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { /// Returns: the number of bytes written to out. pub fn utf8Encode(c: u32, out: []u8) !u3 { const length = try utf8CodepointSequenceLength(c); - debug.assert(out.len >= length); + assert(out.len >= length); switch (length) { // The pattern for each is the same // - Increasing the initial shift by 6 each time @@ -81,8 +81,8 @@ const Utf8Decode2Error = error{ Utf8OverlongEncoding, }; pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 { - debug.assert(bytes.len == 2); - debug.assert(bytes[0] & 0b11100000 == 0b11000000); + assert(bytes.len == 2); + assert(bytes[0] & 0b11100000 == 0b11000000); var value: u32 = bytes[0] & 0b00011111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; @@ -100,8 +100,8 @@ const Utf8Decode3Error = error{ Utf8EncodesSurrogateHalf, }; pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 { - debug.assert(bytes.len == 3); - debug.assert(bytes[0] & 0b11110000 == 0b11100000); + assert(bytes.len == 3); + assert(bytes[0] & 0b11110000 == 0b11100000); var value: u32 = bytes[0] & 0b00001111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; @@ -124,8 +124,8 @@ const Utf8Decode4Error = error{ Utf8CodepointTooLarge, }; pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 { - debug.assert(bytes.len == 4); - debug.assert(bytes[0] & 0b11111000 == 0b11110000); + assert(bytes.len == 4); + assert(bytes[0] & 0b11111000 == 0b11110000); var value: u32 = bytes[0] & 0b00000111; if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation; @@ -249,12 +249,12 @@ pub const Utf16LeIterator = struct { pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 { assert(it.i <= it.bytes.len); if (it.i == it.bytes.len) return null; - const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]); + const c0: u32 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); if (c0 & ~u32(0x03ff) == 0xd800) { // surrogate pair it.i += 2; if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf; - const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]); + const c1: u32 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; it.i += 2; return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); @@ -274,23 +274,23 @@ test "utf8 encode" { fn testUtf8Encode() !void { // A few taken from wikipedia a few taken elsewhere var array: [4]u8 = undefined; - debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3); - debug.assert(array[0] == 0b11100010); - debug.assert(array[1] == 0b10000010); - debug.assert(array[2] == 0b10101100); + testing.expect((try utf8Encode(try utf8Decode("€"), array[0..])) == 3); + testing.expect(array[0] == 0b11100010); + testing.expect(array[1] == 0b10000010); + testing.expect(array[2] == 0b10101100); - debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1); - debug.assert(array[0] == 0b00100100); + testing.expect((try utf8Encode(try utf8Decode("$"), array[0..])) == 1); + testing.expect(array[0] == 0b00100100); - debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2); - debug.assert(array[0] == 0b11000010); - debug.assert(array[1] == 0b10100010); + testing.expect((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2); + testing.expect(array[0] == 0b11000010); + testing.expect(array[1] == 0b10100010); - debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4); - debug.assert(array[0] == 0b11110000); - debug.assert(array[1] == 0b10010000); - debug.assert(array[2] == 0b10001101); - debug.assert(array[3] == 0b10001000); + testing.expect((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4); + testing.expect(array[0] == 0b11110000); + testing.expect(array[1] == 0b10010000); + testing.expect(array[2] == 0b10001101); + testing.expect(array[3] == 0b10001000); } test "utf8 encode error" { @@ -306,11 +306,7 @@ fn testUtf8EncodeError() void { } fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: anyerror) void { - if (utf8Encode(codePoint, array)) |_| { - unreachable; - } else |err| { - debug.assert(err == expectedErr); - } + testing.expectError(expectedErr, utf8Encode(codePoint, array)); } test "utf8 iterator on ascii" { @@ -321,16 +317,16 @@ fn testUtf8IteratorOnAscii() void { const s = Utf8View.initComptime("abc"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "a", it1.nextCodepointSlice().?)); - debug.assert(std.mem.eql(u8, "b", it1.nextCodepointSlice().?)); - debug.assert(std.mem.eql(u8, "c", it1.nextCodepointSlice().?)); - debug.assert(it1.nextCodepointSlice() == null); + testing.expect(std.mem.eql(u8, "a", it1.nextCodepointSlice().?)); + testing.expect(std.mem.eql(u8, "b", it1.nextCodepointSlice().?)); + testing.expect(std.mem.eql(u8, "c", it1.nextCodepointSlice().?)); + testing.expect(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(it2.nextCodepoint().? == 'a'); - debug.assert(it2.nextCodepoint().? == 'b'); - debug.assert(it2.nextCodepoint().? == 'c'); - debug.assert(it2.nextCodepoint() == null); + testing.expect(it2.nextCodepoint().? == 'a'); + testing.expect(it2.nextCodepoint().? == 'b'); + testing.expect(it2.nextCodepoint().? == 'c'); + testing.expect(it2.nextCodepoint() == null); } test "utf8 view bad" { @@ -340,12 +336,7 @@ test "utf8 view bad" { fn testUtf8ViewBad() void { // Compile-time error. // const s3 = Utf8View.initComptime("\xfe\xf2"); - const s = Utf8View.init("hel\xadlo"); - if (s) |_| { - unreachable; - } else |err| { - debug.assert(err == error.InvalidUtf8); - } + testing.expectError(error.InvalidUtf8, Utf8View.init("hel\xadlo")); } test "utf8 view ok" { @@ -356,16 +347,16 @@ fn testUtf8ViewOk() void { const s = Utf8View.initComptime("東京市"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "東", it1.nextCodepointSlice().?)); - debug.assert(std.mem.eql(u8, "京", it1.nextCodepointSlice().?)); - debug.assert(std.mem.eql(u8, "市", it1.nextCodepointSlice().?)); - debug.assert(it1.nextCodepointSlice() == null); + testing.expect(std.mem.eql(u8, "東", it1.nextCodepointSlice().?)); + testing.expect(std.mem.eql(u8, "京", it1.nextCodepointSlice().?)); + testing.expect(std.mem.eql(u8, "市", it1.nextCodepointSlice().?)); + testing.expect(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(it2.nextCodepoint().? == 0x6771); - debug.assert(it2.nextCodepoint().? == 0x4eac); - debug.assert(it2.nextCodepoint().? == 0x5e02); - debug.assert(it2.nextCodepoint() == null); + testing.expect(it2.nextCodepoint().? == 0x6771); + testing.expect(it2.nextCodepoint().? == 0x4eac); + testing.expect(it2.nextCodepoint().? == 0x5e02); + testing.expect(it2.nextCodepoint() == null); } test "bad utf8 slice" { @@ -373,10 +364,10 @@ test "bad utf8 slice" { testBadUtf8Slice(); } fn testBadUtf8Slice() void { - debug.assert(utf8ValidateSlice("abc")); - debug.assert(!utf8ValidateSlice("abc\xc0")); - debug.assert(!utf8ValidateSlice("abc\xc0abc")); - debug.assert(utf8ValidateSlice("abc\xdf\xbf")); + testing.expect(utf8ValidateSlice("abc")); + testing.expect(!utf8ValidateSlice("abc\xc0")); + testing.expect(!utf8ValidateSlice("abc\xc0abc")); + testing.expect(utf8ValidateSlice("abc\xdf\xbf")); } test "valid utf8" { @@ -459,21 +450,17 @@ fn testMiscInvalidUtf8() void { } fn testError(bytes: []const u8, expected_err: anyerror) void { - if (testDecode(bytes)) |_| { - unreachable; - } else |err| { - debug.assert(err == expected_err); - } + testing.expectError(expected_err, testDecode(bytes)); } fn testValid(bytes: []const u8, expected_codepoint: u32) void { - debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint); + testing.expect((testDecode(bytes) catch unreachable) == expected_codepoint); } fn testDecode(bytes: []const u8) !u32 { const length = try utf8ByteSequenceLength(bytes[0]); if (bytes.len < length) return error.UnexpectedEof; - debug.assert(bytes.len == length); + testing.expect(bytes.len == length); return utf8Decode(bytes); } @@ -510,48 +497,48 @@ test "utf16leToUtf8" { const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); { - mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "Aa")); + testing.expect(mem.eql(u8, utf8, "Aa")); } { - mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); + testing.expect(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); } { // the values just outside the surrogate half range - mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); + testing.expect(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); } { // smallest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); + testing.expect(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); } { // largest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); + testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); } { - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + 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); - assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); + testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); } } @@ -583,7 +570,7 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { while (it.nextCodepoint()) |codepoint| { if (end_index == utf16le_as_bytes.len) return (end_index / 2) + 1; // TODO surrogate pairs - mem.writeInt(utf16le_as_bytes[end_index..], @intCast(u16, codepoint), builtin.Endian.Little); + mem.writeIntSliceLittle(u16, utf16le_as_bytes[end_index..], @intCast(u16, codepoint)); end_index += 2; } return end_index / 2; diff --git a/std/zig/ast.zig b/std/zig/ast.zig index bb2099fb75..ea32634566 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1,5 +1,6 @@ const std = @import("../index.zig"); const assert = std.debug.assert; +const testing = std.testing; const SegmentedList = std.SegmentedList; const mem = std.mem; const Token = std.zig.Token; @@ -109,6 +110,7 @@ pub const Tree = struct { pub const Error = union(enum) { InvalidToken: InvalidToken, ExpectedVarDeclOrFn: ExpectedVarDeclOrFn, + ExpectedVarDecl: ExpectedVarDecl, ExpectedAggregateKw: ExpectedAggregateKw, UnattachedDocComment: UnattachedDocComment, ExpectedEqOrSemi: ExpectedEqOrSemi, @@ -132,6 +134,7 @@ pub const Error = union(enum) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedVarDecl => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream), @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream), @@ -157,6 +160,7 @@ pub const Error = union(enum) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |x| return x.token, @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token, + @TagType(Error).ExpectedVarDecl => |x| return x.token, @TagType(Error).ExpectedAggregateKw => |x| return x.token, @TagType(Error).UnattachedDocComment => |x| return x.token, @TagType(Error).ExpectedEqOrSemi => |x| return x.token, @@ -179,6 +183,7 @@ pub const Error = union(enum) { pub const InvalidToken = SingleTokenError("Invalid token {}"); pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}"); + pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found {}"); pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ @tagName(Token.Id.Keyword_enum) ++ ", found {}"); pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}"); pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}"); @@ -495,6 +500,7 @@ pub const Node = struct { base: Node, doc_comments: ?*DocComment, visib_token: ?TokenIndex, + thread_local_token: ?TokenIndex, name_token: TokenIndex, eq_token: TokenIndex, mut_token: TokenIndex, @@ -535,6 +541,7 @@ pub const Node = struct { pub fn firstToken(self: *const VarDecl) TokenIndex { if (self.visib_token) |visib_token| return visib_token; + if (self.thread_local_token) |thread_local_token| return thread_local_token; if (self.comptime_token) |comptime_token| return comptime_token; if (self.extern_export_token) |extern_export_token| return extern_export_token; assert(self.lib_name == null); @@ -2224,5 +2231,5 @@ test "iterate" { .shebang = null, }; var base = &root.base; - assert(base.iterate(0) == null); + testing.expect(base.iterate(0) == null); } diff --git a/std/zig/parse.zig b/std/zig/parse.zig index a216484d7d..867dd11592 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,14 +17,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try arena.create(ast.Node.Root{ + const root_node = try arena.create(ast.Node.Root); + root_node.* = ast.Node.Root{ .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), .doc_comments = null, .shebang = null, // initialized when we get the eof token .eof_token = undefined, - }); + }; var tree = ast.Tree{ .source = source, @@ -75,20 +76,22 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const test_node = try arena.create(ast.Node.TestDecl{ + }; + const test_node = try arena.create(ast.Node.TestDecl); + test_node.* = ast.Node.TestDecl{ .base = ast.Node{ .id = ast.Node.Id.TestDecl }, .doc_comments = comments, .test_token = token_index, .name = undefined, .body_node = &block.base, - }); + }; try root_node.decls.push(&test_node.base); try stack.append(State{ .Block = block }); try stack.append(State{ @@ -119,19 +122,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_comptime => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const node = try arena.create(ast.Node.Comptime{ + }; + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = &block.base, .doc_comments = comments, - }); + }; try root_node.decls.push(&node.base); stack.append(State.TopLevel) catch unreachable; @@ -224,6 +229,32 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }) catch unreachable; continue; }, + State.ThreadLocal => |ctx| { + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; + switch (token_ptr.id) { + Token.Id.Keyword_var, Token.Id.Keyword_const => { + try stack.append(State{ + .VarDecl = VarDeclCtx{ + .comments = ctx.comments, + .visib_token = ctx.visib_token, + .thread_local_token = ctx.thread_local_token, + .lib_name = ctx.lib_name, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .mut_token = token_index, + .list = ctx.list, + }, + }); + continue; + }, + else => { + ((try tree.errors.addOne())).* = Error{ .ExpectedVarDecl = Error.ExpectedVarDecl{ .token = token_index } }; + return tree; + }, + } + }, State.TopLevelDecl => |ctx| { const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -235,14 +266,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.Use{ + const node = try arena.create(ast.Node.Use); + node.* = ast.Node.Use{ .base = ast.Node{ .id = ast.Node.Id.Use }, .use_token = token_index, .visib_token = ctx.visib_token, .expr = undefined, .semicolon_token = undefined, .doc_comments = ctx.comments, - }); + }; try ctx.decls.push(&node.base); stack.append(State{ @@ -254,6 +286,28 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); continue; }, + Token.Id.Keyword_threadlocal => { + if (ctx.extern_export_inline_token) |annotated_token| { + if (annotated_token.ptr.id == Token.Id.Keyword_inline) { + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = annotated_token.index } }; + return tree; + } + } + + try stack.append(State{ + .ThreadLocal = VarDeclCtx{ + .comments = ctx.comments, + .visib_token = ctx.visib_token, + .thread_local_token = token_index, + .lib_name = ctx.lib_name, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = undefined, + .list = ctx.decls, + }, + }); + continue; + }, Token.Id.Keyword_var, Token.Id.Keyword_const => { if (ctx.extern_export_inline_token) |annotated_token| { if (annotated_token.ptr.id == Token.Id.Keyword_inline) { @@ -266,6 +320,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .VarDecl = VarDeclCtx{ .comments = ctx.comments, .visib_token = ctx.visib_token, + .thread_local_token = null, .lib_name = ctx.lib_name, .comptime_token = null, .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, @@ -276,7 +331,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -292,7 +348,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = ctx.lib_name, .align_expr = null, .section_expr = null, - }); + }; try ctx.decls.push(&fn_proto.base); stack.append(State{ .FnDef = fn_proto }) catch unreachable; try stack.append(State{ .FnProto = fn_proto }); @@ -309,12 +365,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_async => { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = token_index, .allocator_type = null, .rangle_bracket = null, - }); + }; fn_proto.async_attr = async_node; try stack.append(State{ @@ -341,13 +398,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.TopLevelExternOrField => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = identifier, .type_expr = undefined, - }); + }; const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -391,7 +449,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try arena.create(ast.Node.ContainerDecl{ + const node = try arena.create(ast.Node.ContainerDecl); + node.* = ast.Node.ContainerDecl{ .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .layout_token = ctx.layout_token, .kind_token = switch (token_ptr.id) { @@ -405,7 +464,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), .lbrace_token = undefined, .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .ContainerDecl = node }) catch unreachable; @@ -464,13 +523,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { switch (tree.tokens.at(container_decl.kind_token).id) { Token.Id.Keyword_struct => { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = comments, .visib_token = null, .name_token = token_index, .type_expr = undefined, - }); + }; const node_ptr = try container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -485,13 +545,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_union => { - const node = try arena.create(ast.Node.UnionTag{ + const node = try arena.create(ast.Node.UnionTag); + node.* = ast.Node.UnionTag{ .base = ast.Node{ .id = ast.Node.Id.UnionTag }, .name_token = token_index, .type_expr = null, .value_expr = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -506,12 +567,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_enum => { - const node = try arena.create(ast.Node.EnumTag{ + const node = try arena.create(ast.Node.EnumTag); + node.* = ast.Node.EnumTag{ .base = ast.Node{ .id = ast.Node.Id.EnumTag }, .name_token = token_index, .value = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -593,10 +655,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.VarDecl => |ctx| { - const var_decl = try arena.create(ast.Node.VarDecl{ + const var_decl = try arena.create(ast.Node.VarDecl); + var_decl.* = ast.Node.VarDecl{ .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, + .thread_local_token = ctx.thread_local_token, .mut_token = ctx.mut_token, .comptime_token = ctx.comptime_token, .extern_export_token = ctx.extern_export_token, @@ -609,7 +673,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .name_token = undefined, .eq_token = undefined, .semicolon_token = undefined, - }); + }; try ctx.list.push(&var_decl.base); try stack.append(State{ .VarDeclAlign = var_decl }); @@ -708,13 +772,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; fn_proto.body_node = &block.base; stack.append(State{ .Block = block }) catch unreachable; continue; @@ -770,10 +835,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_anyerror) { if (tok_it.peek().?.id == Token.Id.LBrace) { - const error_type_node = try arena.create(ast.Node.ErrorType{ + const error_type_node = try arena.create(ast.Node.ErrorType); + error_type_node.* = ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, - }); + }; fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = &error_type_node.base }; continue; } @@ -791,14 +857,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } - const param_decl = try arena.create(ast.Node.ParamDecl{ + const param_decl = try arena.create(ast.Node.ParamDecl); + param_decl.* = ast.Node.ParamDecl{ .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, .comptime_token = null, .noalias_token = null, .name_token = null, .type_node = undefined, .var_args_token = null, - }); + }; try fn_proto.params.push(¶m_decl.base); stack.append(State{ @@ -877,13 +944,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = ctx.label, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.opt_ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; continue; @@ -970,7 +1038,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try arena.create(ast.Node.While{ + const node = try arena.create(ast.Node.While); + node.* = ast.Node.While{ .base = ast.Node{ .id = ast.Node.Id.While }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -980,7 +1049,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .continue_expr = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -999,7 +1068,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.For => |ctx| { - const node = try arena.create(ast.Node.For{ + const node = try arena.create(ast.Node.For); + node.* = ast.Node.For{ .base = ast.Node{ .id = ast.Node.Id.For }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1008,7 +1078,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -1020,12 +1090,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try arena.create(ast.Node.Else{ + const node = try arena.create(ast.Node.Else); + node.* = ast.Node.Else{ .base = ast.Node{ .id = ast.Node.Id.Else }, .else_token = else_token, .payload = null, .body = undefined, - }); + }; dest.* = node; stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }) catch unreachable; @@ -1073,6 +1144,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .VarDecl = VarDeclCtx{ .comments = null, .visib_token = null, + .thread_local_token = null, .comptime_token = null, .extern_export_token = null, .lib_name = null, @@ -1083,11 +1155,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.create(ast.Node.Defer{ + const node = try arena.create(ast.Node.Defer); + node.* = ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, .expr = undefined, - }); + }; const node_ptr = try block.statements.addOne(); node_ptr.* = &node.base; @@ -1096,13 +1169,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBrace => { - const inner_block = try arena.create(ast.Node.Block{ + const inner_block = try arena.create(ast.Node.Block); + inner_block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; try block.statements.push(&inner_block.base); stack.append(State{ .Block = inner_block }) catch unreachable; @@ -1127,6 +1201,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .VarDecl = VarDeclCtx{ .comments = null, .visib_token = null, + .thread_local_token = null, .comptime_token = ctx.comptime_token, .extern_export_token = null, .lib_name = null, @@ -1164,14 +1239,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmOutput{ + const node = try arena.create(ast.Node.AsmOutput); + node.* = ast.Node.AsmOutput{ .base = ast.Node{ .id = ast.Node.Id.AsmOutput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .kind = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmOutputItems = items }) catch unreachable; @@ -1218,14 +1294,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmInput{ + const node = try arena.create(ast.Node.AsmInput); + node.* = ast.Node.AsmInput{ .base = ast.Node{ .id = ast.Node.Id.AsmInput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .expr = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmInputItems = items }) catch unreachable; @@ -1283,12 +1360,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.FieldInitializer{ + const node = try arena.create(ast.Node.FieldInitializer); + node.* = ast.Node.FieldInitializer{ .base = ast.Node{ .id = ast.Node.Id.FieldInitializer }, .period_token = undefined, .name_token = undefined, .expr = undefined, - }); + }; try list_state.list.push(&node.base); stack.append(State{ .FieldInitListCommaOrEnd = list_state }) catch unreachable; @@ -1390,13 +1468,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } const comments = try eatDocComments(arena, &tok_it, &tree); - const node = try arena.create(ast.Node.SwitchCase{ + const node = try arena.create(ast.Node.SwitchCase); + node.* = ast.Node.SwitchCase{ .base = ast.Node{ .id = ast.Node.Id.SwitchCase }, .items = ast.Node.SwitchCase.ItemList.init(arena), .payload = null, .expr = undefined, .arrow_token = undefined, - }); + }; try list_state.list.push(&node.base); try stack.append(State{ .SwitchCaseCommaOrEnd = list_state }); try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); @@ -1427,10 +1506,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.create(ast.Node.SwitchElse{ + const else_node = try arena.create(ast.Node.SwitchElse); + else_node.* = ast.Node.SwitchElse{ .base = ast.Node{ .id = ast.Node.Id.SwitchElse }, .token = token_index, - }); + }; try switch_case.items.push(&else_node.base); try stack.append(State{ @@ -1537,7 +1617,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ExternType => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = null, @@ -1553,7 +1634,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; ctx.opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; @@ -1711,12 +1792,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.Payload{ + const node = try arena.create(ast.Node.Payload); + node.* = ast.Node.Payload{ .base = ast.Node{ .id = ast.Node.Id.Payload }, .lpipe = token_index, .error_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1747,13 +1829,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerPayload{ + const node = try arena.create(ast.Node.PointerPayload); + node.* = ast.Node.PointerPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); try stack.append(State{ @@ -1790,14 +1873,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerIndexPayload{ + const node = try arena.create(ast.Node.PointerIndexPayload); + node.* = ast.Node.PointerIndexPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerIndexPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .index_symbol = null, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1824,12 +1908,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try arena.create(ast.Node.ControlFlowExpression{ + const node = try arena.create(ast.Node.ControlFlowExpression); + node.* = ast.Node.ControlFlowExpression{ .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, .kind = undefined, .rhs = null, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.rhs } }) catch unreachable; @@ -1853,7 +1938,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = switch (token_ptr.id) { @@ -1863,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { else => unreachable, }, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; @@ -1887,13 +1973,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ellipsis3, .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; continue; @@ -1912,13 +1999,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ass_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }); @@ -1942,13 +2030,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = unwrap_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -1974,13 +2063,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = or_token, .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BoolAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -1998,13 +2088,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = and_token, .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .ComparisonExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2025,13 +2116,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = comp_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryOrExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2052,13 +2144,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = pipe, .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryXorExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2076,13 +2169,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = caret, .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2100,13 +2194,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ampersand, .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BitShiftExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2127,13 +2222,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = bitshift_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .AdditionExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2157,13 +2253,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = add_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .MultiplyExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2187,13 +2284,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = mult_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .CurlySuffixExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2215,12 +2313,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (tok_it.peek().?.id == Token.Id.Period) { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2234,12 +2333,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.LBrace }); @@ -2263,13 +2363,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = bang, .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .PrefixOpExpression = OptionalCtx{ .Required = &node.rhs } }); @@ -2282,22 +2383,24 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try arena.create(ast.Node.PrefixOp{ + var node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); // Treat '**' token as two pointer types if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try arena.create(ast.Node.PrefixOp{ + const child = try arena.create(ast.Node.PrefixOp); + child.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; node.rhs = &child.base; node = child; } @@ -2316,12 +2419,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = async_token, .allocator_type = null, .rangle_bracket = null, - }); + }; stack.append(State{ .AsyncEnd = AsyncEndCtx{ .ctx = opt_ctx, @@ -2347,7 +2451,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ @@ -2357,7 +2462,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2371,12 +2476,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayAccess = undefined }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2386,34 +2492,37 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Period => { if (eatToken(&tok_it, &tree, Token.Id.Asterisk)) |asterisk_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.Deref, .rtoken = asterisk_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.UnwrapOptional, .rtoken = question_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2467,11 +2576,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_promise => { - const node = try arena.create(ast.Node.PromiseType{ + const node = try arena.create(ast.Node.PromiseType); + node.* = ast.Node.PromiseType{ .base = ast.Node{ .id = ast.Node.Id.PromiseType }, .promise_token = token.index, .result = null, - }); + }; opt_ctx.store(&node.base); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; @@ -2493,12 +2603,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LParen => { - const node = try arena.create(ast.Node.GroupedExpression{ + const node = try arena.create(ast.Node.GroupedExpression); + node.* = ast.Node.GroupedExpression{ .base = ast.Node{ .id = ast.Node.Id.GroupedExpression }, .lparen = token.index, .expr = undefined, .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2511,12 +2622,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Builtin => { - const node = try arena.create(ast.Node.BuiltinCall{ + const node = try arena.create(ast.Node.BuiltinCall); + node.* = ast.Node.BuiltinCall{ .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), .rparen_token = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2530,12 +2642,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token.index, .op = undefined, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SliceOrArrayType = node }) catch unreachable; @@ -2593,7 +2706,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2609,13 +2723,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2631,7 +2746,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; try stack.append(State{ @@ -2643,7 +2758,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_asm => { - const node = try arena.create(ast.Node.Asm{ + const node = try arena.create(ast.Node.Asm); + node.* = ast.Node.Asm{ .base = ast.Node{ .id = ast.Node.Id.Asm }, .asm_token = token.index, .volatile_token = null, @@ -2652,7 +2768,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .inputs = ast.Node.Asm.InputList.init(arena), .clobbers = ast.Node.Asm.ClobberList.init(arena), .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2701,13 +2817,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ErrorTypeOrSetDecl => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = &(try createLiteral(arena, ast.Node.ErrorType, ctx.error_token)).base, .op_token = undefined, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; try stack.append(State{ @@ -2719,12 +2836,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.ErrorSetDecl{ + const node = try arena.create(ast.Node.ErrorSetDecl); + node.* = ast.Node.ErrorSetDecl{ .base = ast.Node{ .id = ast.Node.Id.ErrorSetDecl }, .error_token = ctx.error_token, .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ @@ -2785,11 +2903,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.ErrorTag{ + const node = try arena.create(ast.Node.ErrorTag); + node.* = ast.Node.ErrorTag{ .base = ast.Node{ .id = ast.Node.Id.ErrorTag }, .doc_comments = comments, .name_token = ident_token_index, - }); + }; node_ptr.* = &node.base; continue; }, @@ -2870,6 +2989,7 @@ const TopLevelDeclCtx = struct { const VarDeclCtx = struct { mut_token: TokenIndex, visib_token: ?TokenIndex, + thread_local_token: ?TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, lib_name: ?*ast.Node, @@ -3014,6 +3134,7 @@ const State = union(enum) { ContainerInitArg: *ast.Node.ContainerDecl, ContainerDecl: *ast.Node.ContainerDecl, + ThreadLocal: VarDeclCtx, VarDecl: VarDeclCtx, VarDeclAlign: *ast.Node.VarDecl, VarDeclSection: *ast.Node.VarDecl, @@ -3129,10 +3250,11 @@ fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*as if (result.*) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.create(ast.Node.DocComment{ + const comment_node = try arena.create(ast.Node.DocComment); + comment_node.* = ast.Node.DocComment{ .base = ast.Node{ .id = ast.Node.Id.DocComment }, .lines = ast.Node.DocComment.LineList.init(arena), - }); + }; result.* = comment_node; break :blk comment_node; } @@ -3158,10 +3280,11 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try arena.create(ast.Node.MultilineStringLiteral{ + const node = try arena.create(ast.Node.MultilineStringLiteral); + node.* = ast.Node.MultilineStringLiteral{ .base = ast.Node{ .id = ast.Node.Id.MultilineStringLiteral }, .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), - }); + }; try node.lines.push(token_index); while (true) { const multiline_str = nextToken(tok_it, tree); @@ -3186,25 +3309,27 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: OptionalCtx, token_ptr: Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ + const node = try arena.create(ast.Node.Suspend); + node.* = ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, .suspend_token = token_index, .body = null, - }); + }; ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; return true; }, Token.Id.Keyword_if => { - const node = try arena.create(ast.Node.If{ + const node = try arena.create(ast.Node.If); + node.* = ast.Node.If{ .base = ast.Node{ .id = ast.Node.Id.If }, .if_token = token_index, .condition = undefined, .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; @@ -3238,13 +3363,14 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_switch => { - const node = try arena.create(ast.Node.Switch{ + const node = try arena.create(ast.Node.Switch); + node.* = ast.Node.Switch{ .base = ast.Node{ .id = ast.Node.Id.Switch }, .switch_token = token_index, .expr = undefined, .cases = ast.Node.Switch.CaseList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&node.base); stack.append(State{ @@ -3260,25 +3386,27 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_comptime => { - const node = try arena.create(ast.Node.Comptime{ + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = undefined, .doc_comments = null, - }); + }; ctx.store(&node.base); try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); return true; }, Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; return true; @@ -3397,7 +3525,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk, Token.Id.BracketStarBracket => ast.Node.PrefixOp.Op{ + + Token.Id.Asterisk, + Token.Id.AsteriskAsterisk, + Token.Id.BracketStarBracket, + Token.Id.BracketStarCBracket, + => ast.Node.PrefixOp.Op{ .PtrType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, @@ -3412,10 +3545,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { } fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { - return arena.create(T{ + const result = try arena.create(T); + result.* = T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, - }); + }; + return result; } fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: OptionalCtx, comptime T: type, token_index: TokenIndex) !*T { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 2b60fb9b00..5b7b7aa2a9 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,17 @@ +test "zig fmt: C pointers" { + try testCanonical( + \\const Ptr = [*c]i32; + \\ + ); +} + +test "zig fmt: threadlocal" { + try testCanonical( + \\threadlocal var x: i32 = 1234; + \\ + ); +} + test "zig fmt: linksection" { try testCanonical( \\export var aoeu: u64 linksection(".text.derp") = 1234; @@ -5,6 +19,7 @@ test "zig fmt: linksection" { \\ ); } + test "zig fmt: shebang line" { try testCanonical( \\#!/usr/bin/env zig @@ -1940,7 +1955,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { warn("std.zig.render returned {} instead of {}\n", anything_changed, changes_expected); return error.TestFailed; } - std.debug.assert(anything_changed == changes_expected); + std.testing.expect(anything_changed == changes_expected); failing_allocator.allocator.free(result_source); break :x failing_allocator.index; }; diff --git a/std/zig/render.zig b/std/zig/render.zig index e55a0beb93..66aba75e1b 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1706,6 +1706,9 @@ fn renderVarDecl( try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime } + if (var_decl.thread_local_token) |thread_local_token| { + try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal + } try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 4941fe2cc2..877e4e8db3 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -53,6 +53,7 @@ pub const Token = struct { Keyword{ .bytes = "switch", .id = Id.Keyword_switch }, Keyword{ .bytes = "test", .id = Id.Keyword_test }, Keyword{ .bytes = "this", .id = Id.Keyword_this }, + 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 }, @@ -140,6 +141,7 @@ pub const Token = struct { LineComment, DocComment, BracketStarBracket, + BracketStarCBracket, ShebangLine, Keyword_align, Keyword_and, @@ -182,6 +184,7 @@ pub const Token = struct { Keyword_switch, Keyword_test, Keyword_this, + Keyword_threadlocal, Keyword_true, Keyword_try, Keyword_undefined, @@ -277,6 +280,7 @@ pub const Tokenizer = struct { SawAtSign, LBracket, LBracketStar, + LBracketStarC, }; pub fn next(self: *Tokenizer) Token { @@ -454,6 +458,9 @@ pub const Tokenizer = struct { }, State.LBracketStar => switch (c) { + 'c' => { + state = State.LBracketStarC; + }, ']' => { result.id = Token.Id.BracketStarBracket; self.index += 1; @@ -465,6 +472,18 @@ pub const Tokenizer = struct { }, }, + 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.AmpersandEqual; @@ -1033,6 +1052,7 @@ pub const Tokenizer = struct { State.CharLiteralEnd, State.StringLiteralBackslash, State.LBracketStar, + State.LBracketStarC, => { result.id = Token.Id.Invalid; }, @@ -1167,12 +1187,15 @@ test "tokenizer" { testTokenize("test", []Token.Id{Token.Id.Keyword_test}); } -test "tokenizer - unknown length pointer" { +test "tokenizer - unknown length pointer and then c pointer" { testTokenize( \\[*]u8 + \\[*c]u8 , []Token.Id{ Token.Id.BracketStarBracket, Token.Id.Identifier, + Token.Id.BracketStarCBracket, + Token.Id.Identifier, }); } @@ -1345,5 +1368,5 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { } } const last_token = tokenizer.next(); - std.debug.assert(last_token.id == Token.Id.Eof); + std.testing.expect(last_token.id == Token.Id.Eof); } diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 1d031343d6..0000000000 --- a/test/behavior.zig +++ /dev/null @@ -1,77 +0,0 @@ -const builtin = @import("builtin"); - -comptime { - _ = @import("cases/align.zig"); - _ = @import("cases/alignof.zig"); - _ = @import("cases/array.zig"); - _ = @import("cases/asm.zig"); - _ = @import("cases/atomics.zig"); - _ = @import("cases/bitcast.zig"); - _ = @import("cases/bool.zig"); - _ = @import("cases/bugs/1076.zig"); - _ = @import("cases/bugs/1111.zig"); - _ = @import("cases/bugs/1277.zig"); - _ = @import("cases/bugs/1322.zig"); - _ = @import("cases/bugs/1381.zig"); - _ = @import("cases/bugs/1421.zig"); - _ = @import("cases/bugs/1442.zig"); - _ = @import("cases/bugs/1486.zig"); - _ = @import("cases/bugs/394.zig"); - _ = @import("cases/bugs/655.zig"); - _ = @import("cases/bugs/656.zig"); - _ = @import("cases/bugs/726.zig"); - _ = @import("cases/bugs/828.zig"); - _ = @import("cases/bugs/920.zig"); - _ = @import("cases/byval_arg_var.zig"); - _ = @import("cases/cancel.zig"); - _ = @import("cases/cast.zig"); - _ = @import("cases/const_slice_child.zig"); - _ = @import("cases/coroutine_await_struct.zig"); - _ = @import("cases/coroutines.zig"); - _ = @import("cases/defer.zig"); - _ = @import("cases/enum.zig"); - _ = @import("cases/enum_with_members.zig"); - _ = @import("cases/error.zig"); - _ = @import("cases/eval.zig"); - _ = @import("cases/field_parent_ptr.zig"); - _ = @import("cases/fn.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); - _ = @import("cases/for.zig"); - _ = @import("cases/generics.zig"); - _ = @import("cases/if.zig"); - _ = @import("cases/import.zig"); - _ = @import("cases/incomplete_struct_param_tld.zig"); - _ = @import("cases/ir_block_deps.zig"); - _ = @import("cases/math.zig"); - _ = @import("cases/merge_error_sets.zig"); - _ = @import("cases/misc.zig"); - _ = @import("cases/namespace_depends_on_compile_var/index.zig"); - _ = @import("cases/new_stack_call.zig"); - _ = @import("cases/null.zig"); - _ = @import("cases/optional.zig"); - _ = @import("cases/pointers.zig"); - _ = @import("cases/popcount.zig"); - _ = @import("cases/pub_enum/index.zig"); - _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("cases/reflection.zig"); - _ = @import("cases/sizeof_and_typeof.zig"); - _ = @import("cases/slice.zig"); - _ = @import("cases/struct.zig"); - _ = @import("cases/struct_contains_null_ptr_itself.zig"); - _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch.zig"); - _ = @import("cases/switch_prong_err_enum.zig"); - _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/syntax.zig"); - _ = @import("cases/this.zig"); - _ = @import("cases/try.zig"); - _ = @import("cases/type_info.zig"); - _ = @import("cases/undefined.zig"); - _ = @import("cases/underscore.zig"); - _ = @import("cases/union.zig"); - _ = @import("cases/var_args.zig"); - _ = @import("cases/void.zig"); - _ = @import("cases/while.zig"); - _ = @import("cases/widening.zig"); - _ = @import("cases/bit_shifting.zig"); -} diff --git a/test/cases/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8..0000000000 --- a/test/cases/array.zig +++ /dev/null @@ -1,173 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = u32(0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - assert(accumulator == 15); - assert(getArrayLen(array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - -test "void arrays" { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - assert(@sizeOf(@typeOf(array)) == 0); - assert(array.len == 4); -} - -test "array literal" { - const hex_mult = []u16{ - 4096, - 256, - 16, - 1, - }; - - assert(hex_mult.len == 4); - assert(hex_mult[1] == 256); -} - -test "array dot len const expr" { - assert(comptime x: { - break :x some_array.len == 4; - }); -} - -const ArrayDotLenConstExpr = struct { - y: [some_array.len]u8, -}; -const some_array = []u8{ - 0, - 1, - 2, - 3, -}; - -test "nested arrays" { - const array_of_strings = [][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; - for (array_of_strings) |s, i| { - if (i == 0) assert(mem.eql(u8, s, "hello")); - if (i == 1) assert(mem.eql(u8, s, "this")); - if (i == 2) assert(mem.eql(u8, s, "is")); - if (i == 3) assert(mem.eql(u8, s, "my")); - if (i == 4) assert(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - assert(s_array[0].b == 1); - assert(s_array[1].b == 2); - assert(s_array[2].b == 3); -} - -test "array literal with specified size" { - var array = [2]u8{ - 1, - 2, - }; - assert(array[0] == 1); - assert(array[1] == 2); -} - -test "array child property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).Child == i32); -} - -test "array len property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).len == 5); -} - -test "array len field" { - var arr = [4]u8{ 0, 0, 0, 0 }; - var ptr = &arr; - assert(arr.len == 4); - comptime assert(arr.len == 4); - assert(ptr.len == 4); - comptime assert(ptr.len == 4); -} - -test "single-item pointer to array indexing and slicing" { - testSingleItemPtrArrayIndexSlice(); - comptime testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() void { - var array = "aaaa"; - doSomeMangling(&array); - assert(mem.eql(u8, "azya", array)); -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - -test "implicit cast single-item pointer" { - testImplicitCastSingleItemPtr(); - comptime testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() void { - var byte: u8 = 100; - const slice = (*[1]u8)(&byte)[0..]; - slice[0] += 1; - assert(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evalutating function that takes array by value" { - const arr = []u8{ 0, 1 }; - _ = comptime testArrayByValAtComptime(arr); - _ = comptime testArrayByValAtComptime(arr); -} - -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - assert(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} diff --git a/test/cases/asm.zig b/test/cases/asm.zig deleted file mode 100644 index 63e37c857c..0000000000 --- a/test/cases/asm.zig +++ /dev/null @@ -1,48 +0,0 @@ -const config = @import("builtin"); -const assert = @import("std").debug.assert; - -comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - asm volatile ( - \\.globl aoeu; - \\.type aoeu, @function; - \\.set aoeu, derp; - ); - } -} - -test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - assert(aoeu() == 1234); - } -} - -test "output constraint modifiers" { - // This is only testing compilation. - var a: u32 = 3; - asm volatile ("" : [_]"=m,r"(a) : : ""); - asm volatile ("" : [_]"=r,m"(a) : : ""); -} - -test "alternative constraints" { - // Make sure we allow commas as a separator for alternative constraints. - var a: u32 = 3; - asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : ""); -} - -test "sized integer/float in asm input" { - asm volatile ("" : : [_]"m"(usize(3)) : ""); - asm volatile ("" : : [_]"m"(i15(-3)) : ""); - asm volatile ("" : : [_]"m"(u3(3)) : ""); - asm volatile ("" : : [_]"m"(i3(3)) : ""); - asm volatile ("" : : [_]"m"(u121(3)) : ""); - asm volatile ("" : : [_]"m"(i121(3)) : ""); - asm volatile ("" : : [_]"m"(f32(3.17)) : ""); - asm volatile ("" : : [_]"m"(f64(3.17)) : ""); -} - -extern fn aoeu() i32; - -export fn derp() i32 { - return 1234; -} diff --git a/test/cases/optional.zig b/test/cases/optional.zig deleted file mode 100644 index d43682bbec..0000000000 --- a/test/cases/optional.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -pub const EmptyStruct = struct {}; - -test "optional pointer to size zero struct" { - var e = EmptyStruct{}; - var o: ?*EmptyStruct = &e; - assert(o != null); -} - -test "equality compare nullable pointers" { - testNullPtrsEql(); - comptime testNullPtrsEql(); -} - -fn testNullPtrsEql() void { - var number: i32 = 1234; - - var x: ?*i32 = null; - var y: ?*i32 = null; - assert(x == y); - y = &number; - assert(x != y); - assert(x != &number); - assert(&number != x); - x = &number; - assert(x == y); - assert(x == &number); - assert(&number == x); -} diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig deleted file mode 100644 index 47afb60a2e..0000000000 --- a/test/cases/pointers.zig +++ /dev/null @@ -1,44 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "dereference pointer" { - comptime testDerefPtr(); - testDerefPtr(); -} - -fn testDerefPtr() void { - var x: i32 = 1234; - var y = &x; - y.* += 1; - assert(x == 1235); -} - -test "pointer arithmetic" { - var ptr = c"abcd"; - - assert(ptr[0] == 'a'); - ptr += 1; - assert(ptr[0] == 'b'); - ptr += 1; - assert(ptr[0] == 'c'); - ptr += 1; - assert(ptr[0] == 'd'); - ptr += 1; - assert(ptr[0] == 0); - ptr -= 1; - assert(ptr[0] == 'd'); - ptr -= 1; - assert(ptr[0] == 'c'); - ptr -= 1; - assert(ptr[0] == 'b'); - ptr -= 1; - assert(ptr[0] == 'a'); -} - -test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); -} - -fn PtrOf(comptime T: type) type { - return *T; -} diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e1..0000000000 --- a/test/cases/reflection.zig +++ /dev/null @@ -1,95 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const reflection = @This(); - -test "reflection: array, pointer, optional, error union type child" { - comptime { - assert(([10]u8).Child == u8); - assert((*u8).Child == u8); - assert((anyerror!u8).Payload == u8); - assert((?u8).Child == u8); - } -} - -test "reflection: function return type, var args, and param types" { - comptime { - assert(@typeOf(dummy).ReturnType == i32); - assert(!@typeOf(dummy).is_var_args); - assert(@typeOf(dummy_varargs).is_var_args); - assert(@typeOf(dummy).arg_count == 3); - assert(@ArgType(@typeOf(dummy), 0) == bool); - assert(@ArgType(@typeOf(dummy), 1) == i32); - assert(@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 { - assert(@memberCount(Foo) == 3); - - assert(@memberType(Foo, 0) == i32); - assert(@memberType(Foo, 1) == bool); - assert(@memberType(Foo, 2) == void); - - assert(mem.eql(u8, @memberName(Foo, 0), "one")); - assert(mem.eql(u8, @memberName(Foo, 1), "two")); - assert(mem.eql(u8, @memberName(Foo, 2), "three")); - } -} - -test "reflection: enum member types and names" { - comptime { - assert(@memberCount(Bar) == 4); - - assert(@memberType(Bar, 0) == void); - assert(@memberType(Bar, 1) == i32); - assert(@memberType(Bar, 2) == bool); - assert(@memberType(Bar, 3) == f64); - - assert(mem.eql(u8, @memberName(Bar, 0), "One")); - assert(mem.eql(u8, @memberName(Bar, 1), "Two")); - assert(mem.eql(u8, @memberName(Bar, 2), "Three")); - assert(mem.eql(u8, @memberName(Bar, 3), "Four")); - } -} - -test "reflection: @field" { - var f = Foo{ - .one = 42, - .two = true, - .three = void{}, - }; - - assert(f.one == f.one); - assert(@field(f, "o" ++ "ne") == f.one); - assert(@field(f, "t" ++ "wo") == f.two); - assert(@field(f, "th" ++ "ree") == f.three); - assert(@field(Foo, "const" ++ "ant") == Foo.constant); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "T" ++ "wo") == Bar.Two); - assert(@field(Bar, "Th" ++ "ree") == Bar.Three); - assert(@field(Bar, "F" ++ "our") == Bar.Four); - assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); - @field(f, "o" ++ "ne") = 4; - assert(f.one == 4); -} - -const Foo = struct { - const constant = 52; - - one: i32, - two: bool, - three: void, -}; - -const Bar = union(enum) { - One: void, - Two: i32, - Three: bool, - Four: f64, -}; diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig deleted file mode 100644 index 11c6b2f6ba..0000000000 --- a/test/cases/sizeof_and_typeof.zig +++ /dev/null @@ -1,69 +0,0 @@ -const builtin = @import("builtin"); -const assert = @import("std").debug.assert; - -test "@sizeOf and @typeOf" { - const y: @typeOf(x) = 120; - assert(@sizeOf(@typeOf(y)) == 2); -} -const x: u16 = 13; -const z: @typeOf(x) = 19; - -const A = struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -const P = packed struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -test "@byteOffsetOf" { - // Packed structs have fixed memory layout - assert(@byteOffsetOf(P, "a") == 0); - assert(@byteOffsetOf(P, "b") == 1); - assert(@byteOffsetOf(P, "c") == 5); - assert(@byteOffsetOf(P, "d") == 6); - assert(@byteOffsetOf(P, "e") == 6); - assert(@byteOffsetOf(P, "f") == 7); - assert(@byteOffsetOf(P, "g") == 9); - - // Normal struct fields can be moved/padded - var a: A = undefined; - assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); - assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); - assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); - assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); - assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); - assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); - assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); -} - -test "@bitOffsetOf" { - // Packed structs have fixed memory layout - assert(@bitOffsetOf(P, "a") == 0); - assert(@bitOffsetOf(P, "b") == 8); - assert(@bitOffsetOf(P, "c") == 40); - assert(@bitOffsetOf(P, "d") == 48); - assert(@bitOffsetOf(P, "e") == 51); - assert(@bitOffsetOf(P, "f") == 56); - assert(@bitOffsetOf(P, "g") == 72); - - assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); - assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); - assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); - assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); - assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); - assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); - assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); -} diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig deleted file mode 100644 index 07987ae32b..0000000000 --- a/test/cases/struct_contains_slice_of_itself.zig +++ /dev/null @@ -1,43 +0,0 @@ -const assert = @import("std").debug.assert; - -const Node = struct { - payload: i32, - children: []Node, -}; - -test "struct contains slice of itself" { - var other_nodes = []Node{ - Node{ - .payload = 31, - .children = []Node{}, - }, - Node{ - .payload = 32, - .children = []Node{}, - }, - }; - var nodes = []Node{ - Node{ - .payload = 1, - .children = []Node{}, - }, - Node{ - .payload = 2, - .children = []Node{}, - }, - Node{ - .payload = 3, - .children = other_nodes[0..], - }, - }; - const root = Node{ - .payload = 1234, - .children = nodes[0..], - }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); -} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index 6f99268c08..0000000000 --- a/test/cases/type_info.zig +++ /dev/null @@ -1,259 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const TypeInfo = @import("builtin").TypeInfo; -const TypeId = @import("builtin").TypeId; - -test "type info: tag type, void info" { - testBasic(); - comptime testBasic(); -} - -fn testBasic() void { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); -} - -test "type info: integer, floating point type info" { - testIntFloat(); - comptime testIntFloat(); -} - -fn testIntFloat() void { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); - - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); -} - -test "type info: pointer type info" { - testPointer(); - comptime testPointer(); -} - -fn testPointer() void { - const u32_ptr_info = @typeInfo(*u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); - assert(u32_ptr_info.Pointer.child == u32); -} - -test "type info: unknown length pointer type info" { - testUnknownLenPtr(); - comptime testUnknownLenPtr(); -} - -fn testUnknownLenPtr() void { - const u32_ptr_info = @typeInfo([*]const volatile f64); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - assert(u32_ptr_info.Pointer.is_const == true); - assert(u32_ptr_info.Pointer.is_volatile == true); - assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); - assert(u32_ptr_info.Pointer.child == f64); -} - -test "type info: slice type info" { - testSlice(); - comptime testSlice(); -} - -fn testSlice() void { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Pointer); - assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); - assert(u32_slice_info.Pointer.is_const == false); - assert(u32_slice_info.Pointer.is_volatile == false); - assert(u32_slice_info.Pointer.alignment == 4); - assert(u32_slice_info.Pointer.child == u32); -} - -test "type info: array type info" { - testArray(); - comptime testArray(); -} - -fn testArray() void { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); -} - -test "type info: optional type info" { - testOptional(); - comptime testOptional(); -} - -fn testOptional() void { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Optional); - assert(null_info.Optional.child == void); -} - -test "type info: promise info" { - testPromise(); - comptime testPromise(); -} - -fn testPromise() void { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == null); - - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child.? == usize); -} - -test "type info: error set, error union info" { - testErrorSet(); - comptime testErrorSet(); -} - -fn testErrorSet() void { - const TestErrorSet = error{ - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); -} - -test "type info: enum info" { - testEnum(); - comptime testEnum(); -} - -fn testEnum() void { - const Os = @import("builtin").Os; - - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 32); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); - assert(os_info.Enum.fields[10].value == 10); - assert(os_info.Enum.tag_type == u5); - assert(os_info.Enum.defs.len == 0); -} - -test "type info: union info" { - testUnion(); - comptime testUnion(); -} - -fn testUnion() void { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type.? == TypeId); - assert(typeinfo_info.Union.fields.len == 24); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == null); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: *c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == null); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == *c_void); -} - -test "type info: struct info" { - testStruct(); - comptime testStruct(); -} - -fn testStruct() void { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == *TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); -} - -const TestStruct = packed struct { - const Self = @This(); - - fieldA: usize, - fieldB: void, - fieldC: *Self, - - pub fn foo(self: *const Self) void {} -}; - -test "type info: function type info" { - testFunction(); - comptime testFunction(); -} - -fn testFunction() void { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == null); - assert(fn_info.Fn.async_allocator_type == null); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); -} - -fn foo(comptime a: usize, b: bool, args: ...) usize { - return 0; -} - -test "typeInfo with comptime parameter in struct fn def" { - const S = struct { - pub fn func(comptime x: f32) void {} - }; - comptime var info = @typeInfo(S); -} diff --git a/test/cli.zig b/test/cli.zig index a07c447d2d..1520b3bde0 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const os = std.os; -const assertOrPanic = std.debug.assertOrPanic; +const testing = std.testing; var a: *std.mem.Allocator = undefined; @@ -27,9 +27,9 @@ pub fn main() !void { std.debug.warn("Expected second argument to be cache root directory path\n"); return error.InvalidArgs; }); - const zig_exe = try os.path.resolve(a, zig_exe_rel); + const zig_exe = try os.path.resolve(a, [][]const u8{zig_exe_rel}); - const dir_path = try os.path.join(a, cache_root, "clitest"); + const dir_path = try os.path.join(a, [][]const u8{ cache_root, "clitest" }); const TestFn = fn ([]const u8, []const u8) anyerror!void; const test_fns = []TestFn{ testZigInitLib, @@ -87,20 +87,20 @@ fn exec(cwd: []const u8, argv: []const []const u8) !os.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" }); - assertOrPanic(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n")); + testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 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" }); - assertOrPanic(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n")); + 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 != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return; - const example_zig_path = try os.path.join(a, dir_path, "example.zig"); - const example_s_path = try os.path.join(a, dir_path, "example.s"); + const example_zig_path = try os.path.join(a, [][]const u8{ dir_path, "example.zig" }); + const example_s_path = try os.path.join(a, [][]const u8{ dir_path, "example.s" }); try std.io.writeFile(example_zig_path, \\// Type your code here, or load an example. @@ -126,6 +126,6 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { _ = try exec(dir_path, args); const out_asm = try std.io.readFileAlloc(a, example_s_path); - assertOrPanic(std.mem.indexOf(u8, out_asm, "square:") != null); - assertOrPanic(std.mem.indexOf(u8, out_asm, "imul\tedi, edi") != null); + testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); + testing.expect(std.mem.indexOf(u8, out_asm, "imul\tedi, edi") != null); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index be839f0550..9ef4af4162 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,270 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "C pointer to c_void", + \\export fn a() void { + \\ var x: *c_void = undefined; + \\ var y: [*c]c_void = x; + \\} + , + ".tmp_source.zig:3:12: error: C pointers cannot point opaque types", + ); + + cases.addTest( + "directly embedding opaque type in struct and union", + \\const O = @OpaqueType(); + \\const Foo = struct { + \\ o: O, + \\}; + \\const Bar = union { + \\ One: i32, + \\ Two: O, + \\}; + \\export fn a() void { + \\ var foo: Foo = undefined; + \\} + \\export fn b() void { + \\ var bar: Bar = undefined; + \\} + , + ".tmp_source.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs", + ".tmp_source.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions", + ); + + cases.addTest( + "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; + \\} + \\export fn b() void { + \\ var x: [*c]const u8 = undefined; + \\ var y: *u8 = x; + \\} + \\export fn c() void { + \\ var x: [*c]u8 = undefined; + \\ var y: *u32 = x; + \\} + \\export fn d() void { + \\ var y: *align(1) u32 = undefined; + \\ var x: [*c]u32 = y; + \\} + \\export fn e() void { + \\ var y: *const u8 = undefined; + \\ var x: [*c]u8 = y; + \\} + \\export fn f() void { + \\ var y: *u8 = undefined; + \\ var x: [*c]u32 = y; + \\} + , + ".tmp_source.zig:3:27: error: cast increases pointer alignment", + ".tmp_source.zig:7:18: error: cast discards const qualifier", + ".tmp_source.zig:11:19: error: expected type '*u32', found '[*c]u8'", + ".tmp_source.zig:11:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'", + ".tmp_source.zig:15:22: error: cast increases pointer alignment", + ".tmp_source.zig:19:21: error: cast discards const qualifier", + ".tmp_source.zig:23:22: error: expected type '[*c]u32', found '*u8'", + ); + + cases.addTest( + "implicit casting null c pointer to zig pointer", + \\comptime { + \\ var c_ptr: [*c]u8 = 0; + \\ var zig_ptr: *u8 = c_ptr; + \\} + , + ".tmp_source.zig:3:24: error: null pointer casted to type '*u8'", + ); + + cases.addTest( + "implicit casting undefined c pointer to zig pointer", + \\comptime { + \\ var c_ptr: [*c]u8 = undefined; + \\ var zig_ptr: *u8 = c_ptr; + \\} + , + ".tmp_source.zig:3:24: error: use of undefined value here causes undefined behavior", + ); + + cases.addTest( + "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; + \\ var ptr_opt_many_ptr = &opt_many_ptr; + \\ var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; + \\ ptr_opt_many_ptr = c_ptr; + \\} + \\export fn entry2() void { + \\ 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; + \\} + , + ".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'", + ".tmp_source.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'", + ".tmp_source.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'", + ".tmp_source.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'", + ".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'", + ".tmp_source.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'", + ); + + cases.addTest( + "implicit casting too big integers to C pointers", + \\export fn a() void { + \\ var ptr: [*c]u8 = (1 << 64) + 1; + \\} + \\export fn b() void { + \\ var x: @IntType(false, 65) = 0x1234; + \\ var ptr: [*c]u8 = x; + \\} + , + ".tmp_source.zig:2:33: error: integer value 71615590737044764481 cannot be implicitly casted to type 'usize'", + ".tmp_source.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'", + ); + + cases.addTest( + "C pointer pointing to non C ABI compatible type or has align attr", + \\const Foo = struct {}; + \\export fn a() void { + \\ const T = [*c]Foo; + \\} + , + ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", + ); + + cases.addTest( + "@truncate undefined value", + \\export fn entry() void { + \\ var z = @truncate(u8, u16(undefined)); + \\} + , + ".tmp_source.zig:2:30: error: use of undefined value here causes undefined behavior", + ); + + cases.addTest( + "return invalid type from test", + \\test "example" { return 1; } + , + ".tmp_source.zig:1:25: error: integer value 1 cannot be implicitly casted to type 'void'", + ); + + cases.add( + "threadlocal qualifier on const", + \\threadlocal const x: i32 = 1234; + \\export fn entry() i32 { + \\ return x; + \\} + , + ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant", + ); + + cases.add( + "threadlocal qualifier on local variable", + \\export fn entry() void { + \\ threadlocal var x: i32 = 1234; + \\} + , + ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal", + ); + + cases.add( + "@bitCast same size but bit count mismatch", + \\export fn entry(byte: u8) void { + \\ var oops = @bitCast(u7, byte); + \\} + , + ".tmp_source.zig:2:16: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", + ); + + cases.add( + "attempted `&&`", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a && b) { + \\ return 1234; + \\ } + \\ return 5678; + \\} + , + ".tmp_source.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND.", + ); + + cases.add( + "attempted `||` on boolean values", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a || b) { + \\ return 1234; + \\ } + \\ return 5678; + \\} + , + ".tmp_source.zig:2:9: error: expected error set type, found 'bool'", + ".tmp_source.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", + ); + + cases.add( + "compile log a pointer to an opaque value", + \\export fn entry() void { + \\ @compileLog(@ptrCast(*const c_void, &entry)); + \\} + , + ".tmp_source.zig:2:5: error: found compile log statement", + ); + + cases.add( + "duplicate boolean switch value", + \\comptime { + \\ const x = switch (true) { + \\ true => false, + \\ false => true, + \\ true => false, + \\ }; + \\} + \\comptime { + \\ const x = switch (true) { + \\ false => true, + \\ true => false, + \\ false => true, + \\ }; + \\} + , + ".tmp_source.zig:5:9: error: duplicate switch value", + ".tmp_source.zig:12:9: error: duplicate switch value", + ); + + cases.add( + "missing boolean switch value", + \\comptime { + \\ const x = switch (true) { + \\ true => false, + \\ }; + \\} + \\comptime { + \\ const x = switch (true) { + \\ false => true, + \\ }; + \\} + , + ".tmp_source.zig:2:15: error: switch must handle all possibilities", + ".tmp_source.zig:7:15: error: switch must handle all possibilities", + ); + + cases.add( + "reading past end of pointer casted array", + \\comptime { + \\ const array = "aoeu"; + \\ const slice = array[2..]; + \\ const int_ptr = @ptrCast(*const u24, slice.ptr); + \\ const deref = int_ptr.*; + \\} + , + ".tmp_source.zig:5:26: error: attempt to read 3 bytes from [4]u8 at index 2 which is 2 bytes", + ); + cases.add( "error note for function parameter incompatibility", \\fn do_the_thing(func: fn (arg: i32) void) void {} @@ -82,13 +346,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "Panic declared with wrong type signature in tests", + "wrong panic signature, runtime function", \\test "" {} \\ \\pub fn panic() void {} \\ , - ".tmp_source.zig:3:5: error: expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'", + ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'", + ); + + cases.add( + "wrong panic signature, generic function", + \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { + \\ while (true) {} + \\} + , + ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'", + ".tmp_source.zig:1:5: note: only one of the functions is generic", ); cases.add( @@ -208,8 +482,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const Errors = error{} || u16; \\} , - ".tmp_source.zig:2:20: error: expected error set type, found 'u8'", - ".tmp_source.zig:5:31: error: expected error set type, found 'u16'", + ".tmp_source.zig:2:20: error: expected error set type, found type 'u8'", + ".tmp_source.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", + ".tmp_source.zig:5:31: error: expected error set type, found type 'u16'", + ".tmp_source.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", ); cases.add( @@ -238,7 +514,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ f(i32); \\} , - ".tmp_source.zig:4:5: error: use of undefined value", + ".tmp_source.zig:4:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -638,7 +914,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ command.exec(); \\} , - ".tmp_source.zig:6:12: error: use of undefined value", + ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -651,7 +927,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ command.exec(); \\} , - ".tmp_source.zig:6:12: error: use of undefined value", + ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2559,7 +2835,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:1:13: error: newline not allowed in string literal", + ".tmp_source.zig:1:15: error: newline not allowed in string literal", ); cases.add( @@ -2622,7 +2898,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(x)); } , - ".tmp_source.zig:1:15: error: use of undefined value", + ".tmp_source.zig:1:15: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2632,7 +2908,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a / a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2642,7 +2918,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a /= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2652,7 +2928,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a % a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2662,7 +2938,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a %= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2672,7 +2948,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a + a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2682,7 +2958,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a += a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2692,7 +2968,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a +% a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2702,7 +2978,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a +%= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2712,7 +2988,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a - a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2722,7 +2998,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a -= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2732,7 +3008,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a -% a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2742,7 +3018,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a -%= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2752,7 +3028,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a * a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2762,7 +3038,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a *= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2772,7 +3048,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a *% a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2782,7 +3058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a *%= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2792,7 +3068,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a << 2; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2802,7 +3078,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a <<= 2; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2812,7 +3088,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a >> 2; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2822,7 +3098,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a >>= 2; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2832,7 +3108,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a & a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2842,7 +3118,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a &= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2852,7 +3128,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a | a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2862,7 +3138,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a |= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2872,7 +3148,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a ^ a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2882,7 +3158,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a ^= a; \\} , - ".tmp_source.zig:3:5: error: use of undefined value", + ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2892,7 +3168,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a == a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2902,7 +3178,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a != a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2912,7 +3188,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a > a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2922,7 +3198,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a >= a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2932,7 +3208,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a < a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2942,7 +3218,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a <= a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2952,7 +3228,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a and a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2962,7 +3238,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a or a; \\} , - ".tmp_source.zig:3:9: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2972,7 +3248,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = -a; \\} , - ".tmp_source.zig:3:10: error: use of undefined value", + ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2982,7 +3258,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = -%a; \\} , - ".tmp_source.zig:3:11: error: use of undefined value", + ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -2992,7 +3268,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = ~a; \\} , - ".tmp_source.zig:3:10: error: use of undefined value", + ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -3002,7 +3278,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = !a; \\} , - ".tmp_source.zig:3:10: error: use of undefined value", + ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -3012,7 +3288,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a orelse false; \\} , - ".tmp_source.zig:3:11: error: use of undefined value", + ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -3022,7 +3298,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a catch |err| false; \\} , - ".tmp_source.zig:3:11: error: use of undefined value", + ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", ); cases.add( @@ -3134,7 +3410,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return 2; \\} , - ".tmp_source.zig:2:15: error: unable to infer expression type", + ".tmp_source.zig:2:15: error: values of type 'comptime_int' must be comptime known", ); cases.add( @@ -3214,7 +3490,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "truncate sign mismatch", \\fn f() i8 { - \\ const x: u32 = 10; + \\ var x: u32 = 10; \\ return @truncate(i8, x); \\} \\ @@ -3480,7 +3756,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:2:11: error: expected type, found 'i32'", + ".tmp_source.zig:2:11: error: expected type 'type', found 'i32'", ); cases.add( @@ -4424,20 +4700,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal", ); - cases.add( - "@setEvalBranchQuota in non-root comptime execution context", - \\comptime { - \\ foo(); - \\} - \\fn foo() void { - \\ @setEvalBranchQuota(1001); - \\} - , - ".tmp_source.zig:5:5: error: @setEvalBranchQuota must be called from the top of the comptime stack", - ".tmp_source.zig:2:8: note: called from here", - ".tmp_source.zig:1:10: note: called from here", - ); - cases.add( "wrong pointer implicitly casted to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); @@ -5272,4 +5534,42 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", ); + + cases.add( + "runtime assignment to comptime struct type", + \\const Foo = struct { + \\ Bar: u8, + \\ Baz: type, + \\}; + \\export fn f() void { + \\ var x: u8 = 0; + \\ const foo = Foo { .Bar = x, .Baz = u8 }; + \\} + , + ".tmp_source.zig:7:30: error: unable to evaluate constant expression", + ); + + cases.add( + "runtime assignment to comptime union type", + \\const Foo = union { + \\ Bar: u8, + \\ Baz: type, + \\}; + \\export fn f() void { + \\ var x: u8 = 0; + \\ const foo = Foo { .Bar = x }; + \\} + , + ".tmp_source.zig:7:30: error: unable to evaluate constant expression", + ); + + cases.addTest( + "nested vectors", + \\export fn entry() void { + \\ const V = @Vector(4, @Vector(4, u8)); + \\ var v: V = undefined; + \\} + , + ".tmp_source.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", + ); } diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 7c13f5b6fa..12cac64b3a 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("pointer casting null to non-optional pointer", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var c_ptr: [*c]u8 = 0; + \\ var zig_ptr: *u8 = c_ptr; + \\} + ); + cases.addRuntimeSafety("@intToEnum - no matching tag value", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); @@ -94,6 +104,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} ); + cases.addRuntimeSafety("vector integer addition overflow", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var a: @Vector(4, i32) = []i32{ 1, 2, 2147483643, 4 }; + \\ var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 }; + \\ const x = add(a, b); + \\} + \\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { + \\ return a + b; + \\} + ); + cases.addRuntimeSafety("integer subtraction overflow", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); @@ -362,6 +386,23 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} ); + // @intCast a runtime integer to u0 actually results in a comptime-known value, + // but we still emit a safety check to ensure the integer was 0 and thus + // did not truncate information. + cases.addRuntimeSafety("@intCast to u0", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\pub fn main() void { + \\ bar(1, 1); + \\} + \\ + \\fn bar(one: u1, not_zero: i32) void { + \\ var x = one << @intCast(u0, not_zero); + \\} + ); + // This case makes sure that the code compiles and runs. There is not actually a special // runtime safety check having to do specifically with error return traces across suspend points. cases.addRuntimeSafety("error return trace across suspend points", diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 0000000000..df311637fa --- /dev/null +++ b/test/stage1/behavior.zig @@ -0,0 +1,82 @@ +comptime { + _ = @import("behavior/align.zig"); + _ = @import("behavior/alignof.zig"); + _ = @import("behavior/array.zig"); + _ = @import("behavior/asm.zig"); + _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bit_shifting.zig"); + _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/bool.zig"); + _ = @import("behavior/bswap.zig"); + _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1277.zig"); + _ = @import("behavior/bugs/1322.zig"); + _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1421.zig"); + _ = @import("behavior/bugs/1442.zig"); + _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/1851.zig"); + _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/655.zig"); + _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); + _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/cancel.zig"); + _ = @import("behavior/cast.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/coroutine_await_struct.zig"); + _ = @import("behavior/coroutines.zig"); + _ = @import("behavior/defer.zig"); + _ = @import("behavior/enum.zig"); + _ = @import("behavior/enum_with_members.zig"); + _ = @import("behavior/error.zig"); + _ = @import("behavior/eval.zig"); + _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/fn.zig"); + _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/for.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/if.zig"); + _ = @import("behavior/import.zig"); + _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/inttoptr.zig"); + _ = @import("behavior/ir_block_deps.zig"); + _ = @import("behavior/math.zig"); + _ = @import("behavior/merge_error_sets.zig"); + _ = @import("behavior/misc.zig"); + _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); + _ = @import("behavior/new_stack_call.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/optional.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); + _ = @import("behavior/ptrcast.zig"); + _ = @import("behavior/pub_enum/index.zig"); + _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/sizeof_and_typeof.zig"); + _ = @import("behavior/slice.zig"); + _ = @import("behavior/struct.zig"); + _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); + _ = @import("behavior/syntax.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/try.zig"); + _ = @import("behavior/type_info.zig"); + _ = @import("behavior/undefined.zig"); + _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); + _ = @import("behavior/var_args.zig"); + _ = @import("behavior/vector.zig"); + _ = @import("behavior/void.zig"); + _ = @import("behavior/while.zig"); + _ = @import("behavior/widening.zig"); +} diff --git a/test/cases/align.zig b/test/stage1/behavior/align.zig similarity index 72% rename from test/cases/align.zig rename to test/stage1/behavior/align.zig index 3dff57feb8..dbd705ddc4 100644 --- a/test/cases/align.zig +++ b/test/stage1/behavior/align.zig @@ -1,13 +1,13 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == *align(4) u8); + expect(@typeOf(&foo).alignment == 4); + expect(@typeOf(&foo) == *align(4) u8); const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); + expect(@typeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -17,9 +17,9 @@ fn noop1() align(1) void {} 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); + expect(derp() == 1234); + expect(@typeOf(noop1) == fn () align(1) void); + expect(@typeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == *align(1) u32); + expect(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,17 +40,17 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@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" { - assert(*u32 == *align(@alignOf(u32)) u32); + expect(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnaligned(&a, &b) == 7); + expect(addUnaligned(&a, &b) == 7); } fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { @@ -60,7 +60,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; - assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); + expect(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; @@ -77,7 +77,7 @@ fn testBytesAlign(b: u8) void { b, }; const ptr = @ptrCast(*u32, &bytes[0]); - assert(ptr.* == 0x33333333); + expect(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { @@ -91,13 +91,13 @@ fn testBytesAlignSlice(b: u8) void { b, }; const slice: []u32 = @bytesToSlice(u32, bytes[0..]); - assert(slice[0] == 0x33333333); + expect(slice[0] == 0x33333333); } test "@alignCast pointers" { var x: u32 align(4) = 1; expectsOnly1(&x); - assert(x == 2); + expect(x == 2); } fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); @@ -113,7 +113,7 @@ test "@alignCast slices" { }; const slice = array[0..]; sliceExpectsOnly1(slice); - assert(slice[0] == 2); + expect(slice[0] == 2); } fn sliceExpectsOnly1(slice: []align(1) u32) void { sliceExpects4(@alignCast(4, slice)); @@ -128,7 +128,7 @@ test "implicitly decreasing fn alignment" { } fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { - assert(ptr() == answer); + expect(ptr() == answer); } fn alignedSmall() align(8) i32 { @@ -139,7 +139,7 @@ fn alignedBig() align(16) i32 { } test "@alignCast functions" { - assert(fnExpectsOnly1(simple4) == 0x19); + expect(fnExpectsOnly1(simple4) == 0x19); } fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); @@ -152,9 +152,9 @@ fn simple4() align(4) i32 { } test "generic function with align param" { - assert(whyWouldYouEverDoThis(1) == 0x1); - assert(whyWouldYouEverDoThis(4) == 0x1); - assert(whyWouldYouEverDoThis(8) == 0x1); + expect(whyWouldYouEverDoThis(1) == 0x1); + expect(whyWouldYouEverDoThis(4) == 0x1); + expect(whyWouldYouEverDoThis(8) == 0x1); } fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { @@ -164,28 +164,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); - assert(@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 }; - assert(@typeOf(&array[0]) == *align(4) u8); - assert(@typeOf(&array[1]) == *u8); - assert(@typeOf(&array[2]) == *align(2) u8); - assert(@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 }; - assert(@typeOf(&bigger[0]) == *align(2) u64); - assert(@typeOf(&bigger[1]) == *align(2) u64); - assert(@typeOf(&bigger[2]) == *align(2) u64); - assert(@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 assert(@typeOf(smaller[0..]) == []align(2) u32); - comptime assert(@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); @@ -198,14 +198,14 @@ 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 assert(@typeOf(&smaller[index]) == T); + comptime expect(@typeOf(&smaller[index]) == T); } fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime assert(@typeOf(&ptr[index]) == T); + comptime expect(@typeOf(&ptr[index]) == T); } test "alignstack" { - assert(fnWithAlignedStack() == 1234); + expect(fnWithAlignedStack() == 1234); } fn fnWithAlignedStack() i32 { @@ -214,7 +214,7 @@ fn fnWithAlignedStack() i32 { } test "alignment of structs" { - assert(@alignOf(struct { + expect(@alignOf(struct { a: i32, b: *i32, }) == @alignOf(usize)); diff --git a/test/cases/alignof.zig b/test/stage1/behavior/alignof.zig similarity index 65% rename from test/cases/alignof.zig rename to test/stage1/behavior/alignof.zig index 433e86e45e..d4d9661ead 100644 --- a/test/cases/alignof.zig +++ b/test/stage1/behavior/alignof.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -10,8 +10,9 @@ const Foo = struct { }; test "@alignOf(T) before referencing T" { - comptime assert(@alignOf(Foo) != maxInt(usize)); + comptime expect(@alignOf(Foo) != maxInt(usize)); if (builtin.arch == builtin.Arch.x86_64) { - comptime assert(@alignOf(Foo) == 4); + comptime expect(@alignOf(Foo) == 4); } } + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 0000000000..99b828e53c --- /dev/null +++ b/test/stage1/behavior/array.zig @@ -0,0 +1,270 @@ +const expect = @import("std").testing.expect; +const mem = @import("std").mem; + +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = u32(0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + expect(accumulator == 15); + expect(getArrayLen(array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} + +test "void arrays" { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + expect(@sizeOf(@typeOf(array)) == 0); + expect(array.len == 4); +} + +test "array literal" { + const hex_mult = []u16{ + 4096, + 256, + 16, + 1, + }; + + expect(hex_mult.len == 4); + expect(hex_mult[1] == 256); +} + +test "array dot len const expr" { + expect(comptime x: { + break :x some_array.len == 4; + }); +} + +const ArrayDotLenConstExpr = struct { + y: [some_array.len]u8, +}; +const some_array = []u8{ + 0, + 1, + 2, + 3, +}; + +test "nested arrays" { + const array_of_strings = [][]const u8{ + "hello", + "this", + "is", + "my", + "thing", + }; + for (array_of_strings) |s, i| { + if (i == 0) expect(mem.eql(u8, s, "hello")); + if (i == 1) expect(mem.eql(u8, s, "this")); + if (i == 2) expect(mem.eql(u8, s, "is")); + if (i == 3) expect(mem.eql(u8, s, "my")); + if (i == 4) expect(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { + b: u8, +}; +const Str = struct { + a: []Sub, +}; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + expect(s_array[0].b == 1); + expect(s_array[1].b == 2); + expect(s_array[2].b == 3); +} + +test "array literal with specified size" { + var array = [2]u8{ + 1, + 2, + }; + expect(array[0] == 1); + expect(array[1] == 2); +} + +test "array child property" { + var x: [5]i32 = undefined; + expect(@typeOf(x).Child == i32); +} + +test "array len property" { + var x: [5]i32 = undefined; + expect(@typeOf(x).len == 5); +} + +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + expect(arr.len == 4); + comptime expect(arr.len == 4); + expect(ptr.len == 4); + comptime expect(ptr.len == 4); +} + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + expect(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + expect(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evalutating function that takes array by value" { + const arr = []u8{ 0, 1 }; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + expect(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} + +test "array literal as argument to function" { + const S = struct { + fn entry(two: i32) void { + foo([]i32{ + 1, + 2, + 3, + }); + foo([]i32{ + 1, + two, + 3, + }); + foo2(true, []i32{ + 1, + 2, + 3, + }); + foo2(true, []i32{ + 1, + two, + 3, + }); + } + fn foo(x: []const i32) void { + expect(x[0] == 1); + expect(x[1] == 2); + expect(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) void { + expect(trash); + expect(x[0] == 1); + expect(x[1] == 2); + expect(x[2] == 3); + } + }; + S.entry(2); + comptime S.entry(2); +} + +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 }, + }, + }; + check(cases); + + const cases2 = [][]const i32{ + []i32{1}, + []i32{ two, 3 }, + }; + expect(cases2.len == 2); + expect(cases2[0].len == 1); + expect(cases2[0][0] == 1); + expect(cases2[1].len == 2); + expect(cases2[1][0] == 2); + expect(cases2[1][1] == 3); + + const cases3 = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ two, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases3); + } + + fn check(cases: []const []const []const i32) void { + expect(cases.len == 3); + expect(cases[0].len == 1); + expect(cases[0][0].len == 1); + expect(cases[0][0][0] == 1); + expect(cases[1].len == 1); + expect(cases[1][0].len == 2); + expect(cases[1][0][0] == 2); + expect(cases[1][0][1] == 3); + expect(cases[2].len == 2); + expect(cases[2][0].len == 1); + expect(cases[2][0][0] == 4); + expect(cases[2][1].len == 3); + expect(cases[2][1][0] == 5); + expect(cases[2][1][1] == 6); + expect(cases[2][1][2] == 7); + } + }; + S.entry(2); + comptime S.entry(2); +} diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig new file mode 100644 index 0000000000..845d80777a --- /dev/null +++ b/test/stage1/behavior/asm.zig @@ -0,0 +1,92 @@ +const config = @import("builtin"); +const expect = @import("std").testing.expect; + +comptime { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + asm volatile ( + \\.globl aoeu; + \\.type aoeu, @function; + \\.set aoeu, derp; + ); + } +} + +test "module level assembly" { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + expect(aoeu() == 1234); + } +} + +test "output constraint modifiers" { + // This is only testing compilation. + var a: u32 = 3; + asm volatile ("" + : [_] "=m,r" (a) + : + : "" + ); + asm volatile ("" + : [_] "=r,m" (a) + : + : "" + ); +} + +test "alternative constraints" { + // Make sure we allow commas as a separator for alternative constraints. + var a: u32 = 3; + asm volatile ("" + : [_] "=r,m" (a) + : [_] "r,m" (a) + : "" + ); +} + +test "sized integer/float in asm input" { + asm volatile ("" + : + : [_] "m" (usize(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i15(-3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f32(3.17)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f64(3.17)) + : "" + ); +} + +extern fn aoeu() i32; + +export fn derp() i32 { + return 1234; +} diff --git a/test/cases/atomics.zig b/test/stage1/behavior/atomics.zig similarity index 71% rename from test/cases/atomics.zig rename to test/stage1/behavior/atomics.zig index 67c9ab3dd1..daa463fd45 100644 --- a/test/cases/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -7,18 +7,18 @@ const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + expect(x1 == 1234); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + expect(x1 == 1234); } - assert(x == 5678); + expect(x == 5678); - assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == 42); + expect(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + expect(x == 42); } test "fence" { @@ -30,24 +30,24 @@ test "fence" { test "atomicrmw and atomicload" { var data: u8 = 200; testAtomicRmw(&data); - assert(data == 42); + expect(data == 42); testAtomicLoad(&data); } fn testAtomicRmw(ptr: *u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); - assert(prev_value == 200); + expect(prev_value == 200); comptime { var x: i32 = 1234; const y: i32 = 12345; - assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); - assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + expect(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + expect(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); } } fn testAtomicLoad(ptr: *u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); - assert(x == 42); + expect(x == 42); } test "cmpxchg with ptr" { @@ -56,16 +56,16 @@ test "cmpxchg with ptr" { var data3: i32 = 9101; var x: *i32 = &data1; if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + expect(x1 == &data1); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + expect(x1 == &data1); } - assert(x == &data3); + expect(x == &data3); - assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == &data2); + expect(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + expect(x == &data2); } diff --git a/test/cases/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig similarity index 92% rename from test/cases/bit_shifting.zig rename to test/stage1/behavior/bit_shifting.zig index 325e765bb0..610acc06c2 100644 --- a/test/cases/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type { - assert(Key == @IntType(false, Key.bit_count)); - assert(Key.bit_count >= mask_bit_count); + expect(Key == @IntType(false, Key.bit_count)); + expect(Key.bit_count >= mask_bit_count); const ShardKey = @IntType(false, mask_bit_count); const shift_amount = Key.bit_count - ShardKey.bit_count; return struct { @@ -77,12 +77,12 @@ fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, c var node_buffer: [node_count]Table.Node = undefined; for (node_buffer) |*node, i| { const key = @intCast(Key, i); - assert(table.get(key) == null); + expect(table.get(key) == null); node.init(key, {}); table.put(node); } for (node_buffer) |*node, i| { - assert(table.get(@intCast(Key, i)) == node); + expect(table.get(@intCast(Key, i)) == node); } } diff --git a/test/cases/bitcast.zig b/test/stage1/behavior/bitcast.zig similarity index 81% rename from test/cases/bitcast.zig rename to test/stage1/behavior/bitcast.zig index d85a84ed22..9607d2e3ef 100644 --- a/test/cases/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const maxInt = std.math.maxInt; test "@bitCast i32 -> u32" { @@ -8,8 +8,8 @@ test "@bitCast i32 -> u32" { } fn testBitCast_i32_u32() void { - assert(conv(-1) == maxInt(u32)); - assert(conv2(maxInt(u32)) == -1); + expect(conv(-1) == maxInt(u32)); + expect(conv2(maxInt(u32)) == -1); } fn conv(x: i32) u32 { @@ -27,11 +27,10 @@ test "@bitCast extern enum to its integer type" { fn testBitCastExternEnum() void { var SOCK_DGRAM = @This().B; var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assert(sock_dgram == 1); + expect(sock_dgram == 1); } }; SOCK.testBitCastExternEnum(); comptime SOCK.testBitCastExternEnum(); } - diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig new file mode 100644 index 0000000000..3897a3eab7 --- /dev/null +++ b/test/stage1/behavior/bitreverse.zig @@ -0,0 +1,81 @@ +const std = @import("std"); +const expect = std.testing.expect; +const minInt = std.math.minInt; + +test "@bitreverse" { + comptime testBitReverse(); + testBitReverse(); +} + +fn testBitReverse() void { + // using comptime_ints, unsigned + expect(@bitreverse(u0, 0) == 0); + expect(@bitreverse(u5, 0x12) == 0x9); + expect(@bitreverse(u8, 0x12) == 0x48); + expect(@bitreverse(u16, 0x1234) == 0x2c48); + expect(@bitreverse(u24, 0x123456) == 0x6a2c48); + expect(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); + expect(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); + expect(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); + expect(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); + expect(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); + expect(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using runtime uints, unsigned + var num0: u0 = 0; + expect(@bitreverse(u0, num0) == 0); + var num5: u5 = 0x12; + expect(@bitreverse(u5, num5) == 0x9); + var num8: u8 = 0x12; + expect(@bitreverse(u8, num8) == 0x48); + var num16: u16 = 0x1234; + expect(@bitreverse(u16, num16) == 0x2c48); + var num24: u24 = 0x123456; + expect(@bitreverse(u24, num24) == 0x6a2c48); + var num32: u32 = 0x12345678; + expect(@bitreverse(u32, num32) == 0x1e6a2c48); + var num40: u40 = 0x123456789a; + expect(@bitreverse(u40, num40) == 0x591e6a2c48); + var num48: u48 = 0x123456789abc; + expect(@bitreverse(u48, num48) == 0x3d591e6a2c48); + var num56: u56 = 0x123456789abcde; + expect(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); + var num64: u64 = 0x123456789abcdef1; + expect(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); + var num128: u128 = 0x123456789abcdef11121314151617181; + expect(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using comptime_ints, signed, positive + expect(@bitreverse(i0, 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))); + + // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. + var neg5: i5 = minInt(i5) + 1; + expect(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); + var neg8: i8 = -18; + expect(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); + var neg16: i16 = -32694; + expect(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); + var neg24: i24 = -6773785; + expect(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); + var neg32: i32 = -16773785; + expect(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); + var neg40: i40 = minInt(i40) + 12345; + expect(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); + var neg48: i48 = minInt(i48) + 12345; + expect(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); + var neg56: i56 = minInt(i56) + 12345; + expect(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); + var neg64: i64 = minInt(i64) + 12345; + expect(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); + var neg128: i128 = minInt(i128) + 12345; + expect(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); +} diff --git a/test/cases/bool.zig b/test/stage1/behavior/bool.zig similarity index 55% rename from test/cases/bool.zig rename to test/stage1/behavior/bool.zig index 3e4ac9c1cf..dfc2279005 100644 --- a/test/cases/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -1,25 +1,25 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "bool literals" { - assert(true); - assert(!false); + expect(true); + expect(!false); } test "cast bool to int" { const t = true; const f = false; - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + expect(@boolToInt(t) == u32(1)); + expect(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + expect(@boolToInt(t) == u32(1)); + expect(@boolToInt(f) == u32(0)); } test "bool cmp" { - assert(testBoolCmp(true, false) == false); + expect(testBoolCmp(true, false) == false); } fn testBoolCmp(a: bool, b: bool) bool { return a == b; @@ -30,6 +30,6 @@ const global_t = true; const not_global_f = !global_f; const not_global_t = !global_t; test "compile time bool not" { - assert(not_global_f); - assert(!not_global_t); + expect(not_global_f); + expect(!not_global_t); } diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig new file mode 100644 index 0000000000..beffa0f73a --- /dev/null +++ b/test/stage1/behavior/bswap.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "@bswap" { + comptime testByteSwap(); + testByteSwap(); +} + +fn testByteSwap() void { + expect(@bswap(u0, 0) == 0); + expect(@bswap(u8, 0x12) == 0x12); + expect(@bswap(u16, 0x1234) == 0x3412); + expect(@bswap(u24, 0x123456) == 0x563412); + expect(@bswap(u32, 0x12345678) == 0x78563412); + expect(@bswap(u40, 0x123456789a) == 0x9a78563412); + expect(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); + expect(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); + expect(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); + expect(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); + + expect(@bswap(i0, 0) == 0); + expect(@bswap(i8, -50) == -50); + expect(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); + expect(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); + expect(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); + expect(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); + expect(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); + expect(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); + expect(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); + expect(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == + @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); +} diff --git a/test/cases/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig similarity index 79% rename from test/cases/bugs/1076.zig rename to test/stage1/behavior/bugs/1076.zig index 7b84312310..9dc1d111ea 100644 --- a/test/cases/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -1,6 +1,6 @@ const std = @import("std"); const mem = std.mem; -const assert = std.debug.assert; +const expect = std.testing.expect; test "comptime code should not modify constant data" { testCastPtrOfArrayToSliceAndPtr(); @@ -11,6 +11,6 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + expect(mem.eql(u8, array[0..], "boeu")); } diff --git a/test/cases/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig similarity index 100% rename from test/cases/bugs/1111.zig rename to test/stage1/behavior/bugs/1111.zig diff --git a/test/cases/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig similarity index 82% rename from test/cases/bugs/1277.zig rename to test/stage1/behavior/bugs/1277.zig index a83e7653e2..3aa1db2ea0 100644 --- a/test/cases/bugs/1277.zig +++ b/test/stage1/behavior/bugs/1277.zig @@ -11,5 +11,5 @@ fn f() i32 { } test "don't emit an LLVM global for a const function when it's in an optional in a struct" { - std.debug.assertOrPanic(s.f.?() == 1234); + std.testing.expect(s.f.?() == 1234); } diff --git a/test/cases/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig similarity index 68% rename from test/cases/bugs/1322.zig rename to test/stage1/behavior/bugs/1322.zig index 2de92191ec..f1d61baa3a 100644 --- a/test/cases/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.debug.assert(@TagType(B)(a.b) == @TagType(B).c); + std.testing.expect(@TagType(B)(a.b) == @TagType(B).c); a = A{ .b = B.None }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); + std.testing.expect(@TagType(B)(a.b) == @TagType(B).None); } diff --git a/test/cases/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig similarity index 88% rename from test/cases/bugs/1381.zig rename to test/stage1/behavior/bugs/1381.zig index 2d452da156..91c994d7c0 100644 --- a/test/cases/bugs/1381.zig +++ b/test/stage1/behavior/bugs/1381.zig @@ -17,5 +17,5 @@ test "union that needs padding bytes inside an array" { }; const a = as[0].B; - std.debug.assertOrPanic(a.D == 1); + std.testing.expect(a.D == 1); } diff --git a/test/cases/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig similarity index 73% rename from test/cases/bugs/1421.zig rename to test/stage1/behavior/bugs/1421.zig index fcbb8b70e4..48cf1ae2a6 100644 --- a/test/cases/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const expect = std.testing.expect; const S = struct { fn method() builtin.TypeInfo { @@ -10,5 +10,5 @@ const S = struct { test "functions with return type required to be comptime are generic" { const ti = S.method(); - assert(builtin.TypeId(ti) == builtin.TypeId.Struct); + expect(builtin.TypeId(ti) == builtin.TypeId.Struct); } diff --git a/test/cases/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig similarity index 72% rename from test/cases/bugs/1442.zig rename to test/stage1/behavior/bugs/1442.zig index e9dfd5d2ce..d5ea3f66fe 100644 --- a/test/cases/bugs/1442.zig +++ b/test/stage1/behavior/bugs/1442.zig @@ -7,5 +7,5 @@ const Union = union(enum) { test "const error union field alignment" { var union_or_err: anyerror!Union = Union{ .Color = 1234 }; - std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); + std.testing.expect((union_or_err catch unreachable).Color == 1234); } diff --git a/test/cases/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig similarity index 56% rename from test/cases/bugs/1486.zig rename to test/stage1/behavior/bugs/1486.zig index 98fae36d3a..d1bb8d7053 100644 --- a/test/cases/bugs/1486.zig +++ b/test/stage1/behavior/bugs/1486.zig @@ -1,11 +1,11 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const ptr = &global; var global: u64 = 123; test "constant pointer to global variable causes runtime load" { global = 1234; - assert(&global == ptr); - assert(ptr.* == 1234); + expect(&global == ptr); + expect(ptr.* == 1234); } diff --git a/test/stage1/behavior/bugs/1851.zig b/test/stage1/behavior/bugs/1851.zig new file mode 100644 index 0000000000..ff9ab419f8 --- /dev/null +++ b/test/stage1/behavior/bugs/1851.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "allocation and looping over 3-byte integer" { + expect(@sizeOf(u24) == 4); + expect(@sizeOf([1]u24) == 4); + expect(@alignOf(u24) == 4); + expect(@alignOf([1]u24) == 4); + var buffer: [100]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buffer).allocator; + + var x = a.alloc(u24, 2) catch unreachable; + expect(x.len == 2); + x[0] = 0xFFFFFF; + x[1] = 0xFFFFFF; + + const bytes = @sliceToBytes(x); + expect(@typeOf(bytes) == []align(4) u8); + expect(bytes.len == 8); + + for (bytes) |*b| { + b.* = 0x00; + } + + expect(x[0] == 0x00); + expect(x[1] == 0x00); +} diff --git a/test/cases/bugs/394.zig b/test/stage1/behavior/bugs/394.zig similarity index 73% rename from test/cases/bugs/394.zig rename to test/stage1/behavior/bugs/394.zig index b0afec2357..b1f0b6b605 100644 --- a/test/cases/bugs/394.zig +++ b/test/stage1/behavior/bugs/394.zig @@ -7,12 +7,12 @@ const S = struct { y: E, }; -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "bug 394 fixed" { const x = S{ .x = 3, .y = E{ .B = 1 }, }; - assert(x.x == 3); + expect(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/stage1/behavior/bugs/655.zig similarity index 69% rename from test/cases/bugs/655.zig rename to test/stage1/behavior/bugs/655.zig index ebb8da0658..d4491bfc27 100644 --- a/test/cases/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -3,10 +3,10 @@ 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.debug.assert(@typeOf(&x) == *const other_file.Integer); + comptime std.testing.expect(@typeOf(&x) == *const other_file.Integer); foo(&x); } fn foo(x: *const other_file.Integer) void { - std.debug.assert(x.* == 1234); + std.testing.expect(x.* == 1234); } diff --git a/test/cases/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig similarity index 100% rename from test/cases/bugs/655_other_file.zig rename to test/stage1/behavior/bugs/655_other_file.zig diff --git a/test/cases/bugs/656.zig b/test/stage1/behavior/bugs/656.zig similarity index 86% rename from test/cases/bugs/656.zig rename to test/stage1/behavior/bugs/656.zig index f93f0ac4d5..159ec52d43 100644 --- a/test/cases/bugs/656.zig +++ b/test/stage1/behavior/bugs/656.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const PrefixOp = union(enum) { Return, @@ -22,7 +22,7 @@ fn foo(a: bool, b: bool) void { PrefixOp.AddrOf => |addr_of_info| { if (b) {} if (addr_of_info.align_expr) |align_expr| { - assert(align_expr == 1234); + expect(align_expr == 1234); } }, PrefixOp.Return => {}, diff --git a/test/cases/bugs/726.zig b/test/stage1/behavior/bugs/726.zig similarity index 75% rename from test/cases/bugs/726.zig rename to test/stage1/behavior/bugs/726.zig index 2acc91eb26..dd2a135b56 100644 --- a/test/cases/bugs/726.zig +++ b/test/stage1/behavior/bugs/726.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "@ptrCast from const to nullable" { const c: u8 = 4; var x: ?*const u8 = @ptrCast(?*const u8, &c); - assert(x.?.* == 4); + expect(x.?.* == 4); } test "@ptrCast from var in empty struct to nullable" { @@ -11,6 +11,6 @@ test "@ptrCast from var in empty struct to nullable" { var c: u8 = 4; }; var x: ?*const u8 = @ptrCast(?*const u8, &container.c); - assert(x.?.* == 4); + expect(x.?.* == 4); } diff --git a/test/cases/bugs/828.zig b/test/stage1/behavior/bugs/828.zig similarity index 100% rename from test/cases/bugs/828.zig rename to test/stage1/behavior/bugs/828.zig diff --git a/test/cases/bugs/920.zig b/test/stage1/behavior/bugs/920.zig similarity index 95% rename from test/cases/bugs/920.zig rename to test/stage1/behavior/bugs/920.zig index 2903f05a29..10c002f6ba 100644 --- a/test/cases/bugs/920.zig +++ b/test/stage1/behavior/bugs/920.zig @@ -60,6 +60,6 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + std.testing.expect(NormalDist1.f[i] == NormalDist.f[i]); } } diff --git a/test/cases/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig similarity index 70% rename from test/cases/byval_arg_var.zig rename to test/stage1/behavior/byval_arg_var.zig index 826b9cc9e5..3794a965c6 100644 --- a/test/cases/byval_arg_var.zig +++ b/test/stage1/behavior/byval_arg_var.zig @@ -2,11 +2,11 @@ const std = @import("std"); var result: []const u8 = "wrong"; -test "aoeu" { +test "pass string literal byvalue to a generic var param" { start(); blowUpStack(10); - std.debug.assert(std.mem.eql(u8, result, "string literal")); + std.testing.expect(std.mem.eql(u8, result, "string literal")); } fn start() void { diff --git a/test/cases/cancel.zig b/test/stage1/behavior/cancel.zig similarity index 86% rename from test/cases/cancel.zig rename to test/stage1/behavior/cancel.zig index c0f74fd34f..7fadf7f230 100644 --- a/test/cases/cancel.zig +++ b/test/stage1/behavior/cancel.zig @@ -10,9 +10,9 @@ test "cancel forwards" { const p = async<&da.allocator> f1() catch unreachable; cancel p; - std.debug.assert(defer_f1); - std.debug.assert(defer_f2); - std.debug.assert(defer_f3); + std.testing.expect(defer_f1); + std.testing.expect(defer_f2); + std.testing.expect(defer_f3); } async fn f1() void { @@ -47,10 +47,10 @@ test "cancel backwards" { const p = async<&da.allocator> b1() catch unreachable; cancel p; - std.debug.assert(defer_b1); - std.debug.assert(defer_b2); - std.debug.assert(defer_b3); - std.debug.assert(defer_b4); + std.testing.expect(defer_b1); + std.testing.expect(defer_b2); + std.testing.expect(defer_b3); + std.testing.expect(defer_b4); } async fn b1() void { diff --git a/test/cases/cast.zig b/test/stage1/behavior/cast.zig similarity index 70% rename from test/cases/cast.zig rename to test/stage1/behavior/cast.zig index bd45bbc00f..8ed03f4936 100644 --- a/test/cases/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const mem = std.mem; const maxInt = std.math.maxInt; @@ -7,12 +7,12 @@ test "int to ptr cast" { const x = usize(13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); - assert(z == 13); + expect(z == 13); } test "integer literal to pointer cast" { const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); + expect(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { @@ -20,7 +20,7 @@ test "pointer reinterpret const float to int" { const float_ptr = &float; const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; - assert(int_val == 858993411); + expect(int_val == 858993411); } test "implicitly cast indirect pointer to maybe-indirect pointer" { @@ -44,10 +44,10 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const p = &s; const q = &p; const r = &q; - assert(42 == S.constConst(q)); - assert(42 == S.maybeConstConst(q)); - assert(42 == S.constConstConst(r)); - assert(42 == S.maybeConstConstConst(r)); + expect(42 == S.constConst(q)); + expect(42 == S.maybeConstConst(q)); + expect(42 == S.constConstConst(r)); + expect(42 == S.maybeConstConstConst(r)); } test "explicit cast from integer to error type" { @@ -57,14 +57,14 @@ test "explicit cast from integer to error type" { fn testCastIntToErr(err: anyerror) void { const x = @errorToInt(err); const y = @intToError(x); - assert(error.ItBroke == y); + expect(error.ItBroke == y); } test "peer resolve arrays of different size to const slice" { - assert(mem.eql(u8, boolToStr(true), "true")); - assert(mem.eql(u8, boolToStr(false), "false")); - comptime assert(mem.eql(u8, boolToStr(true), "true")); - comptime assert(mem.eql(u8, boolToStr(false), "false")); + expect(mem.eql(u8, boolToStr(true), "true")); + expect(mem.eql(u8, boolToStr(false), "false")); + comptime expect(mem.eql(u8, boolToStr(true), "true")); + comptime expect(mem.eql(u8, boolToStr(false), "false")); } fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; @@ -77,28 +77,29 @@ test "peer resolve array and const slice" { fn testPeerResolveArrayConstSlice(b: bool) void { const value1 = if (b) "aoeu" else ([]const u8)("zz"); const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assert(mem.eql(u8, value1, "aoeu")); - assert(mem.eql(u8, value2, "zz")); + expect(mem.eql(u8, value1, "aoeu")); + expect(mem.eql(u8, value2, "zz")); } test "implicitly cast from T to anyerror!?T" { castToOptionalTypeError(1); comptime castToOptionalTypeError(1); } + const A = struct { a: i32, }; fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: anyerror!?i32 = x; - assert((try y).? == 1); + expect((try y).? == 1); const f = z; const g: anyerror!?i32 = f; const a = A{ .a = z }; const b: anyerror!?A = a; - assert((b catch unreachable).?.a == 1); + expect((b catch unreachable).?.a == 1); } test "implicitly cast from int to anyerror!?T" { @@ -113,7 +114,7 @@ fn implicitIntLitToOptional() void { test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); + expect((try a) == null and (try b) == null); } fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { const a: ?*A = null; @@ -124,11 +125,11 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + expect(peerTypeTAndOptionalT(true, false).? == 0); + expect(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + expect(peerTypeTAndOptionalT(true, false).? == 0); + expect(peerTypeTAndOptionalT(false, false).? == 3); } } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { @@ -140,11 +141,11 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); } } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { @@ -156,8 +157,8 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + expect(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime expect(mem.eql(u8, castToOptionalSlice().?, "hi")); } fn castToOptionalSlice() ?[]const u8 { @@ -170,7 +171,7 @@ test "implicitly cast from [0]T to anyerror![]T" { } fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); + expect((gimmeErrOrSlice() catch unreachable).len == 0); } fn gimmeErrOrSlice() anyerror![]u8 { @@ -181,14 +182,14 @@ test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { @@ -206,7 +207,7 @@ test "resolve undefined with integer" { fn testResolveUndefWithInt(b: bool, x: i32) void { const value = if (b) x else undefined; if (b) { - assert(value == x); + expect(value == x); } } @@ -218,17 +219,17 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); + expect(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); + 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 - //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + //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 { @@ -252,9 +253,9 @@ test "@floatToInt" { fn testFloatToInts() void { const x = i32(1e4); - assert(x == 10000); + expect(x == 10000); const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); + expect(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); expectFloatToInt(f16, -128.2, i8, -128); @@ -265,7 +266,7 @@ fn testFloatToInts() void { } fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); + expect(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -274,7 +275,7 @@ test "cast u128 to f128 and back" { } fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); + expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); } fn cast128Int(x: f128) u128 { @@ -294,9 +295,9 @@ test "const slice widen cast" { }; const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); + expect(u32_value == 0x12121212); - assert(@bitCast(u32, bytes) == 0x12121212); + expect(@bitCast(u32, bytes) == 0x12121212); } test "single-item pointer of array to slice and to unknown length pointer" { @@ -308,76 +309,76 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + expect(mem.eql(u8, array[0..], "boeu")); const y: []u8 = &array; y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); + 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 x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); + expect(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } test "@intCast comptime_int" { const result = @intCast(i32, 1234); - assert(@typeOf(result) == i32); - assert(result == 1234); + expect(@typeOf(result) == i32); + expect(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + expect(@typeOf(result) == f16); + expect(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + expect(@typeOf(result) == f16); + expect(result == 1234.0); } { const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + expect(@typeOf(result) == f32); + expect(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + expect(@typeOf(result) == f32); + expect(result == 1234.0); } } test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + expect(@typeOf(result) == f16); + expect(result == 1234.0); } { const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + expect(@typeOf(result) == f32); + expect(result == 1234.0); } } test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + comptime expect(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } test "@intCast i32 to u7" { var x: u128 = maxInt(u128); var y: i32 = 120; var z = x >> @intCast(u7, y); - assert(z == 0xff); + expect(z == 0xff); } test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); + expect(MakeType(void).getNull() == null); + expect(MakeType(void).getNonNull() != null); } fn MakeType(comptime T: type) type { @@ -397,16 +398,16 @@ test "implicit cast from *[N]T to ?[*]T" { var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + expect(std.mem.eql(u16, x.?[0..4], y[0..4])); x.?[0] = 8; y[3] = 6; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + expect(std.mem.eql(u16, x.?[0..4], y[0..4])); } test "implicit cast from *T to ?*c_void" { var a: u8 = 1; incrementVoidPtrValue(&a); - std.debug.assert(a == 2); + std.testing.expect(a == 2); } fn incrementVoidPtrValue(value: ?*c_void) void { @@ -416,7 +417,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); - assert(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 { @@ -440,27 +441,27 @@ pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); pub const PFN_void = extern fn (*c_void) void; fn foobar(func: PFN_void) void { - std.debug.assert(@ptrToInt(func) == maxInt(usize)); + std.testing.expect(@ptrToInt(func) == maxInt(usize)); } test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; var b: *u32 = @ptrCast(*u32, ptr); - assert(b.* == 1); + expect(b.* == 1); var ptr2: ?*c_void = &a; var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); + expect(c.* == 1); } test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); + expect(@intCast(comptime_int, 0) == 0); } test "implicit cast comptime numbers to any type when the value fits" { const a: u64 = 255; var b: u8 = a; - assert(b == 255); + expect(b == 255); } test "@intToEnum passed a comptime_int to an enum with one item" { @@ -468,5 +469,16 @@ test "@intToEnum passed a comptime_int to an enum with one item" { A, }; const x = @intToEnum(E, 0); - assert(x == E.A); + expect(x == E.A); +} + +test "@intCast to u0 and use the result" { + const S = struct { + fn doTheTest(zero: u1, one: u1, bigzero: i32) void { + expect((one << @intCast(u0, bigzero)) == 1); + expect((zero << @intCast(u0, bigzero)) == 0); + } + }; + S.doTheTest(0, 1, 0); + comptime S.doTheTest(0, 1, 0); } diff --git a/test/cases/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig similarity index 79% rename from test/cases/const_slice_child.zig rename to test/stage1/behavior/const_slice_child.zig index 07d02d5df0..57044d8aae 100644 --- a/test/cases/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -1,5 +1,6 @@ -const debug = @import("std").debug; -const assert = debug.assert; +const std = @import("std"); +const debug = std.debug; +const expect = std.testing.expect; var argv: [*]const [*]const u8 = undefined; @@ -15,10 +16,10 @@ test "const slice child" { } fn foo(args: [][]const u8) void { - assert(args.len == 3); - assert(streql(args[0], "one")); - assert(streql(args[1], "two")); - assert(streql(args[2], "three")); + expect(args.len == 3); + expect(streql(args[0], "one")); + expect(streql(args[1], "two")); + expect(streql(args[2], "three")); } fn bar(argc: usize) void { diff --git a/test/cases/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig similarity index 88% rename from test/cases/coroutine_await_struct.zig rename to test/stage1/behavior/coroutine_await_struct.zig index 79168715d8..29f77bf67c 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const expect = std.testing.expect; const Foo = struct { x: i32, @@ -18,8 +18,8 @@ test "coroutine await struct" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + expect(await_final_result.x == 1234); + expect(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/cases/coroutines.zig b/test/stage1/behavior/coroutines.zig similarity index 87% rename from test/cases/coroutines.zig rename to test/stage1/behavior/coroutines.zig index 89490ebc2c..be977bbfce 100644 --- a/test/cases/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const expect = std.testing.expect; var x: i32 = 1; @@ -9,9 +9,9 @@ test "create a coroutine and cancel it" { defer da.deinit(); const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); + comptime expect(@typeOf(p) == promise->void); cancel p; - assert(x == 2); + expect(x == 2); } async fn simpleAsyncFn() void { x += 1; @@ -31,7 +31,7 @@ test "coroutine suspend, resume, cancel" { cancel p; seq('g'); - assert(std.mem.eql(u8, points, "abcdefg")); + expect(std.mem.eql(u8, points, "abcdefg")); } async fn testAsyncSeq() void { defer seq('e'); @@ -53,9 +53,9 @@ test "coroutine suspend with block" { defer da.deinit(); const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); + std.testing.expect(!result); resume a_promise; - std.debug.assert(result); + std.testing.expect(result); cancel p; } @@ -63,13 +63,13 @@ var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@handle()) == promise->void); + comptime expect(@typeOf(@handle()) == promise->void); a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) //var our_handle: promise = @handle(); - assert( a_promise == @handle() ); + expect(a_promise == @handle()); result = true; } @@ -86,8 +86,8 @@ test "coroutine await" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + expect(await_final_result == 1234); + expect(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -123,8 +123,8 @@ test "coroutine await early return" { early_seq('a'); const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); - assert(early_final_result == 1234); - assert(std.mem.eql(u8, early_points, "abcdef")); + expect(early_final_result == 1234); + expect(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -170,7 +170,7 @@ test "async function with dot syntax" { defer da.deinit(); const p = try async<&da.allocator> S.foo(); cancel p; - assert(S.y == 2); + expect(S.y == 2); } test "async fn pointer in a struct field" { @@ -182,9 +182,9 @@ test "async fn pointer in a struct field" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); + expect(data == 2); cancel p; - assert(data == 4); + expect(data == 4); } async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; @@ -199,6 +199,7 @@ test "async fn with inferred error set" { resume p; cancel p; } + async fn failing() !void { suspend; return error.Fail; @@ -220,8 +221,7 @@ test "error return trace across suspend points - async return" { cancel p2; } -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { +fn nonFailing() (promise->anyerror!void) { return async suspendThenFail() catch unreachable; } async fn suspendThenFail() anyerror!void { @@ -230,9 +230,9 @@ async fn suspendThenFail() anyerror!void { } async fn printTrace(p: promise->(anyerror!void)) void { (await p) catch |e| { - std.debug.assert(e == error.Fail); + std.testing.expect(e == error.Fail); if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); + expect(trace.index == 1); } else switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, @@ -246,7 +246,7 @@ test "break from suspend" { var my_result: i32 = 1; const p = try async testBreakFromSuspend(&my_result); cancel p; - std.debug.assert(my_result == 2); + std.testing.expect(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { suspend { diff --git a/test/cases/defer.zig b/test/stage1/behavior/defer.zig similarity index 76% rename from test/cases/defer.zig rename to test/stage1/behavior/defer.zig index f9a2b69cd9..0bb9125e7c 100644 --- a/test/cases/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; var result: [3]u8 = undefined; var index: usize = undefined; @@ -21,18 +21,18 @@ fn runSomeErrorDefers(x: bool) !bool { } test "mixing normal and error defers" { - assert(runSomeErrorDefers(true) catch unreachable); - assert(result[0] == 'c'); - assert(result[1] == 'a'); + expect(runSomeErrorDefers(true) catch unreachable); + expect(result[0] == 'c'); + expect(result[1] == 'a'); const ok = runSomeErrorDefers(false) catch |err| x: { - assert(err == error.FalseNotAllowed); + expect(err == error.FalseNotAllowed); break :x true; }; - assert(ok); - assert(result[0] == 'c'); - assert(result[1] == 'b'); - assert(result[2] == 'a'); + expect(ok); + expect(result[0] == 'c'); + expect(result[1] == 'b'); + expect(result[2] == 'a'); } test "break and continue inside loop inside defer expression" { @@ -47,7 +47,7 @@ fn testBreakContInDefer(x: usize) void { if (i < 5) continue; if (i == 5) break; } - assert(i == 5); + expect(i == 5); } } @@ -59,11 +59,11 @@ test "defer and labeled break" { break :blk; } - assert(i == 1); + expect(i == 1); } test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| expect(e == error.Bad); } fn testNestedFnErrDefer() anyerror!void { diff --git a/test/cases/enum.zig b/test/stage1/behavior/enum.zig similarity index 86% rename from test/cases/enum.zig rename to test/stage1/behavior/enum.zig index 2dd552488c..899aeea67d 100644 --- a/test/cases/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const mem = @import("std").mem; test "enum type" { @@ -11,16 +11,16 @@ test "enum type" { }; const bar = Bar.B; - assert(bar == Bar.B); - assert(@memberCount(Foo) == 3); - assert(@memberCount(Bar) == 4); - assert(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assert(@sizeOf(Bar) == 1); + expect(bar == Bar.B); + expect(@memberCount(Foo) == 3); + expect(@memberCount(Bar) == 4); + expect(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + expect(@sizeOf(Bar) == 1); } test "enum as return value" { switch (returnAnInt(13)) { - Foo.One => |value| assert(value == 13), + Foo.One => |value| expect(value == 13), else => unreachable, } } @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); + expect(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + expect(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -110,8 +110,8 @@ const IntToEnumNumber = enum { }; test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) []const u8 { @@ -126,8 +126,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); + expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + expect(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } @@ -663,10 +663,10 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); + expect(@sizeOf(ValueCount1) == 0); + expect(@sizeOf(ValueCount2) == 1); + expect(@sizeOf(ValueCount256) == 1); + expect(@sizeOf(ValueCount257) == 2); } } @@ -685,12 +685,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@TagType(Small) == u2); + comptime expect(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@TagType(Small2) == u2); + comptime expect(@TagType(Small2) == u2); } } @@ -737,17 +737,17 @@ const bit_field_1 = BitFieldOfEnums{ test "bit field access with enum fields" { var data = bit_field_1; - assert(getA(&data) == A.Two); - assert(getB(&data) == B.Three3); - assert(getC(&data) == C.Four4); - comptime assert(@sizeOf(BitFieldOfEnums) == 1); + expect(getA(&data) == A.Two); + expect(getB(&data) == B.Three3); + expect(getC(&data) == C.Four4); + comptime expect(@sizeOf(BitFieldOfEnums) == 1); data.b = B.Four3; - assert(data.b == B.Four3); + expect(data.b == B.Four3); data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); + expect(data.a == A.Three); + expect(data.b == B.Four3); } fn getA(data: *const BitFieldOfEnums) A { @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); + expect(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,8 +784,8 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { + expect(@enumToInt(x) == 60); + expect(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, MultipleChoice.C => u32(1234), @@ -811,8 +811,8 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { + expect(@enumToInt(x) == 1000); + expect(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); + expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,14 +865,14 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); + expect(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2, }; - comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); + comptime @import("std").testing.expect(@sizeOf(Enum) == @sizeOf(u8)); } test "empty extern enum with members" { @@ -881,14 +881,14 @@ test "empty extern enum with members" { B, C, }; - assert(@sizeOf(E) == @sizeOf(c_int)); + expect(@sizeOf(E) == @sizeOf(c_int)); } -test "aoeu" { +test "tag name with assigned enum values" { const LocalFoo = enum { A = 1, B = 0, }; var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); + expect(mem.eql(u8, @tagName(b), "B")); } diff --git a/test/cases/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig similarity index 67% rename from test/cases/enum_with_members.zig rename to test/stage1/behavior/enum_with_members.zig index 088496bd2f..2e022a3427 100644 --- a/test/cases/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const mem = @import("std").mem; const fmt = @import("std").fmt; @@ -19,9 +19,9 @@ test "enum with members" { const b = ET{ .UINT = 42 }; var buf: [20]u8 = undefined; - assert((a.print(buf[0..]) catch unreachable) == 3); - assert(mem.eql(u8, buf[0..3], "-42")); + expect((a.print(buf[0..]) catch unreachable) == 3); + expect(mem.eql(u8, buf[0..3], "-42")); - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); + expect((b.print(buf[0..]) catch unreachable) == 2); + expect(mem.eql(u8, buf[0..2], "42")); } diff --git a/test/cases/error.zig b/test/stage1/behavior/error.zig similarity index 58% rename from test/cases/error.zig rename to test/stage1/behavior/error.zig index a731f39021..7d9dae7bdd 100644 --- a/test/cases/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; +const expectError = std.testing.expectError; const mem = std.mem; const builtin = @import("builtin"); @@ -18,7 +19,7 @@ pub fn baz() anyerror!i32 { } test "error wrapping" { - assert((baz() catch unreachable) == 15); + expect((baz() catch unreachable) == 15); } fn gimmeItBroke() []const u8 { @@ -26,14 +27,14 @@ fn gimmeItBroke() []const u8 { } test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); + expect(mem.eql(u8, @errorName(error.AnError), "AnError")); + expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } test "error values" { const a = @errorToInt(error.err1); const b = @errorToInt(error.err2); - assert(a != b); + expect(a != b); } test "redefinition of error values allowed" { @@ -46,8 +47,8 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); + expect(a == 3); + expect(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); @@ -55,7 +56,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); + expect(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; @@ -81,13 +82,13 @@ test "error union type " { fn testErrorUnionType() void { const x: anyerror!i32 = 1234; - if (x) |value| assert(value == 1234) else |_| unreachable; - assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assert(@typeOf(x).ErrorSet == anyerror); + 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); } -test "error set type " { +test "error set type" { testErrorSetType(); comptime testErrorSetType(); } @@ -98,12 +99,12 @@ const MyErrSet = error{ }; fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); + expect(@memberCount(MyErrSet) == 2); const a: MyErrSet!i32 = 5678; const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - if (a) |value| assert(value == 5678) else |err| switch (err) { + if (a) |value| expect(value == 5678) else |err| switch (err) { error.OutOfMemory => unreachable, error.FileNotFound => unreachable, } @@ -126,7 +127,7 @@ const Set2 = error{ fn testExplicitErrorSetCast(set1: Set1) void { var x = @errSetCast(Set2, set1); var y = @errSetCast(Set1, x); - assert(y == error.A); + expect(y == error.A); } test "comptime test error for empty error set" { @@ -137,12 +138,12 @@ test "comptime test error for empty error set" { const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); + if (x) |v| expect(v == 1234) else |err| @compileError("bad"); } test "syntax: optional operator in front of error union operator" { comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); + expect(?(anyerror!i32) == ?(anyerror!i32)); } } @@ -161,7 +162,6 @@ fn testErrToIntWithOnePossibleValue( test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); - comptime testErrorUnionPeerTypeResolution(1); } fn testErrorUnionPeerTypeResolution(x: i32) void { @@ -170,6 +170,11 @@ fn testErrorUnionPeerTypeResolution(x: i32) void { 2 => baz_1(), else => quux_1(), }; + if (y) |_| { + @panic("expected error"); + } else |e| { + expect(e == error.A); + } } fn bar_1() anyerror { @@ -243,3 +248,90 @@ fn intLiteral(str: []const u8) !?i64 { return error.T; } + +test "nested error union function call in optional unwrap" { + const S = struct { + const Foo = struct { + a: i32, + }; + + fn errorable() !i32 { + var x: Foo = (try getFoo()) orelse return error.Other; + return x.a; + } + + fn errorable2() !i32 { + var x: Foo = (try getFoo2()) orelse return error.Other; + return x.a; + } + + fn errorable3() !i32 { + var x: Foo = (try getFoo3()) orelse return error.Other; + return x.a; + } + + fn getFoo() anyerror!?Foo { + return Foo{ .a = 1234 }; + } + + fn getFoo2() anyerror!?Foo { + return error.Failure; + } + + fn getFoo3() anyerror!?Foo { + return null; + } + }; + expect((try S.errorable()) == 1234); + expectError(error.Failure, S.errorable2()); + expectError(error.Other, S.errorable3()); + comptime { + expect((try S.errorable()) == 1234); + expectError(error.Failure, S.errorable2()); + expectError(error.Other, S.errorable3()); + } +} + +test "widen cast integer payload of error union function call" { + const S = struct { + fn errorable() !u64 { + var x = u64(try number()); + return x; + } + + fn number() anyerror!u32 { + return 1234; + } + }; + expect((try S.errorable()) == 1234); +} + +test "return function call to error set from error union function" { + const S = struct { + fn errorable() anyerror!i32 { + return fail(); + } + + fn fail() anyerror { + return error.Failure; + } + }; + expectError(error.Failure, S.errorable()); + comptime expectError(error.Failure, S.errorable()); +} + +test "optional error set is the same size as error set" { + comptime expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); + const S = struct { + fn returnsOptErrSet() ?anyerror { + return null; + } + }; + expect(S.returnsOptErrSet() == null); + comptime expect(S.returnsOptErrSet() == null); +} + +test "debug info for optional error set" { + const SomeError = error{Hello}; + var a_local_variable: ?SomeError = null; +} diff --git a/test/cases/eval.zig b/test/stage1/behavior/eval.zig similarity index 73% rename from test/cases/eval.zig rename to test/stage1/behavior/eval.zig index a9eded151e..5976761f77 100644 --- a/test/cases/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); test "compile time recursion" { - assert(some_data.len == 21); + expect(some_data.len == 21); } var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { @@ -16,7 +16,7 @@ fn unwrapAndAddOne(blah: ?i32) i32 { } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { - assert(should_be_1235 == 1235); + expect(should_be_1235 == 1235); } test "inlined loop" { @@ -24,7 +24,7 @@ test "inlined loop" { comptime var sum = 0; inline while (i <= 5) : (i += 1) sum += i; - assert(sum == 15); + expect(sum == 15); } fn gimme1or2(comptime a: bool) i32 { @@ -34,12 +34,12 @@ fn gimme1or2(comptime a: bool) i32 { return z; } test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); + expect(gimme1or2(true) == 1); + expect(gimme1or2(false) == 2); } test "static function evaluation" { - assert(statically_added_number == 3); + expect(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) i32 { @@ -47,7 +47,8 @@ fn staticAdd(a: i32, b: i32) i32 { } test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + expect(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + comptime expect(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { @@ -63,10 +64,10 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { } test "statically initialized list" { - assert(static_point_list[0].x == 1); - assert(static_point_list[0].y == 2); - assert(static_point_list[1].x == 3); - assert(static_point_list[1].y == 4); + expect(static_point_list[0].x == 1); + expect(static_point_list[0].y == 2); + expect(static_point_list[1].x == 3); + expect(static_point_list[1].y == 4); } const Point = struct { x: i32, @@ -84,8 +85,8 @@ fn makePoint(x: i32, y: i32) Point { } test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); + expect(static_vec3.data[2] == 1.0); + expect(vec3(0.0, 0.0, 3.0).data[2] == 3.0); } const static_vec3 = vec3(0.0, 0.0, 1.0); pub const Vec3 = struct { @@ -101,12 +102,12 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); + expect(@sizeOf(@typeOf(array)) == 20); } const array_size: u8 = 20; test "constant struct with negation" { - assert(vertices[0].x == -0.6); + expect(vertices[0].x == -0.6); } const Vertex = struct { x: f32, @@ -141,7 +142,7 @@ const vertices = []Vertex{ test "statically initialized struct" { st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); + expect(st_init_str_foo.x == 14); } const StInitStrFoo = struct { x: i32, @@ -154,7 +155,7 @@ var st_init_str_foo = StInitStrFoo{ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); + expect(y[3] == 4); } const st_init_arr_lit_x = []u8{ 1, @@ -166,15 +167,15 @@ const st_init_arr_lit_x = []u8{ test "const slice" { comptime { const a = "1234567890"; - assert(a.len == 10); + expect(a.len == 10); const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); + expect(b.len == 1); + expect(b[0] == '2'); } } test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); + expect(testTryToTrickEvalWithRuntimeIf(true) == 10); } fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { @@ -200,16 +201,16 @@ fn letsTryToCompareBools(a: bool, b: bool) bool { return max(bool, a, b); } test "inlined block and runtime block phi" { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + expect(letsTryToCompareBools(true, true)); + expect(letsTryToCompareBools(true, false)); + expect(letsTryToCompareBools(false, true)); + expect(!letsTryToCompareBools(false, false)); comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + expect(letsTryToCompareBools(true, true)); + expect(letsTryToCompareBools(true, false)); + expect(letsTryToCompareBools(false, true)); + expect(!letsTryToCompareBools(false, false)); } } @@ -254,14 +255,14 @@ fn performFn(comptime prefix_char: u8, start_value: i32) i32 { } test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); + expect(performFn('t', 1) == 6); + expect(performFn('o', 0) == 1); + expect(performFn('w', 99) == 99); } test "eval @setRuntimeSafety at compile-time" { const result = comptime fnWithSetRuntimeSafety(); - assert(result == 1234); + expect(result == 1234); } fn fnWithSetRuntimeSafety() i32 { @@ -271,7 +272,7 @@ fn fnWithSetRuntimeSafety() i32 { test "eval @setFloatMode at compile-time" { const result = comptime fnWithFloatMode(); - assert(result == 1234.0); + expect(result == 1234.0); } fn fnWithFloatMode() f32 { @@ -292,15 +293,15 @@ var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; test "call method on bound fn referring to var instance" { - assert(bound_fn() == 1237); + expect(bound_fn() == 1237); } test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; modifySomeBytes(bytes[0..]); - assert(bytes[0] == 'a'); - assert(bytes[9] == 'b'); + expect(bytes[0] == 'a'); + expect(bytes[9] == 'b'); } } @@ -328,9 +329,9 @@ fn testCompTimeUIntComparisons(x: u32) void { } test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); + expect(foo_ref.name[0] == 'a'); foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); + expect(foo_ref.name[0] == 'b'); } const Foo = struct { @@ -341,8 +342,8 @@ var foo_contents = Foo{ .name = "a" }; const foo_ref = &foo_contents; test "create global array with for loop" { - assert(global_array[5] == 5 * 5); - assert(global_array[9] == 9 * 9); + expect(global_array[5] == 5 * 5); + expect(global_array[9] == 9 * 9); } const global_array = x: { @@ -357,7 +358,7 @@ test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; const byte = @intCast(u8, spartan_count); - assert(byte == 255); + expect(byte == 255); } } @@ -365,44 +366,45 @@ const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assert(&hi1[0] == &hi2[0]); + comptime expect(&hi1[0] == &hi2[0]); } fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); + expect(ptr1 == ptr2); } test "@setEvalBranchQuota" { comptime { - // 1001 for the loop and then 1 more for the assert fn call + // 1001 for the loop and then 1 more for the expect fn call @setEvalBranchQuota(1002); var i = 0; var sum = 0; while (i < 1001) : (i += 1) { sum += i; } - assert(sum == 500500); + expect(sum == 500500); } } // TODO test "float literal at compile time not lossy" { -// TODO assert(16777216.0 + 1.0 == 16777217.0); -// TODO assert(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO expect(16777216.0 + 1.0 == 16777217.0); +// TODO expect(9007199254740992.0 + 1.0 == 9007199254740993.0); // TODO } test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); + expect(f32(1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); + expect(f64(1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + expect(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } -// TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); +comptime { + expect(f128(1 << 113) == 10384593717069655257060992658440192); +} pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { @@ -413,15 +415,15 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { test "string literal used as comptime slice is memoized" { const a = "link"; const b = "link"; - comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); + comptime expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } test "comptime slice of undefined pointer of length 0" { const slice1 = ([*]i32)(undefined)[0..0]; - assert(slice1.len == 0); + expect(slice1.len == 0); const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); + expect(slice2.len == 0); } fn copyWithPartialInline(s: []u32, b: []u8) void { @@ -443,16 +445,16 @@ test "binary math operator in partially inlined function" { r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); - assert(s[0] == 0x1020304); - assert(s[1] == 0x5060708); - assert(s[2] == 0x90a0b0c); - assert(s[3] == 0xd0e0f10); + expect(s[0] == 0x1020304); + expect(s[1] == 0x5060708); + expect(s[2] == 0x90a0b0c); + expect(s[3] == 0xd0e0f10); } test "comptime function with the same args is memoized" { comptime { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); + expect(MakeType(i32) == MakeType(i32)); + expect(MakeType(i32) != MakeType(f64)); } } @@ -468,7 +470,7 @@ test "comptime function with mutable pointer is not memoized" { const ptr = &x; increment(ptr); increment(ptr); - assert(x == 3); + expect(x == 3); } } @@ -494,14 +496,14 @@ fn doesAlotT(comptime T: type, value: usize) T { } test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); + expect(doesAlotT(u32, 2) == 2); } test "comptime slice of slice preserves comptime var" { comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; - assert(buff[0..][0..][0] == 1); + expect(buff[0..][0..][0] == 1); } } @@ -510,7 +512,7 @@ test "comptime slice of pointer preserves comptime var" { var buff: [10]u8 = undefined; var a = buff[0..].ptr; a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); + expect(buff[0..][0..][0] == 1); } } @@ -524,9 +526,9 @@ const SingleFieldStruct = struct { test "const ptr to comptime mutable data is not memoized" { comptime { var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); + expect(foo.read_x() == 1); foo.x = 2; - assert(foo.read_x() == 2); + expect(foo.read_x() == 2); } } @@ -535,7 +537,7 @@ test "array concat of slices gives slice" { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); + expect(std.mem.eql(u8, c, "aoeuasdf")); } } @@ -552,14 +554,14 @@ test "comptime shlWithOverflow" { break :amt amt; }; - assert(ct_shifted == rt_shifted); + expect(ct_shifted == rt_shifted); } test "runtime 128 bit integer division" { var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; var c = a / b; - assert(c == 15231399999); + expect(c == 15231399999); } pub const Info = struct { @@ -572,20 +574,20 @@ test "comptime modification of const struct field" { comptime { var res = diamond_info; res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); + expect(diamond_info.version == 0); + expect(res.version == 1); } } test "pointer to type" { comptime { var T: type = i32; - assert(T == i32); + expect(T == i32); var ptr = &T; - assert(@typeOf(ptr) == *type); + expect(@typeOf(ptr) == *type); ptr.* = f32; - assert(T == f32); - assert(*T == *f32); + expect(T == f32); + expect(*T == *f32); } } @@ -594,17 +596,17 @@ test "slice of type" { var types_array = []type{ i32, f64, type }; for (types_array) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => expect(T == i32), + 1 => expect(T == f64), + 2 => expect(T == type), else => unreachable, } } for (types_array[0..]) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => expect(T == i32), + 1 => expect(T == f64), + 2 => expect(T == type), else => unreachable, } } @@ -621,7 +623,7 @@ fn wrap(comptime T: type) Wrapper { test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; - assert(ty == i32); + expect(ty == i32); } test "call method with comptime pass-by-non-copying-value self parameter" { @@ -635,12 +637,12 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const s = S{ .a = 2 }; var b = s.b(); - assert(b == 2); + expect(b == 2); } test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); + expect(std.mem.eql(u8, str, "Int")); } test "setting backward branch quota just before a generic fn call" { @@ -661,8 +663,8 @@ fn testVarInsideInlineLoop(args: ...) void { comptime var i = 0; inline while (i < args.len) : (i += 1) { const x = args[i]; - if (i == 0) assert(x); - if (i == 1) assert(x == 42); + if (i == 0) expect(x); + if (i == 1) expect(x == 42); } } @@ -672,7 +674,7 @@ test "inline for with same type but different values" { var a: T = undefined; res += a.len; } - assert(res == 5); + expect(res == 5); } test "refer to the type of a generic function" { @@ -686,19 +688,13 @@ fn doNothingWithType(comptime T: type) void {} test "zero extend from u0 to u1" { var zero_u0: u0 = 0; var zero_u1: u1 = zero_u0; - assert(zero_u1 == 0); + expect(zero_u1 == 0); } test "bit shift a u1" { var x: u1 = 1; var y = x << 0; - assert(y == 1); -} - -test "@intCast to a u0" { - var x: u8 = 0; - var y: u0 = @intCast(u0, x); - assert(y == 0); + expect(y == 1); } test "@bytesToslice on a packed struct" { @@ -708,7 +704,7 @@ test "@bytesToslice on a packed struct" { var b = [1]u8{9}; var f = @bytesToSlice(F, b); - assert(f[0].a == 9); + expect(f[0].a == 9); } test "comptime pointer cast array and then slice" { @@ -720,57 +716,57 @@ test "comptime pointer cast array and then slice" { const ptrB: [*]const u8 = &array; const sliceB: []const u8 = ptrB[0..2]; - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); + expect(sliceA[1] == 2); + expect(sliceB[1] == 2); } test "slice bounds in comptime concatenation" { const bs = comptime blk: { - const b = c"11"; - break :blk b[0..1]; + const b = c"........1........"; + break :blk b[8..9]; }; const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); + expect(str.len == 1); + expect(std.mem.eql(u8, str, "1")); const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); + expect(str2.len == 1); + expect(std.mem.eql(u8, str2, "1")); } test "comptime bitwise operators" { comptime { - assert(3 & 1 == 1); - assert(3 & -1 == 3); - assert(-3 & -1 == -3); - assert(3 | -1 == -1); - assert(-3 | -1 == -1); - assert(3 ^ -1 == -4); - assert(-3 ^ -1 == 2); - assert(~i8(-1) == 0); - assert(~i128(-1) == 0); - assert(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assert(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assert(~u128(0) == 0xffffffffffffffffffffffffffffffff); + expect(3 & 1 == 1); + expect(3 & -1 == 3); + expect(-3 & -1 == -3); + expect(3 | -1 == -1); + expect(-3 | -1 == -1); + expect(3 ^ -1 == -4); + expect(-3 ^ -1 == 2); + expect(~i8(-1) == 0); + expect(~i128(-1) == 0); + expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + expect(~u128(0) == 0xffffffffffffffffffffffffffffffff); } } test "*align(1) u16 is the same as *align(1:0:2) u16" { comptime { - assert(*align(1:0:2) u16 == *align(1) u16); + expect(*align(1:0:2) u16 == *align(1) u16); // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); + //expect(*align(:0:2) u16 == *u16); } } test "array concatenation forces comptime" { var a = oneItem(3) ++ oneItem(4); - assert(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); - assert(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/cases/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig similarity index 74% rename from test/cases/field_parent_ptr.zig rename to test/stage1/behavior/field_parent_ptr.zig index 00d4e0f367..6026a49d12 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "@fieldParentPtr non-first field" { testParentFieldPtr(&foo.c); @@ -25,17 +25,17 @@ const foo = Foo{ }; fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); + expect(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); + expect(base == &foo); + expect(&base.c == c); } fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); + expect(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); + expect(base == &foo); + expect(&base.a == a); } diff --git a/test/cases/fn.zig b/test/stage1/behavior/fn.zig similarity index 81% rename from test/cases/fn.zig rename to test/stage1/behavior/fn.zig index 8908bd7854..3f78c80290 100644 --- a/test/cases/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,7 +1,7 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "params" { - assert(testParamsAdd(22, 11) == 33); + expect(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) i32 { return a + b; @@ -21,32 +21,32 @@ test "void parameters" { fn voidFun(a: i32, b: void, c: i32, d: void) void { const v = b; const vv: void = if (a == 1) v else {}; - assert(a + c == 3); + expect(a + c == 3); return vv; } test "mutable local variables" { var zero: i32 = 0; - assert(zero == 0); + expect(zero == 0); var i = i32(0); while (i != 3) { i += 1; } - assert(i == 3); + expect(i == 3); } test "separate block scopes" { { const no_conflict: i32 = 5; - assert(no_conflict == 5); + expect(no_conflict == 5); } const c = x: { const no_conflict = i32(10); break :x no_conflict; }; - assert(c == 10); + expect(c == 10); } test "call function with empty string" { @@ -59,7 +59,7 @@ fn @"weird function name"() i32 { return 1234; } test "weird function name" { - assert(@"weird function name"() == 1234); + expect(@"weird function name"() == 1234); } test "implicit cast function unreachable return" { @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); + expect(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { @@ -97,7 +97,7 @@ fn fn4() u32 { } test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); + expect(@inlineCall(add, 3, 9) == 12); } fn add(a: i32, b: i32) i32 { @@ -110,7 +110,7 @@ test "number literal as an argument" { } fn numberLiteralArg(a: var) void { - assert(a == 3); + expect(a == 3); } test "assign inline fn to const variable" { @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); + expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -134,17 +134,17 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); + expect(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); } fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); + comptime expect(@typeOf(pt) == Point); return pt.x + pt.y; } test "pass by non-copying value as method" { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + expect(pt.addPointCoords() == 3); } const Point2 = struct { @@ -158,7 +158,7 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); + expect(pt.addPointCoords(i32) == 3); } const Point3 = struct { @@ -173,7 +173,7 @@ const Point3 = struct { test "pass by non-copying value as method, at comptime" { comptime { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + expect(pt.addPointCoords() == 3); } } @@ -189,7 +189,7 @@ fn outer(y: u32) fn (u32) u32 { test "return inner function which references comptime variable of outer function" { var func = outer(10); - assert(func(3) == 7); + expect(func(3) == 7); } test "extern struct with stdcallcc fn pointer" { @@ -203,5 +203,6 @@ test "extern struct with stdcallcc fn pointer" { var s: S = undefined; s.ptr = S.foo; - assert(s.ptr() == 1234); + expect(s.ptr() == 1234); } + diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig similarity index 76% rename from test/cases/fn_in_struct_in_comptime.zig rename to test/stage1/behavior/fn_in_struct_in_comptime.zig index fabb57e9cb..030693ac59 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; fn get_foo() fn (*u8) usize { comptime { @@ -13,5 +13,5 @@ fn get_foo() fn (*u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); + expect(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/for.zig b/test/stage1/behavior/for.zig similarity index 91% rename from test/cases/for.zig rename to test/stage1/behavior/for.zig index aecd8b9a07..b10dd14fa4 100644 --- a/test/cases/for.zig +++ b/test/stage1/behavior/for.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const mem = std.mem; test "continue in for loop" { @@ -26,7 +26,7 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); + expect(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) void { for (s) |*c| { @@ -68,7 +68,7 @@ test "basic for loop" { buf_index += 1; } - assert(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" { @@ -85,7 +85,7 @@ fn testBreakOuter() void { break :outer; } } - assert(count == 1); + expect(count == 1); } test "continue outer for loop" { @@ -102,5 +102,5 @@ fn testContinueOuter() void { continue :outer; } } - assert(counter == array.len); + expect(counter == array.len); } diff --git a/test/cases/generics.zig b/test/stage1/behavior/generics.zig similarity index 71% rename from test/cases/generics.zig rename to test/stage1/behavior/generics.zig index 52aa013989..637f9b88ae 100644 --- a/test/cases/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); + expect(max(i32, 3, -1) == 3); + expect(max(f32, 0.123, 0.456) == 0.456); + expect(add(2, 3) == 5); } fn max(comptime T: type, a: T, b: T) T { @@ -16,7 +16,7 @@ fn add(comptime a: i32, b: i32) i32 { const the_max = max(u32, 1234, 5678); test "compile time generic eval" { - assert(the_max == 5678); + expect(the_max == 5678); } fn gimmeTheBigOne(a: u32, b: u32) u32 { @@ -32,19 +32,19 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); + expect(gimmeTheBigOne(1234, 5678) == 5678); + expect(shouldCallSameInstance(34, 12) == 34); + expect(sameButWithFloats(0.43, 0.49) == 0.49); } test "var params" { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + expect(max_i32(12, 34) == 34); + expect(max_f64(1.2, 3.4) == 3.4); } comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + expect(max_i32(12, 34) == 34); + expect(max_f64(1.2, 3.4) == 3.4); } fn max_var(a: var, b: var) @typeOf(a + b) { @@ -76,8 +76,8 @@ test "function with return type type" { var list2: List(i32) = undefined; list.length = 10; list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); + expect(list.prealloc_items.len == 8); + expect(list2.prealloc_items.len == 8); } test "generic struct" { @@ -89,9 +89,9 @@ test "generic struct" { .value = true, .next = null, }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); + expect(a1.value == 13); + expect(a1.value == a1.getVal()); + expect(b1.getVal()); } fn GenNode(comptime T: type) type { return struct { @@ -104,7 +104,7 @@ fn GenNode(comptime T: type) type { } test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); + expect(GenericDataThing(3).count_plus_one == 4); } fn GenericDataThing(comptime count: isize) type { return struct { @@ -113,15 +113,15 @@ fn GenericDataThing(comptime count: isize) type { } test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); + expect(aGenericFn(i32, 3, 4) == 7); } fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ + expect(getFirstByte(u8, []u8{13}) == 13); + expect(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -146,6 +146,6 @@ fn foo2(arg: var) bool { } test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); + expect(foos[0](true)); + expect(!foos[1](true)); } diff --git a/test/cases/if.zig b/test/stage1/behavior/if.zig similarity index 87% rename from test/cases/if.zig rename to test/stage1/behavior/if.zig index 808936bfa5..a61b9dcfb4 100644 --- a/test/cases/if.zig +++ b/test/stage1/behavior/if.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "if statements" { shouldBeEqual(1, 1); @@ -24,7 +24,7 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } test "else if expression" { - assert(elseIfExpressionF(1) == 1); + expect(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) u8 { if (c == 0) { diff --git a/test/cases/import.zig b/test/stage1/behavior/import.zig similarity index 54% rename from test/cases/import.zig rename to test/stage1/behavior/import.zig index 6d6d4b0208..9a8c6848e2 100644 --- a/test/cases/import.zig +++ b/test/stage1/behavior/import.zig @@ -1,10 +1,10 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const a_namespace = @import("import/a_namespace.zig"); test "call fn via namespace lookup" { - assert(a_namespace.foo() == 1234); + expect(a_namespace.foo() == 1234); } test "importing the same thing gives the same import" { - assert(@import("std") == @import("std")); + expect(@import("std") == @import("std")); } diff --git a/test/cases/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig similarity index 100% rename from test/cases/import/a_namespace.zig rename to test/stage1/behavior/import/a_namespace.zig diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig similarity index 82% rename from test/cases/incomplete_struct_param_tld.zig rename to test/stage1/behavior/incomplete_struct_param_tld.zig index f1ac03a292..77a3dfd221 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const A = struct { b: B, @@ -26,5 +26,5 @@ test "incomplete struct param top level declaration" { .c = C{ .x = 13 }, }, }; - assert(foo(a) == 13); + expect(foo(a) == 13); } diff --git a/test/stage1/behavior/inttoptr.zig b/test/stage1/behavior/inttoptr.zig new file mode 100644 index 0000000000..b1780f93d6 --- /dev/null +++ b/test/stage1/behavior/inttoptr.zig @@ -0,0 +1,26 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const expect = std.testing.expect; + +test "casting random address to function pointer" { + randomAddressToFunction(); + comptime randomAddressToFunction(); +} + +fn randomAddressToFunction() void { + var addr: usize = 0xdeadbeef; + var ptr = @intToPtr(fn () void, addr); +} + +test "mutate through ptr initialized with constant intToPtr value" { + forceCompilerAnalyzeBranchHardCodedPtrDereference(false); +} + +fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { + const hardCodedP = @intToPtr(*volatile u8, 0xdeadbeef); + if (x) { + hardCodedP.* = hardCodedP.* | 10; + } else { + return; + } +} diff --git a/test/cases/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig similarity index 68% rename from test/cases/ir_block_deps.zig rename to test/stage1/behavior/ir_block_deps.zig index 5c1b18c00e..821079df79 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/stage1/behavior/ir_block_deps.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; fn foo(id: u64) !i32 { return switch (id) { @@ -16,6 +16,6 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); + expect((foo(1) catch unreachable) == 0); + expect((foo(2) catch unreachable) == 0); } diff --git a/test/cases/math.zig b/test/stage1/behavior/math.zig similarity index 62% rename from test/cases/math.zig rename to test/stage1/behavior/math.zig index 7d6b1bd9ac..36e81e11ed 100644 --- a/test/cases/math.zig +++ b/test/stage1/behavior/math.zig @@ -1,5 +1,7 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const expectEqualSlices = std.testing.expectEqualSlices; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -8,57 +10,57 @@ test "division" { comptime testDivision(); } fn testDivision() void { - assert(div(u32, 13, 3) == 4); - assert(div(f16, 1.0, 2.0) == 0.5); - assert(div(f32, 1.0, 2.0) == 0.5); + expect(div(u32, 13, 3) == 4); + expect(div(f16, 1.0, 2.0) == 0.5); + expect(div(f32, 1.0, 2.0) == 0.5); - assert(divExact(u32, 55, 11) == 5); - assert(divExact(i32, -55, 11) == -5); - assert(divExact(f16, 55.0, 11.0) == 5.0); - assert(divExact(f16, -55.0, 11.0) == -5.0); - assert(divExact(f32, 55.0, 11.0) == 5.0); - assert(divExact(f32, -55.0, 11.0) == -5.0); + expect(divExact(u32, 55, 11) == 5); + expect(divExact(i32, -55, 11) == -5); + expect(divExact(f16, 55.0, 11.0) == 5.0); + expect(divExact(f16, -55.0, 11.0) == -5.0); + expect(divExact(f32, 55.0, 11.0) == 5.0); + expect(divExact(f32, -55.0, 11.0) == -5.0); - assert(divFloor(i32, 5, 3) == 1); - assert(divFloor(i32, -5, 3) == -2); - assert(divFloor(f16, 5.0, 3.0) == 1.0); - assert(divFloor(f16, -5.0, 3.0) == -2.0); - assert(divFloor(f32, 5.0, 3.0) == 1.0); - assert(divFloor(f32, -5.0, 3.0) == -2.0); - assert(divFloor(i32, -0x80000000, -2) == 0x40000000); - assert(divFloor(i32, 0, -0x80000000) == 0); - assert(divFloor(i32, -0x40000001, 0x40000000) == -2); - assert(divFloor(i32, -0x80000000, 1) == -0x80000000); + expect(divFloor(i32, 5, 3) == 1); + expect(divFloor(i32, -5, 3) == -2); + expect(divFloor(f16, 5.0, 3.0) == 1.0); + expect(divFloor(f16, -5.0, 3.0) == -2.0); + expect(divFloor(f32, 5.0, 3.0) == 1.0); + expect(divFloor(f32, -5.0, 3.0) == -2.0); + expect(divFloor(i32, -0x80000000, -2) == 0x40000000); + expect(divFloor(i32, 0, -0x80000000) == 0); + expect(divFloor(i32, -0x40000001, 0x40000000) == -2); + expect(divFloor(i32, -0x80000000, 1) == -0x80000000); - assert(divTrunc(i32, 5, 3) == 1); - assert(divTrunc(i32, -5, 3) == -1); - assert(divTrunc(f16, 5.0, 3.0) == 1.0); - assert(divTrunc(f16, -5.0, 3.0) == -1.0); - assert(divTrunc(f32, 5.0, 3.0) == 1.0); - assert(divTrunc(f32, -5.0, 3.0) == -1.0); - assert(divTrunc(f64, 5.0, 3.0) == 1.0); - assert(divTrunc(f64, -5.0, 3.0) == -1.0); + expect(divTrunc(i32, 5, 3) == 1); + expect(divTrunc(i32, -5, 3) == -1); + expect(divTrunc(f16, 5.0, 3.0) == 1.0); + expect(divTrunc(f16, -5.0, 3.0) == -1.0); + expect(divTrunc(f32, 5.0, 3.0) == 1.0); + expect(divTrunc(f32, -5.0, 3.0) == -1.0); + expect(divTrunc(f64, 5.0, 3.0) == 1.0); + expect(divTrunc(f64, -5.0, 3.0) == -1.0); comptime { - assert( + expect( 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, ); - assert( + expect( @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, ); - assert( + expect( 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, ); - assert( + expect( @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, ); - assert( + expect( @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, ); - assert( + expect( @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, ); - assert( + expect( 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, ); } @@ -78,9 +80,9 @@ fn divTrunc(comptime T: type, a: T, b: T) T { test "@addWithOverflow" { var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); + expect(@addWithOverflow(u8, 250, 100, &result)); + expect(!@addWithOverflow(u8, 100, 150, &result)); + expect(result == 250); } // TODO test mulWithOverflow @@ -88,9 +90,9 @@ test "@addWithOverflow" { test "@shlWithOverflow" { var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); + expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + expect(result == 0b1011111111111100); } test "@clz" { @@ -99,11 +101,11 @@ test "@clz" { } fn testClz() void { - assert(clz(u8(0b00001010)) == 4); - assert(clz(u8(0b10001010)) == 0); - assert(clz(u8(0b00000000)) == 8); - assert(clz(u128(0xffffffffffffffff)) == 64); - assert(clz(u128(0x10000000000000000)) == 63); + expect(clz(u8(0b00001010)) == 4); + expect(clz(u8(0b10001010)) == 0); + expect(clz(u8(0b00000000)) == 8); + expect(clz(u128(0xffffffffffffffff)) == 64); + expect(clz(u128(0x10000000000000000)) == 63); } fn clz(x: var) usize { @@ -116,9 +118,9 @@ test "@ctz" { } fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); + expect(ctz(u8(0b10100000)) == 5); + expect(ctz(u8(0b10001010)) == 1); + expect(ctz(u8(0b00000000)) == 8); } fn ctz(x: var) usize { @@ -128,27 +130,27 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; i += 5; - assert(i == 5); + expect(i == 5); i -= 2; - assert(i == 3); + expect(i == 3); i *= 20; - assert(i == 60); + expect(i == 60); i /= 3; - assert(i == 20); + expect(i == 20); i %= 11; - assert(i == 9); + expect(i == 9); i <<= 1; - assert(i == 18); + expect(i == 18); i >>= 2; - assert(i == 4); + expect(i == 4); i = 6; i &= 5; - assert(i == 4); + expect(i == 4); i ^= 6; - assert(i == 2); + expect(i == 2); i = 6; i |= 3; - assert(i == 7); + expect(i == 7); } test "three expr in a row" { @@ -170,14 +172,14 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(i32(7) != --(i32(7))); } fn assertFalse(b: bool) void { - assert(!b); + expect(!b); } test "const number literal" { const one = 1; const eleven = ten + one; - assert(eleven == 11); + expect(eleven == 11); } const ten = 10; @@ -187,9 +189,9 @@ test "unsigned wrapping" { } fn testUnsignedWrappingEval(x: u32) void { const zero = x +% 1; - assert(zero == 0); + expect(zero == 0); const orig = zero -% 1; - assert(orig == maxInt(u32)); + expect(orig == maxInt(u32)); } test "signed wrapping" { @@ -198,9 +200,9 @@ test "signed wrapping" { } fn testSignedWrappingEval(x: i32) void { const min_val = x +% 1; - assert(min_val == minInt(i32)); + expect(min_val == minInt(i32)); const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); + expect(max_val == maxInt(i32)); } test "negation wrapping" { @@ -208,9 +210,9 @@ test "negation wrapping" { comptime testNegationWrappingEval(minInt(i16)); } fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); + expect(x == -32768); const neg = -%x; - assert(neg == -32768); + expect(neg == -32768); } test "unsigned 64-bit division" { @@ -219,8 +221,8 @@ test "unsigned 64-bit division" { } fn test_u64_div() void { const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); + expect(result.quotient == 33554432); + expect(result.remainder == 100663296); } fn divWithResult(a: u64, b: u64) DivResult { return DivResult{ @@ -234,36 +236,36 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: { + expect(comptime x: { break :x ~u16(0b1010101010101010) == 0b0101010101010101; }); - assert(comptime x: { + expect(comptime x: { break :x ~u64(2147483647) == 18446744071562067968; }); testBinaryNot(0b1010101010101010); } fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); + expect(~x == 0b0101010101010101); } test "small int addition" { var x: @IntType(false, 2) = 0; - assert(x == 0); + expect(x == 0); x += 1; - assert(x == 1); + expect(x == 1); x += 1; - assert(x == 2); + expect(x == 2); x += 1; - assert(x == 3); + expect(x == 3); var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); + expect(@addWithOverflow(@typeOf(x), x, 1, &result)); - assert(result == 0); + expect(result == 0); } test "float equality" { @@ -276,20 +278,20 @@ test "float equality" { fn testFloatEqualityImpl(x: f64, y: f64) void { const y2 = x + 1.0; - assert(y == y2); + expect(y == y2); } test "allow signed integer division/remainder when values are comptime known and positive or exact" { - assert(5 / 3 == 1); - assert(-5 / -3 == 1); - assert(-6 / 3 == -2); + expect(5 / 3 == 1); + expect(-5 / -3 == 1); + expect(-6 / 3 == -2); - assert(5 % 3 == 2); - assert(-6 % 3 == 0); + expect(5 % 3 == 2); + expect(-6 % 3 == 0); } test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); + comptime expect(0x1.0 == 1.0); } test "quad hex float literal parsing in range" { @@ -304,7 +306,7 @@ test "quad hex float literal parsing accurate" { // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); + expect(@bitCast(u128, a) == expected); } test "hex float literal within range" { @@ -319,7 +321,7 @@ test "truncating shift left" { } fn testShlTrunc(x: u16) void { const shifted = x << 1; - assert(shifted == 65534); + expect(shifted == 65534); } test "truncating shift right" { @@ -328,7 +330,7 @@ test "truncating shift right" { } fn testShrTrunc(x: u16) void { const shifted = x >> 1; - assert(shifted == 32767); + expect(shifted == 32767); } test "exact shift left" { @@ -337,7 +339,7 @@ test "exact shift left" { } fn testShlExact(x: u8) void { const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); + expect(shifted == 0b11010100); } test "exact shift right" { @@ -346,22 +348,22 @@ test "exact shift right" { } fn testShrExact(x: u8) void { const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); + expect(shifted == 0b00101101); } test "comptime_int addition" { comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "comptime_int multiplication" { comptime { - assert( + expect( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, ); - assert( + expect( 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, ); } @@ -369,7 +371,7 @@ test "comptime_int multiplication" { test "comptime_int shifting" { comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); + expect((u128(1) << 127) == 0x80000000000000000000000000000000); } } @@ -377,16 +379,16 @@ test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - assert(u32(a & 0xffffffff) == 0xaaaaaaab); + expect(u32(a & 0xffffffff) == 0xaaaaaaab); a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); + expect(u32(a & 0xffffffff) == 0xeeeeeeef); a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); + expect(u32(a & 0xffffffff) == 0xa0000001); a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); + expect(u32(a & 0xffffffff) == 0xefffffff); a >>= 32; - assert(a == 0); + expect(a == 0); } } @@ -394,7 +396,7 @@ test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; - assert(a == 0x1ffffffffeeee); + expect(a == 0x1ffffffffeeee); } } @@ -404,23 +406,23 @@ test "xor" { } fn test_xor() void { - assert(0xFF ^ 0x00 == 0xFF); - assert(0xF0 ^ 0x0F == 0xFF); - assert(0xFF ^ 0xF0 == 0x0F); - assert(0xFF ^ 0x0F == 0xF0); - assert(0xFF ^ 0xFF == 0x00); + expect(0xFF ^ 0x00 == 0xFF); + expect(0xF0 ^ 0x0F == 0xFF); + expect(0xFF ^ 0xF0 == 0x0F); + expect(0xFF ^ 0x0F == 0xF0); + expect(0xFF ^ 0xFF == 0x00); } test "comptime_int xor" { comptime { - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); - assert(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); - assert(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); + expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); + expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); + expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); + expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); + expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); } } @@ -434,23 +436,23 @@ fn make_f128(x: f128) f128 { } fn test_f128() void { - assert(@sizeOf(f128) == 16); - assert(make_f128(1.0) == 1.0); - assert(make_f128(1.0) != 1.1); - assert(make_f128(1.0) > 0.9); - assert(make_f128(1.0) >= 0.9); - assert(make_f128(1.0) >= 1.0); + expect(@sizeOf(f128) == 16); + expect(make_f128(1.0) == 1.0); + expect(make_f128(1.0) != 1.1); + expect(make_f128(1.0) > 0.9); + expect(make_f128(1.0) >= 0.9); + expect(make_f128(1.0) >= 1.0); should_not_be_zero(1.0); } fn should_not_be_zero(x: f128) void { - assert(x != 0.0); + expect(x != 0.0); } test "comptime float rem int" { comptime { var x = f32(1) % 2; - assert(x == 1.0); + expect(x == 1.0); } } @@ -465,8 +467,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); + expect(T(1) == T(1) % T(2)); + expect(T(1) == T(7) % T(3)); } test "@sqrt" { @@ -480,21 +482,36 @@ test "@sqrt" { const x = 14.0; const y = x * x; const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); + comptime expect(z == x); } fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); + expect(@sqrt(T, x * x) == x); } test "comptime_int param and return" { const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); + expect(a == 137114567242441932203689521744947848950); const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); + expect(b == 985095453608931032642182098849559179469148836107390954364380); } fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { return a + b; } + +test "vector integer addition" { + const S = struct { + fn doTheTest() void { + var a: @Vector(4, i32) = []i32{ 1, 2, 3, 4 }; + var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 }; + var result = a + b; + var result_array: [4]i32 = result; + const expected = []i32{ 6, 8, 10, 12 }; + expectEqualSlices(i32, &expected, &result_array); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/cases/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig similarity index 100% rename from test/cases/merge_error_sets.zig rename to test/stage1/behavior/merge_error_sets.zig diff --git a/test/cases/misc.zig b/test/stage1/behavior/misc.zig similarity index 64% rename from test/cases/misc.zig rename to test/stage1/behavior/misc.zig index 1a34d54e9e..91cab78bc7 100644 --- a/test/cases/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); @@ -26,38 +26,38 @@ test "call disabled extern fn" { } test "@IntType builtin" { - assert(@IntType(true, 8) == i8); - assert(@IntType(true, 16) == i16); - assert(@IntType(true, 32) == i32); - assert(@IntType(true, 64) == i64); + expect(@IntType(true, 8) == i8); + expect(@IntType(true, 16) == i16); + expect(@IntType(true, 32) == i32); + expect(@IntType(true, 64) == i64); - assert(@IntType(false, 8) == u8); - assert(@IntType(false, 16) == u16); - assert(@IntType(false, 32) == u32); - assert(@IntType(false, 64) == u64); + expect(@IntType(false, 8) == u8); + expect(@IntType(false, 16) == u16); + expect(@IntType(false, 32) == u32); + expect(@IntType(false, 64) == u64); - assert(i8.bit_count == 8); - assert(i16.bit_count == 16); - assert(i32.bit_count == 32); - assert(i64.bit_count == 64); + expect(i8.bit_count == 8); + expect(i16.bit_count == 16); + expect(i32.bit_count == 32); + expect(i64.bit_count == 64); - assert(i8.is_signed); - assert(i16.is_signed); - assert(i32.is_signed); - assert(i64.is_signed); - assert(isize.is_signed); + expect(i8.is_signed); + expect(i16.is_signed); + expect(i32.is_signed); + expect(i64.is_signed); + expect(isize.is_signed); - assert(!u8.is_signed); - assert(!u16.is_signed); - assert(!u32.is_signed); - assert(!u64.is_signed); - assert(!usize.is_signed); + expect(!u8.is_signed); + expect(!u16.is_signed); + expect(!u32.is_signed); + expect(!u64.is_signed); + expect(!usize.is_signed); } test "floating point primitive bit counts" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); + expect(f16.bit_count == 16); + expect(f32.bit_count == 32); + expect(f64.bit_count == 64); } test "short circuit" { @@ -72,7 +72,7 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_4 = f; if (t or x: { - assert(f); + expect(f); break :x f; }) { hit_1 = t; @@ -81,31 +81,31 @@ fn testShortCircuit(f: bool, t: bool) void { hit_2 = t; break :x f; }) { - assert(f); + expect(f); } if (t and x: { hit_3 = t; break :x f; }) { - assert(f); + expect(f); } if (f and x: { - assert(f); + expect(f); break :x f; }) { - assert(f); + expect(f); } else { hit_4 = t; } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); + expect(hit_1); + expect(hit_2); + expect(hit_3); + expect(hit_4); } test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); + expect(testTruncate(0x10fd) == 0xfd); } fn testTruncate(x: u32) u8 { return @truncate(u8, x); @@ -116,16 +116,16 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); + expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } const g1: i32 = 1233 + 1; var g2: i32 = 0; test "global variables" { - assert(g2 == 0); + expect(g2 == 0); g2 = g1; - assert(g2 == 1234); + expect(g2 == 1234); } test "memcpy and memset intrinsics" { @@ -142,7 +142,7 @@ test "builtin static eval" { const x: i32 = comptime x: { break :x 1 + 2 + 3; }; - assert(x == comptime 6); + expect(x == comptime 6); } test "slicing" { @@ -163,7 +163,7 @@ test "slicing" { test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: { + expect(comptime x: { break :x emptyFn == alias; }); } @@ -171,25 +171,25 @@ test "constant equal function pointers" { fn emptyFn() void {} test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); + expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); + expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); + expect(mem.eql(u8, "ab" ** 5, "ababababab")); } test "string escapes" { - assert(mem.eql(u8, "\"", "\x22")); - assert(mem.eql(u8, "\'", "\x27")); - assert(mem.eql(u8, "\n", "\x0a")); - assert(mem.eql(u8, "\r", "\x0d")); - assert(mem.eql(u8, "\t", "\x09")); - assert(mem.eql(u8, "\\", "\x5c")); - assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); + expect(mem.eql(u8, "\"", "\x22")); + expect(mem.eql(u8, "\'", "\x27")); + expect(mem.eql(u8, "\n", "\x0a")); + expect(mem.eql(u8, "\r", "\x0d")); + expect(mem.eql(u8, "\t", "\x09")); + expect(mem.eql(u8, "\\", "\x5c")); + expect(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); } test "multiline string" { @@ -199,7 +199,7 @@ test "multiline string" { \\three ; const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); + expect(mem.eql(u8, s1, s2)); } test "multiline C string" { @@ -209,11 +209,11 @@ test "multiline C string" { c\\three ; const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); + expect(cstr.cmp(s1, s2) == 0); } test "type equality" { - assert(*const u8 != *u8); + expect(*const u8 != *u8); } const global_a: i32 = 1234; @@ -221,7 +221,7 @@ const global_b: *const i32 = &global_a; const global_c: *const f32 = @ptrCast(*const f32, global_b); test "compile time global reinterpret" { const d = @ptrCast(*const i32, global_c); - assert(d.* == 1234); + expect(d.* == 1234); } test "explicit cast maybe pointers" { @@ -247,8 +247,8 @@ test "cast undefined" { fn testCastUndefined(x: []const u8) void {} test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); + expect(castSmallUnsignedToLargerSigned1(200) == i16(200)); + expect(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; @@ -258,7 +258,7 @@ fn castSmallUnsignedToLargerSigned2(x: u16) i64 { } test "implicit cast after unreachable" { - assert(outer() == 1234); + expect(outer() == 1234); } fn inner() i32 { return 1234; @@ -273,13 +273,13 @@ test "pointer dereferencing" { y.* += 1; - assert(x == 4); - assert(y.* == 4); + expect(x == 4); + expect(y.* == 4); } test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); + expect(mem.eql(u8, f2(true), "a")); + expect(mem.eql(u8, f2(false), "b")); } fn f2(x: bool) []const u8 { return (if (x) fA else fB)(); @@ -321,8 +321,8 @@ const test3_bar = Test3Foo{ .Two = 13 }; fn test3_1(f: Test3Foo) void { switch (f) { Test3Foo.Three => |pt| { - assert(pt.x == 3); - assert(pt.y == 4); + expect(pt.x == 3); + expect(pt.y == 4); }, else => unreachable, } @@ -330,14 +330,14 @@ fn test3_1(f: Test3Foo) void { fn test3_2(f: Test3Foo) void { switch (f) { Test3Foo.Two => |x| { - assert(x == 13); + expect(x == 13); }, else => unreachable, } } test "character literals" { - assert('\'' == single_quote); + expect('\'' == single_quote); } const single_quote = '\''; @@ -346,13 +346,13 @@ test "take address of parameter" { } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(f_ptr.* == 12.34); + expect(f_ptr.* == 12.34); } test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; - assert(ptrEql(b, b)); + expect(ptrEql(b, b)); } fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; @@ -367,36 +367,31 @@ test "C string concatenation" { { var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); + expect(a[i] == b[i]); } } - assert(a[len] == 0); - assert(b[len] == 0); + expect(a[len] == 0); + expect(b[len] == 0); } test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; + expect(@sizeOf(i32) == 4); + var big_thing_array = []i32{ 1, 2, 3, 4 }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = @sliceToBytes(big_thing_slice); - assert(bytes.len == 4 * 4); + expect(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; - assert(big_thing_slice[1] == 0); + expect(big_thing_slice[1] == 0); const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); + expect(big_thing_again[2] == 3); big_thing_again[2] = -1; - assert(bytes[8] == maxInt(u8)); - assert(bytes[9] == maxInt(u8)); - assert(bytes[10] == maxInt(u8)); - assert(bytes[11] == maxInt(u8)); + expect(bytes[8] == maxInt(u8)); + expect(bytes[9] == maxInt(u8)); + expect(bytes[10] == maxInt(u8)); + expect(bytes[11] == maxInt(u8)); } test "pointer to void return type" { @@ -413,7 +408,7 @@ fn testPointerToVoidReturnType2() *const void { test "non const ptr to aliased type" { const int = i32; - assert(?*int == ?*i32); + expect(?*int == ?*i32); } test "array 2D const double ptr" { @@ -426,8 +421,8 @@ test "array 2D const double ptr" { fn testArray2DConstDoublePtr(ptr: *const f32) void { const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); + expect(ptr2[0] == 1.0); + expect(ptr2[1] == 2.0); } const Tid = builtin.TypeId; @@ -449,32 +444,32 @@ const AUnion = union { test "@typeId" { comptime { - assert(@typeId(type) == Tid.Type); - assert(@typeId(void) == Tid.Void); - assert(@typeId(bool) == Tid.Bool); - assert(@typeId(noreturn) == Tid.NoReturn); - assert(@typeId(i8) == Tid.Int); - assert(@typeId(u8) == Tid.Int); - assert(@typeId(i64) == Tid.Int); - assert(@typeId(u64) == Tid.Int); - assert(@typeId(f32) == Tid.Float); - assert(@typeId(f64) == Tid.Float); - assert(@typeId(*f32) == Tid.Pointer); - assert(@typeId([2]u8) == Tid.Array); - assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.Undefined); - assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Optional); - assert(@typeId(anyerror!i32) == Tid.ErrorUnion); - assert(@typeId(anyerror) == Tid.ErrorSet); - assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assert(@typeId(AUnionEnum) == Tid.Union); - assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn () void) == Tid.Fn); - assert(@typeId(@typeOf(builtin)) == Tid.Namespace); + expect(@typeId(type) == Tid.Type); + expect(@typeId(void) == Tid.Void); + expect(@typeId(bool) == Tid.Bool); + expect(@typeId(noreturn) == Tid.NoReturn); + expect(@typeId(i8) == Tid.Int); + expect(@typeId(u8) == Tid.Int); + expect(@typeId(i64) == Tid.Int); + expect(@typeId(u64) == Tid.Int); + expect(@typeId(f32) == Tid.Float); + expect(@typeId(f64) == Tid.Float); + 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(?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(AUnionEnum) == Tid.Union); + expect(@typeId(AUnion) == Tid.Union); + expect(@typeId(fn () void) == Tid.Fn); + expect(@typeId(@typeOf(builtin)) == Tid.Namespace); // TODO bound fn // TODO arg tuple // TODO opaque @@ -490,13 +485,13 @@ test "@typeName" { Unused, }; comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); + expect(mem.eql(u8, @typeName(i64), "i64")); + expect(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 - assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assert(mem.eql(u8, @typeName(Struct), "Struct")); - assert(mem.eql(u8, @typeName(Union), "Union")); - assert(mem.eql(u8, @typeName(Enum), "Enum")); + expect(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + expect(mem.eql(u8, @typeName(Struct), "Struct")); + expect(mem.eql(u8, @typeName(Union), "Union")); + expect(mem.eql(u8, @typeName(Enum), "Enum")); } } @@ -504,28 +499,16 @@ fn TypeFromFn(comptime T: type) type { return struct {}; } -test "volatile load and store" { - var number: i32 = 1234; - const ptr = (*volatile i32)(&number); - ptr.* += 1; - assert(ptr.* == 1235); +test "double implicit cast in same expression" { + var x = i32(u16(nine())); + expect(x == 9); } - -test "slice string literal has type []const u8" { - comptime { - assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{ - 1, - 2, - 3, - 4, - }; - assert(@typeOf(array[0..]) == []const i32); - } +fn nine() u8 { + return 9; } test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); + expect(global_ptr == &gdt[0]); } const GDTEntry = struct { field: i32, @@ -543,16 +526,12 @@ export fn writeToVRam() void { vram[0] = 'X'; } -test "pointer child field" { - assert((*u32).Child == u32); -} - const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(*OpaqueA != *OpaqueB); - assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); + expect(*OpaqueA != *OpaqueB); + expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { @@ -581,25 +560,6 @@ test "implicit comptime while" { } } -test "struct inside function" { - testStructInFn(); - comptime testStructInFn(); -} - -fn testStructInFn() void { - const BlockKind = u32; - - const Block = struct { - kind: BlockKind, - }; - - var block = Block{ .kind = 1234 }; - - block.kind += 1; - - assert(block.kind == 1235); -} - fn fnThatClosesOverLocalConst() type { const c = 1; return struct { @@ -611,7 +571,7 @@ fn fnThatClosesOverLocalConst() type { test "function closes over local const" { const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); + expect(x == 1); } test "cold function" { @@ -648,21 +608,21 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: Pack test "slicing zero length array" { const s1 = ""[0..]; const s2 = ([]u32{})[0..]; - assert(s1.len == 0); - assert(s2.len == 0); - assert(mem.eql(u8, s1, "")); - assert(mem.eql(u32, s2, []u32{})); + expect(s1.len == 0); + expect(s2.len == 0); + expect(mem.eql(u8, s1, "")); + expect(mem.eql(u32, s2, []u32{})); } const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); - comptime assert(addr1 == addr2); + comptime expect(addr1 == addr2); } test "equality compare fn ptrs" { var a = emptyFn; - assert(a == a); + expect(a == a); } test "self reference through fn ptr field" { @@ -677,5 +637,59 @@ test "self reference through fn ptr field" { }; var a: S.A = undefined; a.f = S.foo; - assert(a.f(a) == 12); + expect(a.f(a) == 12); +} + +test "volatile load and store" { + var number: i32 = 1234; + const ptr = (*volatile i32)(&number); + ptr.* += 1; + expect(ptr.* == 1235); +} + +test "slice string literal has type []const u8" { + comptime { + expect(@typeOf("aoeu"[0..]) == []const u8); + const array = []i32{ 1, 2, 3, 4 }; + expect(@typeOf(array[0..]) == []const i32); + } +} + +test "pointer child field" { + expect((*u32).Child == u32); +} + +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block{ .kind = 1234 }; + + block.kind += 1; + + expect(block.kind == 1235); +} + +test "fn call returning scalar optional in equality expression" { + expect(getNull() == null); +} + +fn getNull() ?*i32 { + return null; +} + +test "thread local variable" { + const S = struct { + threadlocal var t: i32 = 1234; + }; + S.t += 1; + expect(S.t == 1235); } diff --git a/test/cases/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/a.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/a.zig diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/b.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/b.zig diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig similarity index 66% rename from test/cases/namespace_depends_on_compile_var/index.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/index.zig index ccc49d9367..32feaced10 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -1,11 +1,11 @@ const builtin = @import("builtin"); -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "namespace depends on compile var" { if (some_namespace.a_bool) { - assert(some_namespace.a_bool); + expect(some_namespace.a_bool); } else { - assert(!some_namespace.a_bool); + expect(!some_namespace.a_bool); } } const some_namespace = switch (builtin.os) { diff --git a/test/cases/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig similarity index 77% rename from test/cases/new_stack_call.zig rename to test/stage1/behavior/new_stack_call.zig index 5912550d54..1e01a5a8a2 100644 --- a/test/cases/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; var new_stack_bytes: [1024]u8 = undefined; @@ -10,17 +10,17 @@ test "calling a function with a new stack" { const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); _ = targetFunction(arg); - assert(arg == 1234); - assert(a < b); + expect(arg == 1234); + expect(a < b); } fn targetFunction(x: i32) usize { - assert(x == 1234); + expect(x == 1234); var local_variable: i32 = 42; const ptr = &local_variable; ptr.* += 1; - assert(local_variable == 43); + expect(local_variable == 43); return @ptrToInt(ptr); } diff --git a/test/cases/null.zig b/test/stage1/behavior/null.zig similarity index 83% rename from test/cases/null.zig rename to test/stage1/behavior/null.zig index 825db88b1e..8c9b86b260 100644 --- a/test/cases/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "optional type" { const x: ?bool = true; @@ -17,13 +17,13 @@ test "optional type" { const z = next_x orelse 1234; - assert(z == 1234); + expect(z == 1234); const final_x: ?i32 = 13; const num = final_x orelse unreachable; - assert(num == 13); + expect(num == 13); } test "test maybe object and get a pointer to the inner value" { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(maybe_bool.? == false); + expect(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(foo(1235).?); + expect(foo(1235).?); if (foo(null) != null) unreachable; - assert(!foo(1234).?); + expect(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ + expect(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -84,10 +84,10 @@ const Particle = struct { test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; - assert(is_null); + expect(is_null); const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); + expect(!is_non_null); } const SillyStruct = struct { context: ?i32, @@ -98,8 +98,8 @@ test "test null runtime" { testTestNullRuntime(null); } fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); + expect(x == null); + expect(!(x != null)); } test "optional void" { @@ -108,8 +108,8 @@ test "optional void" { } fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); + expect(bar(null) == null); + expect(bar({}) != null); } fn bar(x: ?void) ?void { @@ -133,7 +133,7 @@ test "unwrap optional which is field of global var" { } struct_with_optional.field = 1234; if (struct_with_optional.field) |payload| { - assert(payload == 1234); + expect(payload == 1234); } else { unreachable; } @@ -141,13 +141,13 @@ test "unwrap optional which is field of global var" { test "null with default unwrap" { const x: i32 = null orelse 1; - assert(x == 1); + expect(x == 1); } test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); + expect(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } @@ -158,5 +158,5 @@ const StructWithOptionalType = struct { test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; var x: ?*EmptyStruct = null; - assert(x == null); + expect(x == null); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 0000000000..a65bed020c --- /dev/null +++ b/test/stage1/behavior/optional.zig @@ -0,0 +1,81 @@ +const expect = @import("std").testing.expect; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + expect(o != null); +} + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + expect(x == y); + y = &number; + expect(x != y); + expect(x != &number); + expect(&number != x); + x = &number; + expect(x == y); + expect(x == &number); + expect(&number == x); +} + +test "address of unwrap optional" { + const S = struct { + const Foo = struct { + a: i32, + }; + + var global: ?Foo = null; + + pub fn getFoo() anyerror!*Foo { + return &global.?; + } + }; + S.global = S.Foo{ .a = 1234 }; + const foo = S.getFoo() catch unreachable; + expect(foo.a == 1234); +} + +test "passing an optional integer as a parameter" { + const S = struct { + fn entry() bool { + var x: i32 = 1234; + return foo(x); + } + + fn foo(x: ?i32) bool { + return x.? == 1234; + } + }; + expect(S.entry()); + comptime expect(S.entry()); +} + +test "unwrap function call with optional pointer return value" { + const S = struct { + fn entry() void { + expect(foo().?.* == 1234); + expect(bar() == null); + } + const global: i32 = 1234; + fn foo() ?*const i32 { + return &global; + } + fn bar() ?*i32 { + return null; + } + }; + S.entry(); + // TODO https://github.com/ziglang/zig/issues/1901 + //comptime S.entry(); +} diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig new file mode 100644 index 0000000000..eed7a765d7 --- /dev/null +++ b/test/stage1/behavior/pointers.zig @@ -0,0 +1,126 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectError = std.testing.expectError; + +test "dereference pointer" { + comptime testDerefPtr(); + testDerefPtr(); +} + +fn testDerefPtr() void { + var x: i32 = 1234; + var y = &x; + y.* += 1; + expect(x == 1235); +} + +test "pointer arithmetic" { + var ptr = c"abcd"; + + expect(ptr[0] == 'a'); + ptr += 1; + expect(ptr[0] == 'b'); + ptr += 1; + expect(ptr[0] == 'c'); + ptr += 1; + expect(ptr[0] == 'd'); + ptr += 1; + expect(ptr[0] == 0); + ptr -= 1; + expect(ptr[0] == 'd'); + ptr -= 1; + expect(ptr[0] == 'c'); + ptr -= 1; + expect(ptr[0] == 'b'); + ptr -= 1; + expect(ptr[0] == 'a'); +} + +test "double pointer parsing" { + comptime expect(PtrOf(PtrOf(i32)) == **i32); +} + +fn PtrOf(comptime T: type) type { + return *T; +} + +test "assigning integer to C pointer" { + var x: i32 = 0; + var ptr: [*c]u8 = 0; + var ptr2: [*c]u8 = x; +} + +test "implicit cast single item pointer to C pointer and back" { + var y: u8 = 11; + var x: [*c]u8 = &y; + var z: *u8 = x; + z.* += 1; + expect(y == 12); +} + +test "C pointer comparison and arithmetic" { + const S = struct { + fn doTheTest() void { + var one: usize = 1; + var ptr1: [*c]u32 = 0; + var ptr2 = ptr1 + 10; + expect(ptr1 == 0); + expect(ptr1 >= 0); + expect(ptr1 <= 0); + expect(ptr1 < 1); + expect(ptr1 < one); + expect(1 > ptr1); + expect(one > ptr1); + expect(ptr1 < ptr2); + expect(ptr2 > ptr1); + expect(ptr2 >= 40); + expect(ptr2 == 40); + expect(ptr2 <= 40); + ptr2 -= 10; + expect(ptr1 == ptr2); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "peer type resolution with C pointers" { + var ptr_one: *u8 = undefined; + var ptr_many: [*]u8 = undefined; + var ptr_c: [*c]u8 = undefined; + var t = true; + var x1 = if (t) ptr_one else ptr_c; + 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); +} + +test "implicit casting between C pointer and optional non-C pointer" { + var slice: []const u8 = "aoeu"; + const opt_many_ptr: ?[*]const u8 = slice.ptr; + var ptr_opt_many_ptr = &opt_many_ptr; + var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; + expect(c_ptr.*.* == 'a'); + ptr_opt_many_ptr = c_ptr; + expect(ptr_opt_many_ptr.*.?[1] == 'o'); +} + +test "implicit cast error unions with non-optional to optional pointer" { + const S = struct { + fn doTheTest() void { + expectError(error.Fail, foo()); + } + fn foo() anyerror!?*u8 { + return bar() orelse error.Fail; + } + fn bar() ?*u8 { + return null; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/cases/popcount.zig b/test/stage1/behavior/popcount.zig similarity index 55% rename from test/cases/popcount.zig rename to test/stage1/behavior/popcount.zig index 7dc7f28c0e..2b63284720 100644 --- a/test/cases/popcount.zig +++ b/test/stage1/behavior/popcount.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "@popCount" { comptime testPopCount(); @@ -8,17 +8,18 @@ test "@popCount" { fn testPopCount() void { { var x: u32 = 0xaa; - assert(@popCount(x) == 4); + expect(@popCount(x) == 4); } { var x: u32 = 0xaaaaaaaa; - assert(@popCount(x) == 16); + expect(@popCount(x) == 16); } { var x: i16 = -1; - assert(@popCount(x) == 16); + expect(@popCount(x) == 16); } comptime { - assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + expect(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); } } + diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig new file mode 100644 index 0000000000..3787382aea --- /dev/null +++ b/test/stage1/behavior/ptrcast.zig @@ -0,0 +1,52 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const expect = std.testing.expect; + +test "reinterpret bytes as integer with nonzero offset" { + testReinterpretBytesAsInteger(); + comptime testReinterpretBytesAsInteger(); +} + +fn testReinterpretBytesAsInteger() void { + const bytes = "\x12\x34\x56\x78\xab"; + const expected = switch (builtin.endian) { + builtin.Endian.Little => 0xab785634, + builtin.Endian.Big => 0x345678ab, + }; + expect(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); +} + +test "reinterpret bytes of an array into an extern struct" { + testReinterpretBytesAsExternStruct(); + comptime testReinterpretBytesAsExternStruct(); +} + +fn testReinterpretBytesAsExternStruct() void { + var bytes align(2) = []u8{ 1, 2, 3, 4, 5, 6 }; + + const S = extern struct { + a: u8, + b: u16, + c: u8, + }; + + var ptr = @ptrCast(*const S, &bytes); + var val = ptr.c; + expect(val == 5); +} + +test "reinterpret struct field at comptime" { + const numLittle = comptime Bytes.init(0x12345678); + expect(std.mem.eql(u8, []u8{ 0x78, 0x56, 0x34, 0x12 }, numLittle.bytes)); +} + +const Bytes = struct { + bytes: [4]u8, + + pub fn init(v: u32) Bytes { + var res: Bytes = undefined; + @ptrCast(*align(1) u32, &res.bytes).* = v; + + return res; + } +}; diff --git a/test/cases/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig similarity index 59% rename from test/cases/pub_enum/index.zig rename to test/stage1/behavior/pub_enum/index.zig index 7fdd07b8a3..15e4114c2d 100644 --- a/test/cases/pub_enum/index.zig +++ b/test/stage1/behavior/pub_enum/index.zig @@ -1,13 +1,13 @@ const other = @import("other.zig"); -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "pub enum" { pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); + expect(foo == other.APubEnum.Two); } test "cast with imported symbol" { - assert(other.size_t(42) == 42); + expect(other.size_t(42) == 42); } diff --git a/test/cases/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig similarity index 100% rename from test/cases/pub_enum/other.zig rename to test/stage1/behavior/pub_enum/other.zig diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig similarity index 82% rename from test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig rename to test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index 3c94bb0d49..2c1cf06268 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -1,14 +1,14 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const mem = @import("std").mem; var ok: bool = false; test "reference a variable in an if after an if in the 2nd switch prong" { foo(true, Num.Two, false, "aoeu"); - assert(!ok); + expect(!ok); foo(false, Num.One, false, "aoeu"); - assert(!ok); + expect(!ok); foo(true, Num.One, false, "aoeu"); - assert(ok); + expect(ok); } const Num = enum { @@ -32,6 +32,6 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { } fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); + expect(mem.eql(u8, x, "aoeu")); ok = true; } diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig new file mode 100644 index 0000000000..55efc85b97 --- /dev/null +++ b/test/stage1/behavior/reflection.zig @@ -0,0 +1,96 @@ +const expect = @import("std").testing.expect; +const mem = @import("std").mem; +const reflection = @This(); + +test "reflection: array, pointer, optional, error union type child" { + comptime { + expect(([10]u8).Child == u8); + expect((*u8).Child == u8); + expect((anyerror!u8).Payload == u8); + expect((?u8).Child == u8); + } +} + +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); + } +} + +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} +fn dummy_varargs(args: ...) void {} + +test "reflection: struct member types and names" { + comptime { + expect(@memberCount(Foo) == 3); + + expect(@memberType(Foo, 0) == i32); + expect(@memberType(Foo, 1) == bool); + expect(@memberType(Foo, 2) == void); + + expect(mem.eql(u8, @memberName(Foo, 0), "one")); + expect(mem.eql(u8, @memberName(Foo, 1), "two")); + expect(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + expect(@memberCount(Bar) == 4); + + expect(@memberType(Bar, 0) == void); + expect(@memberType(Bar, 1) == i32); + expect(@memberType(Bar, 2) == bool); + expect(@memberType(Bar, 3) == f64); + + expect(mem.eql(u8, @memberName(Bar, 0), "One")); + expect(mem.eql(u8, @memberName(Bar, 1), "Two")); + expect(mem.eql(u8, @memberName(Bar, 2), "Three")); + expect(mem.eql(u8, @memberName(Bar, 3), "Four")); + } +} + +test "reflection: @field" { + var f = Foo{ + .one = 42, + .two = true, + .three = void{}, + }; + + expect(f.one == f.one); + expect(@field(f, "o" ++ "ne") == f.one); + expect(@field(f, "t" ++ "wo") == f.two); + expect(@field(f, "th" ++ "ree") == f.three); + expect(@field(Foo, "const" ++ "ant") == Foo.constant); + expect(@field(Bar, "O" ++ "ne") == Bar.One); + expect(@field(Bar, "T" ++ "wo") == Bar.Two); + expect(@field(Bar, "Th" ++ "ree") == Bar.Three); + expect(@field(Bar, "F" ++ "our") == Bar.Four); + expect(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + expect(f.one == 4); +} + +const Foo = struct { + const constant = 52; + + one: i32, + two: bool, + three: void, +}; + +const Bar = union(enum) { + One: void, + Two: i32, + Three: bool, + Four: f64, +}; + diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig new file mode 100644 index 0000000000..58a6c81759 --- /dev/null +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const expect = @import("std").testing.expect; + +test "@sizeOf and @typeOf" { + const y: @typeOf(x) = 120; + expect(@sizeOf(@typeOf(y)) == 2); +} +const x: u16 = 13; +const z: @typeOf(x) = 19; + +const A = struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +const P = packed struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +test "@byteOffsetOf" { + // Packed structs have fixed memory layout + expect(@byteOffsetOf(P, "a") == 0); + expect(@byteOffsetOf(P, "b") == 1); + expect(@byteOffsetOf(P, "c") == 5); + expect(@byteOffsetOf(P, "d") == 6); + expect(@byteOffsetOf(P, "e") == 6); + expect(@byteOffsetOf(P, "f") == 7); + expect(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); +} + +test "@bitOffsetOf" { + // Packed structs have fixed memory layout + expect(@bitOffsetOf(P, "a") == 0); + expect(@bitOffsetOf(P, "b") == 8); + expect(@bitOffsetOf(P, "c") == 40); + expect(@bitOffsetOf(P, "d") == 48); + expect(@bitOffsetOf(P, "e") == 51); + expect(@bitOffsetOf(P, "f") == 56); + expect(@bitOffsetOf(P, "g") == 72); + + expect(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + expect(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + expect(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + expect(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + expect(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + expect(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + expect(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/cases/slice.zig b/test/stage1/behavior/slice.zig similarity index 67% rename from test/cases/slice.zig rename to test/stage1/behavior/slice.zig index b4b43bdd19..13fa84d0fa 100644 --- a/test/cases/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,20 +1,20 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const mem = @import("std").mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); + expect(@ptrToInt(x.ptr) == 0x1000); + expect(x.len == 0x500); - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); + expect(@ptrToInt(y.ptr) == 0x1100); + expect(y.len == 0x400); } test "slice child property" { var array: [5]i32 = undefined; var slice = array[0..]; - assert(@typeOf(slice).Child == i32); + expect(@typeOf(slice).Child == i32); } test "runtime safety lets us slice from len..len" { @@ -23,7 +23,7 @@ test "runtime safety lets us slice from len..len" { 2, 3, }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); + expect(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { @@ -36,5 +36,5 @@ test "implicitly cast array of size 0 to slice" { } fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); + expect(msg.len == 0); } diff --git a/test/cases/struct.zig b/test/stage1/behavior/struct.zig similarity index 69% rename from test/cases/struct.zig rename to test/stage1/behavior/struct.zig index bbbd21912c..a045f482a2 100644 --- a/test/cases/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -12,7 +12,7 @@ const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); - assert(result == 7); + expect(result == 7); } test "return empty struct instance" { @@ -24,8 +24,8 @@ fn returnEmptyStructInstance() StructWithNoFields { const should_be_11 = StructWithNoFields.add(5, 6); -test "invake static method in global scope" { - assert(should_be_11 == 11); +test "invoke static method in global scope" { + expect(should_be_11 == 11); } test "void struct fields" { @@ -34,8 +34,8 @@ test "void struct fields" { .b = 1, .c = void{}, }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); + expect(foo.b == 1); + expect(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { a: void, @@ -50,7 +50,7 @@ test "structs" { foo.b = foo.a == 1; testFoo(foo); testMutation(&foo); - assert(foo.c == 100); + expect(foo.c == 100); } const StructFoo = struct { a: i32, @@ -58,7 +58,7 @@ const StructFoo = struct { c: f32, }; fn testFoo(foo: StructFoo) void { - assert(foo.b); + expect(foo.b); } fn testMutation(foo: *StructFoo) void { foo.c = 100; @@ -83,7 +83,7 @@ test "struct point to self" { root.next = &node; - assert(node.next.next.next.val.x == 1); + expect(node.next.next.next.val.x == 1); } test "struct byval assign" { @@ -92,18 +92,18 @@ test "struct byval assign" { foo1.a = 1234; foo2.a = 0; - assert(foo2.a == 0); + expect(foo2.a == 0); foo2 = foo1; - assert(foo2.a == 1234); + expect(foo2.a == 1234); } fn structInitializer() void { const val = Val{ .x = 42 }; - assert(val.x == 42); + expect(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); + expect(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -122,7 +122,7 @@ test "store member function in variable" { const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); - assert(result == 1234); + expect(result == 1234); } const MemberFnTestFoo = struct { x: i32, @@ -134,12 +134,12 @@ const MemberFnTestFoo = struct { test "call member function directly" { const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); - assert(result == 1234); + expect(result == 1234); } test "member functions" { const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); + expect(r.getSeed() == 1234); } const MemberFnRand = struct { seed: u32, @@ -150,7 +150,7 @@ const MemberFnRand = struct { test "return struct byval from function" { const bar = makeBar(1234, 5678); - assert(bar.y == 5678); + expect(bar.y == 5678); } const Bar = struct { x: i32, @@ -165,7 +165,7 @@ fn makeBar(x: i32, y: i32) Bar { test "empty struct method call" { const es = EmptyStruct{}; - assert(es.method() == 1234); + expect(es.method() == 1234); } const EmptyStruct = struct { fn method(es: *const EmptyStruct) i32 { @@ -182,7 +182,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); + expect(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -200,7 +200,7 @@ test "packed struct" { }; foo.y += 1; const four = foo.x + foo.y; - assert(four == 4); + expect(four == 4); } const BitField1 = packed struct { @@ -217,17 +217,17 @@ const bit_field_1 = BitField1{ test "bit field access" { var data = bit_field_1; - assert(getA(&data) == 1); - assert(getB(&data) == 2); - assert(getC(&data) == 3); - comptime assert(@sizeOf(BitField1) == 1); + expect(getA(&data) == 1); + expect(getB(&data) == 2); + expect(getC(&data) == 3); + comptime expect(@sizeOf(BitField1) == 1); data.b += 1; - assert(data.b == 3); + expect(data.b == 3); data.a += 1; - assert(data.a == 2); - assert(data.b == 3); + expect(data.a == 2); + expect(data.b == 3); } fn getA(data: *const BitField1) u3 { @@ -254,8 +254,8 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); + expect(@sizeOf(Foo24Bits) == 3); + expect(@sizeOf(Foo96Bits) == 12); } var value = Foo96Bits{ @@ -265,28 +265,28 @@ test "packed struct 24bits" { .d = 0, }; value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); + expect(value.a == 1); + expect(value.b == 0); + expect(value.c == 0); + expect(value.d == 0); value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); + expect(value.a == 1); + expect(value.b == 1); + expect(value.c == 0); + expect(value.d == 0); value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); + expect(value.a == 1); + expect(value.b == 1); + expect(value.c == 1); + expect(value.d == 0); value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); + expect(value.a == 1); + expect(value.b == 1); + expect(value.c == 1); + expect(value.d == 1); } const FooArray24Bits = packed struct { @@ -297,43 +297,43 @@ const FooArray24Bits = packed struct { test "packed array 24bits" { comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + expect(@sizeOf([9]Foo24Bits) == 9 * 3); + expect(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + expect(ptr.a == 0); + expect(ptr.b[0].field == 0); + expect(ptr.b[1].field == 0); + expect(ptr.c == 0); ptr.a = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + expect(ptr.a == maxInt(u16)); + expect(ptr.b[0].field == 0); + expect(ptr.b[1].field == 0); + expect(ptr.c == 0); ptr.b[0].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + expect(ptr.a == maxInt(u16)); + expect(ptr.b[0].field == maxInt(u24)); + expect(ptr.b[1].field == 0); + expect(ptr.c == 0); ptr.b[1].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); + expect(ptr.a == maxInt(u16)); + expect(ptr.b[0].field == maxInt(u24)); + expect(ptr.b[1].field == maxInt(u24)); + expect(ptr.c == 0); ptr.c = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == maxInt(u16)); + expect(ptr.a == maxInt(u16)); + expect(ptr.b[0].field == maxInt(u24)); + expect(ptr.b[1].field == maxInt(u24)); + expect(ptr.c == maxInt(u16)); - assert(bytes[bytes.len - 1] == 0xaa); + expect(bytes[bytes.len - 1] == 0xaa); } const FooStructAligned = packed struct { @@ -347,17 +347,17 @@ const FooArrayOfAligned = packed struct { test "aligned array of packed struct" { comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); + expect(@sizeOf(FooStructAligned) == 2); + expect(@sizeOf(FooArrayOfAligned) == 2 * 2); } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - assert(ptr.a[0].a == 0xbb); - assert(ptr.a[0].b == 0xbb); - assert(ptr.a[1].a == 0xbb); - assert(ptr.a[1].b == 0xbb); + expect(ptr.a[0].a == 0xbb); + expect(ptr.a[0].b == 0xbb); + expect(ptr.a[1].a == 0xbb); + expect(ptr.a[1].b == 0xbb); } test "runtime struct initialization of bitfield" { @@ -370,10 +370,10 @@ test "runtime struct initialization of bitfield" { .y = @intCast(u4, x2), }; - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.y == @intCast(u4, x2)); + expect(s1.x == x1); + expect(s1.y == x1); + expect(s2.x == @intCast(u4, x2)); + expect(s2.y == @intCast(u4, x2)); } var x1 = u4(1); @@ -400,18 +400,18 @@ test "native bit field understands endianness" { @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - assert(bitfields.f1 == 0x1111); - assert(bitfields.f2 == 0x2222); - assert(bitfields.f3 == 0x33); - assert(bitfields.f4 == 0x44); - assert(bitfields.f5 == 0x5); - assert(bitfields.f6 == 0x6); - assert(bitfields.f7 == 0x77); + expect(bitfields.f1 == 0x1111); + expect(bitfields.f2 == 0x2222); + expect(bitfields.f3 == 0x33); + expect(bitfields.f4 == 0x44); + expect(bitfields.f5 == 0x5); + expect(bitfields.f6 == 0x6); + expect(bitfields.f7 == 0x77); } test "align 1 field before self referential align 8 field as slice return type" { const result = alloc(Expr); - assert(result.len == 0); + expect(result.len == 0); } const Expr = union(enum) { @@ -434,10 +434,10 @@ test "call method with mutable reference to struct with no fields" { }; var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); + expect(S.doC(&s)); + expect(s.doC()); + expect(S.do(&s)); + expect(s.do()); } test "implicit cast packed struct field to const ptr" { @@ -453,7 +453,7 @@ test "implicit cast packed struct field to const ptr" { var lup: LevelUpMove = undefined; lup.level = 12; const res = LevelUpMove.toInt(lup.level); - assert(res == 12); + expect(res == 12); } test "pointer to packed struct member in a stack variable" { @@ -464,7 +464,7 @@ test "pointer to packed struct member in a stack variable" { var s = S{ .a = 2, .b = 0 }; var b_ptr = &s.b; - assert(s.b == 0); + expect(s.b == 0); b_ptr.* = 2; - assert(s.b == 2); + expect(s.b == 2); } diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig similarity index 85% rename from test/cases/struct_contains_null_ptr_itself.zig rename to test/stage1/behavior/struct_contains_null_ptr_itself.zig index 21175974b3..991d742cec 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; test "struct contains null pointer which contains original struct" { var x: ?*NodeLineComment = null; - assert(x == null); + expect(x == null); } pub const Node = struct { diff --git a/test/stage1/behavior/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig new file mode 100644 index 0000000000..52c6579654 --- /dev/null +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -0,0 +1,85 @@ +const expect = @import("std").testing.expect; + +const Node = struct { + payload: i32, + children: []Node, +}; + +const NodeAligned = struct { + payload: i32, + children: []align(@alignOf(NodeAligned)) NodeAligned, +}; + +test "struct contains slice of itself" { + var other_nodes = []Node{ + Node{ + .payload = 31, + .children = []Node{}, + }, + Node{ + .payload = 32, + .children = []Node{}, + }, + }; + var nodes = []Node{ + Node{ + .payload = 1, + .children = []Node{}, + }, + Node{ + .payload = 2, + .children = []Node{}, + }, + Node{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = Node{ + .payload = 1234, + .children = nodes[0..], + }; + expect(root.payload == 1234); + expect(root.children[0].payload == 1); + expect(root.children[1].payload == 2); + expect(root.children[2].payload == 3); + expect(root.children[2].children[0].payload == 31); + expect(root.children[2].children[1].payload == 32); +} + +test "struct contains aligned slice of itself" { + var other_nodes = []NodeAligned{ + NodeAligned{ + .payload = 31, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 32, + .children = []NodeAligned{}, + }, + }; + var nodes = []NodeAligned{ + NodeAligned{ + .payload = 1, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 2, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = NodeAligned{ + .payload = 1234, + .children = nodes[0..], + }; + expect(root.payload == 1234); + expect(root.children[0].payload == 1); + expect(root.children[1].payload == 2); + expect(root.children[2].payload == 3); + expect(root.children[2].children[0].payload == 31); + expect(root.children[2].children[1].payload == 32); +} diff --git a/test/cases/switch.zig b/test/stage1/behavior/switch.zig similarity index 66% rename from test/cases/switch.zig rename to test/stage1/behavior/switch.zig index d5258f0bb1..1059bf28f8 100644 --- a/test/cases/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "switch with numbers" { testSwitchWithNumbers(13); @@ -10,14 +10,14 @@ fn testSwitchWithNumbers(x: u32) void { 13 => true, else => false, }; - assert(result); + expect(result); } test "switch with all ranges" { - assert(testSwitchWithAllRanges(50, 3) == 1); - assert(testSwitchWithAllRanges(101, 0) == 2); - assert(testSwitchWithAllRanges(300, 5) == 3); - assert(testSwitchWithAllRanges(301, 6) == 6); + expect(testSwitchWithAllRanges(50, 3) == 1); + expect(testSwitchWithAllRanges(101, 0) == 2); + expect(testSwitchWithAllRanges(300, 5) == 3); + expect(testSwitchWithAllRanges(301, 6) == 6); } fn testSwitchWithAllRanges(x: u32, y: u32) u32 { @@ -40,7 +40,7 @@ test "implicit comptime switch" { }; comptime { - assert(result + 1 == 14); + expect(result + 1 == 14); } } @@ -71,7 +71,7 @@ fn nonConstSwitch(foo: SwitchStatmentFoo) void { SwitchStatmentFoo.C => 3, SwitchStatmentFoo.D => 4, }; - assert(val == 3); + expect(val == 3); } const SwitchStatmentFoo = enum { A, @@ -93,10 +93,10 @@ const SwitchProngWithVarEnum = union(enum) { fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { switch (a) { SwitchProngWithVarEnum.One => |x| { - assert(x == 13); + expect(x == 13); }, SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); + expect(x == 13.0); }, SwitchProngWithVarEnum.Meh => |x| { const v: void = x; @@ -116,7 +116,7 @@ fn testSwitchEnumPtrCapture() void { else => unreachable, } switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), + SwitchProngWithVarEnum.One => |x| expect(x == 1235), else => unreachable, } } @@ -127,7 +127,7 @@ test "switch with multiple expressions" { 4, 5, 6 => 2, else => i32(3), }; - assert(x == 2); + expect(x == 2); } fn returnsFive() i32 { return 5; @@ -149,12 +149,12 @@ fn returnsFalse() bool { } } test "switch on const enum with var" { - assert(!returnsFalse()); + expect(!returnsFalse()); } test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); + expect(trueIfBoolFalseOtherwise(bool)); + expect(!trueIfBoolFalseOtherwise(i32)); } fn trueIfBoolFalseOtherwise(comptime T: type) bool { @@ -170,16 +170,16 @@ test "switch handles all cases of number" { } fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); + expect(testSwitchHandleAllCasesExhaustive(0) == 3); + expect(testSwitchHandleAllCasesExhaustive(1) == 2); + expect(testSwitchHandleAllCasesExhaustive(2) == 1); + expect(testSwitchHandleAllCasesExhaustive(3) == 0); - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); + expect(testSwitchHandleAllCasesRange(100) == 0); + expect(testSwitchHandleAllCasesRange(200) == 1); + expect(testSwitchHandleAllCasesRange(201) == 2); + expect(testSwitchHandleAllCasesRange(202) == 4); + expect(testSwitchHandleAllCasesRange(230) == 3); } fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { @@ -207,8 +207,8 @@ test "switch all prongs unreachable" { } fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); + expect(switchWithUnreachable(1) == 2); + expect(switchWithUnreachable(2) == 10); } fn switchWithUnreachable(x: i32) i32 { @@ -230,5 +230,42 @@ test "capture value of switch with all unreachable prongs" { const x = return_a_number() catch |err| switch (err) { else => unreachable, }; - assert(x == 1); + expect(x == 1); +} + +test "switching on booleans" { + testSwitchOnBools(); + comptime testSwitchOnBools(); +} + +fn testSwitchOnBools() void { + expect(testSwitchOnBoolsTrueAndFalse(true) == false); + expect(testSwitchOnBoolsTrueAndFalse(false) == true); + + expect(testSwitchOnBoolsTrueWithElse(true) == false); + expect(testSwitchOnBoolsTrueWithElse(false) == true); + + expect(testSwitchOnBoolsFalseWithElse(true) == false); + expect(testSwitchOnBoolsFalseWithElse(false) == true); +} + +fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { + return switch (x) { + true => false, + false => true, + }; +} + +fn testSwitchOnBoolsTrueWithElse(x: bool) bool { + return switch (x) { + true => false, + else => true, + }; +} + +fn testSwitchOnBoolsFalseWithElse(x: bool) bool { + return switch (x) { + false => true, + else => false, + }; } diff --git a/test/cases/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig similarity index 82% rename from test/cases/switch_prong_err_enum.zig rename to test/stage1/behavior/switch_prong_err_enum.zig index 89060690fc..3593eabb5a 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; var read_count: u64 = 0; @@ -22,9 +22,9 @@ fn doThing(form_id: u64) anyerror!FormValue { test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { - assert(payload == 1); + expect(payload == 1); }, else => unreachable, } - assert(read_count == 1); + expect(read_count == 1); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig similarity index 86% rename from test/cases/switch_prong_implicit_cast.zig rename to test/stage1/behavior/switch_prong_implicit_cast.zig index 56d37e290f..da965915ca 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const FormValue = union(enum) { One: void, @@ -18,5 +18,5 @@ test "switch prong implicit cast" { FormValue.One => false, FormValue.Two => |x| x, }; - assert(result); + expect(result); } diff --git a/test/cases/syntax.zig b/test/stage1/behavior/syntax.zig similarity index 99% rename from test/cases/syntax.zig rename to test/stage1/behavior/syntax.zig index 0c8c3c5ed3..451e396142 100644 --- a/test/cases/syntax.zig +++ b/test/stage1/behavior/syntax.zig @@ -57,3 +57,4 @@ fn asm_lists() void { :::"a","b",); } } + diff --git a/test/cases/this.zig b/test/stage1/behavior/this.zig similarity index 78% rename from test/cases/this.zig rename to test/stage1/behavior/this.zig index c7be074f36..a0bee3a3ee 100644 --- a/test/cases/this.zig +++ b/test/stage1/behavior/this.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const module = @This(); @@ -20,7 +20,7 @@ fn add(x: i32, y: i32) i32 { } test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); + expect(module.add(1, 2) == 3); } test "this refer to container" { @@ -29,6 +29,7 @@ test "this refer to container" { .y = 34, }; pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); + expect(pt.x == 13); + expect(pt.y == 35); } + diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig new file mode 100644 index 0000000000..099b6c3359 --- /dev/null +++ b/test/stage1/behavior/truncate.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "truncate u0 to larger integer allowed and has comptime known result" { + var x: u0 = 0; + const y = @truncate(u8, x); + comptime expect(y == 0); +} + +test "truncate.u0.literal" { + var z = @truncate(u0, 0); + expect(z == 0); +} + +test "truncate.u0.const" { + const c0: usize = 0; + var z = @truncate(u0, c0); + expect(z == 0); +} + +test "truncate.u0.var" { + var d: u8 = 2; + var z = @truncate(u0, d); + expect(z == 0); +} + +test "truncate sign mismatch but comptime known so it works anyway" { + const x: u32 = 10; + var result = @truncate(i8, x); + expect(result == 10); +} + +test "truncate on comptime integer" { + var x = @truncate(u16, 9999); + expect(x == 9999); +} diff --git a/test/cases/try.zig b/test/stage1/behavior/try.zig similarity index 82% rename from test/cases/try.zig rename to test/stage1/behavior/try.zig index 450a9af6ac..9c700f6260 100644 --- a/test/cases/try.zig +++ b/test/stage1/behavior/try.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "try on error union" { tryOnErrorUnionImpl(); @@ -11,7 +11,7 @@ fn tryOnErrorUnionImpl() void { error.CrappedOut => i32(2), else => unreachable, }; - assert(x == 11); + expect(x == 11); } fn returnsTen() anyerror!i32 { @@ -20,10 +20,10 @@ fn returnsTen() anyerror!i32 { test "try without vars" { const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); + expect(result1 == 2); const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); + expect(result2 == 1); } fn failIfTrue(ok: bool) anyerror!void { @@ -38,6 +38,6 @@ test "try then not executed with assignment" { if (failIfTrue(true)) { unreachable; } else |err| { - assert(err == error.ItBroke); + expect(err == error.ItBroke); } } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig new file mode 100644 index 0000000000..dc185cc960 --- /dev/null +++ b/test/stage1/behavior/type_info.zig @@ -0,0 +1,291 @@ +const expect = @import("std").testing.expect; +const mem = @import("std").mem; +const TypeInfo = @import("builtin").TypeInfo; +const TypeId = @import("builtin").TypeId; + +test "type info: tag type, void info" { + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + expect(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + expect(TypeId(void_info) == TypeId.Void); + expect(void_info.Void == {}); +} + +test "type info: integer, floating point type info" { + testIntFloat(); + comptime testIntFloat(); +} + +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + expect(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(f64_info.Float.bits == 64); +} + +test "type info: pointer type info" { + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(*u32); + expect(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); +} + +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + expect(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.alignment == @alignOf(f64)); + expect(u32_ptr_info.Pointer.child == f64); +} + +test "type info: C pointer type info" { + testCPtr(); + comptime testCPtr(); +} + +fn testCPtr() void { + const ptr_info = @typeInfo([*c]align(4) const i8); + expect(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); + expect(ptr_info.Pointer.alignment == 4); + expect(ptr_info.Pointer.child == i8); +} + +test "type info: slice type info" { + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + expect(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); + expect(u32_slice_info.Pointer.alignment == 4); + expect(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + expect(TypeId(arr_info) == TypeId.Array); + expect(arr_info.Array.len == 42); + expect(arr_info.Array.child == bool); +} + +test "type info: optional type info" { + testOptional(); + comptime testOptional(); +} + +fn testOptional() void { + const null_info = @typeInfo(?void); + expect(TypeId(null_info) == TypeId.Optional); + expect(null_info.Optional.child == void); +} + +test "type info: promise info" { + testPromise(); + comptime testPromise(); +} + +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + expect(TypeId(null_promise_info) == TypeId.Promise); + expect(null_promise_info.Promise.child == null); + + const promise_info = @typeInfo(promise->usize); + expect(TypeId(promise_info) == TypeId.Promise); + expect(promise_info.Promise.child.? == usize); +} + +test "type info: error set, error union info" { + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error{ + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + expect(TypeId(error_set_info) == TypeId.ErrorSet); + expect(error_set_info.ErrorSet.errors.len == 3); + expect(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + expect(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + expect(TypeId(error_union_info) == TypeId.ErrorUnion); + expect(error_union_info.ErrorUnion.error_set == TestErrorSet); + expect(error_union_info.ErrorUnion.payload == usize); +} + +test "type info: enum info" { + testEnum(); + comptime testEnum(); +} + +fn testEnum() void { + const Os = enum { + Windows, + Macos, + Linux, + FreeBSD, + }; + + const os_info = @typeInfo(Os); + expect(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")); + expect(os_info.Enum.fields[3].value == 3); + expect(os_info.Enum.tag_type == u2); + expect(os_info.Enum.defs.len == 0); +} + +test "type info: union info" { + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + expect(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 == 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.defs.len == 21); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + expect(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); + expect(notag_union_info.Union.fields[0].enum_field == null); + expect(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: *c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + expect(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + expect(extern_union_info.Union.tag_type == null); + expect(extern_union_info.Union.fields[0].enum_field == null); + expect(extern_union_info.Union.fields[0].field_type == *c_void); +} + +test "type info: struct info" { + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + expect(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); + expect(struct_info.Struct.fields[2].field_type == *TestStruct); + expect(struct_info.Struct.defs.len == 2); + expect(struct_info.Struct.defs[0].is_pub); + expect(!struct_info.Struct.defs[0].data.Fn.is_extern); + expect(struct_info.Struct.defs[0].data.Fn.lib_name == null); + expect(struct_info.Struct.defs[0].data.Fn.return_type == void); + expect(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); +} + +const TestStruct = packed struct { + const Self = @This(); + + fieldA: usize, + fieldB: void, + fieldC: *Self, + + pub fn foo(self: *const Self) void {} +}; + +test "type info: function type info" { + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + expect(TypeId(fn_info) == TypeId.Fn); + expect(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.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); + expect(fn_info.Fn.async_allocator_type == null); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + expect(TypeId(bound_fn_info) == TypeId.BoundFn); + expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); +} + +fn foo(comptime a: usize, b: bool, args: ...) usize { + return 0; +} + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void {} + }; + comptime var info = @typeInfo(S); +} + +test "type info: vectors" { + testVector(); + comptime testVector(); +} + +fn testVector() void { + const vec_info = @typeInfo(@Vector(4, i32)); + expect(TypeId(vec_info) == TypeId.Vector); + expect(vec_info.Vector.len == 4); + expect(vec_info.Vector.child == i32); +} diff --git a/test/cases/undefined.zig b/test/stage1/behavior/undefined.zig similarity index 63% rename from test/cases/undefined.zig rename to test/stage1/behavior/undefined.zig index 83c620d211..4c233576cd 100644 --- a/test/cases/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const mem = @import("std").mem; fn initStaticArray() [10]i32 { @@ -11,16 +11,16 @@ fn initStaticArray() [10]i32 { } const static_array = initStaticArray(); test "init static array to undefined" { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + expect(static_array[0] == 1); + expect(static_array[4] == 2); + expect(static_array[7] == 3); + expect(static_array[9] == 4); comptime { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + expect(static_array[0] == 1); + expect(static_array[4] == 2); + expect(static_array[7] == 3); + expect(static_array[9] == 4); } } @@ -40,12 +40,12 @@ test "assign undefined to struct" { comptime { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + expect(foo.x == 2); } { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + expect(foo.x == 2); } } @@ -53,16 +53,17 @@ test "assign undefined to struct with method" { comptime { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + expect(foo.x == 3); } { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + expect(foo.x == 3); } } test "type name of undefined" { const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + expect(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } + diff --git a/test/cases/underscore.zig b/test/stage1/behavior/underscore.zig similarity index 93% rename from test/cases/underscore.zig rename to test/stage1/behavior/underscore.zig index da1c97659c..fd5aebc87e 100644 --- a/test/cases/underscore.zig +++ b/test/stage1/behavior/underscore.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; test "ignore lval with underscore" { _ = false; diff --git a/test/cases/union.zig b/test/stage1/behavior/union.zig similarity index 80% rename from test/cases/union.zig rename to test/stage1/behavior/union.zig index 019a7012da..0a4e2cfb92 100644 --- a/test/cases/union.zig +++ b/test/stage1/behavior/union.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const Value = union(enum) { Int: u64, @@ -27,11 +27,11 @@ const array = []Value{ test "unions embedded in aggregate types" { switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), + Value.Array => |arr| expect(arr[4] == 3), else => unreachable, } switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), + Value.Int => |x| expect(x == 1234), else => unreachable, } } @@ -43,18 +43,18 @@ const Foo = union { test "basic unions" { var foo = Foo{ .int = 1 }; - assert(foo.int == 1); + expect(foo.int == 1); foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); + expect(foo.float == 12.34); } test "comptime union field access" { comptime { var foo = Foo{ .int = 0 }; - assert(foo.int == 0); + expect(foo.int == 0); foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); + expect(foo.float == 42.42); } } @@ -62,10 +62,10 @@ test "init union with runtime value" { var foo: Foo = undefined; setFloat(&foo, 12.34); - assert(foo.float == 12.34); + expect(foo.float == 12.34); setInt(&foo, 42); - assert(foo.int == 42); + expect(foo.int == 42); } fn setFloat(foo: *Foo, x: f64) void { @@ -83,9 +83,9 @@ const FooExtern = extern union { test "basic extern unions" { var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); + expect(foo.int == 1); foo.float = 12.34; - assert(foo.float == 12.34); + expect(foo.float == 12.34); } const Letter = enum { @@ -105,11 +105,11 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); + expect(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); + expect(Letter(value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, @@ -125,8 +125,8 @@ const MultipleChoice = union(enum(u32)) { }; test "simple union(enum(u32))" { var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + expect(x == MultipleChoice.C); + expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -142,14 +142,14 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + comptime expect(@TagType(@TagType(MultipleChoice2)) == u32); testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assert(1123 == switch (x) { + expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + expect(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -167,7 +167,7 @@ const ExternPtrOrInt = extern union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); + comptime expect(@sizeOf(ExternPtrOrInt) == 8); } const PackedPtrOrInt = packed union { @@ -175,14 +175,14 @@ const PackedPtrOrInt = packed union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime expect(@sizeOf(PackedPtrOrInt) == 8); } const ZeroBits = union { OnlyField: void, }; test "union with only 1 field which is void should be zero bits" { - comptime assert(@sizeOf(ZeroBits) == 0); + comptime expect(@sizeOf(ZeroBits) == 0); } const TheTag = enum { @@ -196,9 +196,9 @@ const TheUnion = union(TheTag) { C: i32, }; test "union field access gives the enum values" { - assert(TheUnion.A == TheTag.A); - assert(TheUnion.B == TheTag.B); - assert(TheUnion.C == TheTag.C); + expect(TheUnion.A == TheTag.A); + expect(TheUnion.B == TheTag.B); + expect(TheUnion.C == TheTag.C); } test "cast union to tag type of union" { @@ -207,12 +207,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); + expect(TheTag(x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); + expect(Letter2(x) == Letter2.B); } const Letter2 = enum { A, @@ -227,11 +227,11 @@ const Value2 = union(Letter2) { test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; - assert(x == Letter2.B); + expect(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); + expect(x == Value2.B); } pub const PackThis = union(enum) { @@ -244,7 +244,7 @@ test "constant packed union" { } fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); + expect(expected_tokens[0].StringLiteral == 1); } test "switch on union with only 1 field" { @@ -256,7 +256,7 @@ test "switch on union with only 1 field" { z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); + expect(x == 1234); return; }, } @@ -282,11 +282,11 @@ test "access a member of tagged union with conflicting enum tag name" { const B = void; }; - comptime assert(Bar.A == u8); + comptime expect(Bar.A == u8); } test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); + expect(testTaggedUnionInit({})); } const TaggedUnionWithAVoid = union(enum) { @@ -324,9 +324,9 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); + comptime expect(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); + expect(t == Expr.Literal); } test "union with only 1 field casted to its enum type which has enum value specified" { @@ -344,9 +344,9 @@ test "union with only 1 field casted to its enum type which has enum value speci }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); + comptime expect(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); + expect(t == Expr.Literal); + expect(@enumToInt(t) == 33); + comptime expect(@enumToInt(t) == 33); } diff --git a/test/cases/var_args.zig b/test/stage1/behavior/var_args.zig similarity index 60% rename from test/cases/var_args.zig rename to test/stage1/behavior/var_args.zig index 3eb6e30448..cc93b57f06 100644 --- a/test/cases/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; fn add(args: ...) i32 { var sum = i32(0); @@ -12,9 +12,9 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); + expect(add(i32(1), i32(2), i32(3), i32(4)) == 10); + expect(add(i32(1234)) == 1234); + expect(add() == 0); } fn readFirstVarArg(args: ...) void { @@ -26,9 +26,9 @@ test "send void arg to var args" { } test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); + expect(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + expect(addSomeStuff(i32(1234)) == 1234); + expect(addSomeStuff() == 0); } fn addSomeStuff(args: ...) i32 { @@ -36,24 +36,24 @@ fn addSomeStuff(args: ...) i32 { } test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); + expect(extraFn(10) == 0); + expect(extraFn(10, false) == 1); + expect(extraFn(10, false, true) == 2); // TODO issue #313 //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); + // expect(extraFn(10) == 0); + // expect(extraFn(10, false) == 1); + // expect(extraFn(10, false, true) == 2); //} } fn extraFn(extra: u32, args: ...) usize { if (args.len >= 1) { - assert(args[0] == false); + expect(args[0] == false); } if (args.len >= 2) { - assert(args[1] == true); + expect(args[1] == true); } return args.len; } @@ -71,8 +71,8 @@ fn foo2(args: ...) bool { } test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); + expect(foos[0]()); + expect(!foos[1]()); } test "pass zero length array to var args param" { diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig new file mode 100644 index 0000000000..b0d2871454 --- /dev/null +++ b/test/stage1/behavior/vector.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const mem = std.mem; +const expect = std.testing.expect; + +test "vector wrap operators" { + const S = struct { + fn doTheTest() void { + const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; + const 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 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "vector float operators" { + const S = struct { + fn doTheTest() void { + const v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; + const 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 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "vector bit operators" { + const S = struct { + fn doTheTest() void { + const v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; + const 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 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/cases/void.zig b/test/stage1/behavior/void.zig similarity index 71% rename from test/cases/void.zig rename to test/stage1/behavior/void.zig index 7121ac664b..9722791946 100644 --- a/test/cases/void.zig +++ b/test/stage1/behavior/void.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; const Foo = struct { a: void, @@ -13,14 +13,14 @@ test "compare void with void compile time known" { .b = 1, .c = {}, }; - assert(foo.a == {}); + expect(foo.a == {}); } } test "iterate over a void slice" { var j: usize = 0; for (times(10)) |_, i| { - assert(i == j); + expect(i == j); j += 1; } } @@ -28,3 +28,8 @@ test "iterate over a void slice" { fn times(n: usize) []const void { return ([*]void)(undefined)[0..n]; } + +test "void optional" { + var x: ?void = {}; + expect(x != null); +} diff --git a/test/cases/while.zig b/test/stage1/behavior/while.zig similarity index 86% rename from test/cases/while.zig rename to test/stage1/behavior/while.zig index f774e0ec6b..29ad90ed17 100644 --- a/test/cases/while.zig +++ b/test/stage1/behavior/while.zig @@ -1,12 +1,12 @@ -const assert = @import("std").debug.assert; +const expect = @import("std").testing.expect; test "while loop" { var i: i32 = 0; while (i < 4) { i += 1; } - assert(i == 4); - assert(whileLoop1() == 1); + expect(i == 4); + expect(whileLoop1() == 1); } fn whileLoop1() i32 { return whileLoop2(); @@ -16,8 +16,9 @@ fn whileLoop2() i32 { return 1; } } + test "static eval while" { - assert(static_eval_while_number == 1); + expect(static_eval_while_number == 1); } const static_eval_while_number = staticWhileLoop1(); fn staticWhileLoop1() i32 { @@ -31,7 +32,7 @@ fn staticWhileLoop2() i32 { test "continue and break" { runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); + expect(continue_and_break_counter == 8); } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { @@ -44,7 +45,7 @@ fn runContinueAndBreakTest() void { } break; } - assert(i == 4); + expect(i == 4); } test "return with implicit cast from while loop" { @@ -65,7 +66,7 @@ test "while with continue expression" { sum += i; } } - assert(sum == 40); + expect(sum == 40); } test "while with else" { @@ -77,8 +78,8 @@ test "while with else" { } else { got_else += 1; } - assert(sum == 10); - assert(got_else == 1); + expect(sum == 10); + expect(got_else == 1); } test "while with optional as condition" { @@ -87,7 +88,7 @@ test "while with optional as condition" { while (getNumberOrNull()) |value| { sum += value; } - assert(sum == 45); + expect(sum == 45); } test "while with optional as condition with else" { @@ -96,12 +97,12 @@ test "while with optional as condition with else" { var got_else: i32 = 0; while (getNumberOrNull()) |value| { sum += value; - assert(got_else == 0); + expect(got_else == 0); } else { got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + expect(sum == 45); + expect(got_else == 1); } test "while with error union condition" { @@ -111,11 +112,11 @@ test "while with error union condition" { while (getNumberOrErr()) |value| { sum += value; } else |err| { - assert(err == error.OutOfNumbers); + expect(err == error.OutOfNumbers); got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + expect(sum == 45); + expect(got_else == 1); } var numbers_left: i32 = undefined; @@ -137,7 +138,7 @@ test "while on optional with else result follow else prong" { break value; } else i32(2); - assert(result == 2); + expect(result == 2); } test "while on optional with else result follow break prong" { @@ -145,7 +146,7 @@ test "while on optional with else result follow break prong" { break value; } else i32(2); - assert(result == 10); + expect(result == 10); } test "while on error union with else result follow else prong" { @@ -153,7 +154,7 @@ test "while on error union with else result follow else prong" { break value; } else |err| i32(2); - assert(result == 2); + expect(result == 2); } test "while on error union with else result follow break prong" { @@ -161,7 +162,7 @@ test "while on error union with else result follow break prong" { break value; } else |err| i32(2); - assert(result == 10); + expect(result == 10); } test "while on bool with else result follow else prong" { @@ -169,7 +170,7 @@ test "while on bool with else result follow else prong" { break i32(10); } else i32(2); - assert(result == 2); + expect(result == 2); } test "while on bool with else result follow break prong" { @@ -177,7 +178,7 @@ test "while on bool with else result follow break prong" { break i32(10); } else i32(2); - assert(result == 10); + expect(result == 10); } test "break from outer while loop" { diff --git a/test/cases/widening.zig b/test/stage1/behavior/widening.zig similarity index 80% rename from test/cases/widening.zig rename to test/stage1/behavior/widening.zig index cf6ab4ca0f..f7c238ee8d 100644 --- a/test/cases/widening.zig +++ b/test/stage1/behavior/widening.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const expect = std.testing.expect; const mem = std.mem; test "integer widening" { @@ -9,13 +9,13 @@ test "integer widening" { var d: u64 = c; var e: u64 = d; var f: u128 = e; - assert(f == a); + expect(f == a); } test "implicit unsigned integer to signed integer" { var a: u8 = 250; var b: i16 = a; - assert(b == 250); + expect(b == 250); } test "float widening" { @@ -23,5 +23,6 @@ test "float widening" { var b: f32 = a; var c: f64 = b; var d: f128 = c; - assert(d == a); + expect(d == a); } + diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 196311a283..4805fc9896 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; +const expect = std.testing.expect; extern fn run_c_tests() void; @@ -33,28 +33,28 @@ test "C ABI integers" { } export fn zig_u8(x: u8) void { - assertOrPanic(x == 0xff); + expect(x == 0xff); } export fn zig_u16(x: u16) void { - assertOrPanic(x == 0xfffe); + expect(x == 0xfffe); } export fn zig_u32(x: u32) void { - assertOrPanic(x == 0xfffffffd); + expect(x == 0xfffffffd); } export fn zig_u64(x: u64) void { - assertOrPanic(x == 0xfffffffffffffffc); + expect(x == 0xfffffffffffffffc); } export fn zig_i8(x: i8) void { - assertOrPanic(x == -1); + expect(x == -1); } export fn zig_i16(x: i16) void { - assertOrPanic(x == -2); + expect(x == -2); } export fn zig_i32(x: i32) void { - assertOrPanic(x == -3); + expect(x == -3); } export fn zig_i64(x: i64) void { - assertOrPanic(x == -4); + expect(x == -4); } extern fn c_f32(f32) void; @@ -66,10 +66,10 @@ test "C ABI floats" { } export fn zig_f32(x: f32) void { - assertOrPanic(x == 12.34); + expect(x == 12.34); } export fn zig_f64(x: f64) void { - assertOrPanic(x == 56.78); + expect(x == 56.78); } extern fn c_ptr(*c_void) void; @@ -79,7 +79,7 @@ test "C ABI pointer" { } export fn zig_ptr(x: *c_void) void { - assertOrPanic(@ptrToInt(x) == 0xdeadbeef); + expect(@ptrToInt(x) == 0xdeadbeef); } extern fn c_bool(bool) void; @@ -89,7 +89,7 @@ test "C ABI bool" { } export fn zig_bool(x: bool) void { - assertOrPanic(x); + expect(x); } extern fn c_array([10]u8) void; @@ -100,7 +100,7 @@ test "C ABI array" { } export fn zig_array(x: [10]u8) void { - assertOrPanic(std.mem.eql(u8, x, "1234567890")); + expect(std.mem.eql(u8, x, "1234567890")); } const BigStruct = extern struct { @@ -124,11 +124,11 @@ test "C ABI big struct" { } export fn zig_big_struct(x: BigStruct) void { - assertOrPanic(x.a == 1); - assertOrPanic(x.b == 2); - assertOrPanic(x.c == 3); - assertOrPanic(x.d == 4); - assertOrPanic(x.e == 5); + expect(x.a == 1); + expect(x.b == 2); + expect(x.c == 3); + expect(x.d == 4); + expect(x.e == 5); } const BigUnion = extern union { @@ -150,11 +150,11 @@ test "C ABI big union" { } export fn zig_big_union(x: BigUnion) void { - assertOrPanic(x.a.a == 1); - assertOrPanic(x.a.b == 2); - assertOrPanic(x.a.c == 3); - assertOrPanic(x.a.d == 4); - assertOrPanic(x.a.e == 5); + expect(x.a.a == 1); + expect(x.a.b == 2); + expect(x.a.c == 3); + expect(x.a.d == 4); + expect(x.a.e == 5); } const SmallStructInts = extern struct { @@ -176,10 +176,10 @@ test "C ABI small struct of ints" { } export fn zig_small_struct_ints(x: SmallStructInts) void { - assertOrPanic(x.a == 1); - assertOrPanic(x.b == 2); - assertOrPanic(x.c == 3); - assertOrPanic(x.d == 4); + expect(x.a == 1); + expect(x.b == 2); + expect(x.c == 3); + expect(x.d == 4); } const SplitStructInt = extern struct { @@ -199,9 +199,9 @@ test "C ABI split struct of ints" { } export fn zig_split_struct_ints(x: SplitStructInt) void { - assertOrPanic(x.a == 1234); - assertOrPanic(x.b == 100); - assertOrPanic(x.c == 1337); + expect(x.a == 1234); + expect(x.b == 100); + expect(x.c == 1337); } extern fn c_big_struct_both(BigStruct) BigStruct; @@ -215,19 +215,19 @@ test "C ABI sret and byval together" { .e = 5, }; var y = c_big_struct_both(s); - assertOrPanic(y.a == 10); - assertOrPanic(y.b == 11); - assertOrPanic(y.c == 12); - assertOrPanic(y.d == 13); - assertOrPanic(y.e == 14); + expect(y.a == 10); + expect(y.b == 11); + expect(y.c == 12); + expect(y.d == 13); + expect(y.e == 14); } export fn zig_big_struct_both(x: BigStruct) BigStruct { - assertOrPanic(x.a == 30); - assertOrPanic(x.b == 31); - assertOrPanic(x.c == 32); - assertOrPanic(x.d == 33); - assertOrPanic(x.e == 34); + expect(x.a == 30); + expect(x.b == 31); + expect(x.c == 32); + expect(x.d == 33); + expect(x.e == 34); var s = BigStruct{ .a = 20, .b = 21, diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index 52863d5fa4..f5bcd59ecf 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -3,6 +3,7 @@ const io = std.io; const mem = std.mem; const debug = std.debug; const assert = debug.assert; +const testing = std.testing; const Buffer = std.Buffer; const ArrayList = std.ArrayList; const maxInt = std.math.maxInt; @@ -220,11 +221,7 @@ fn expectError(test_input: []const u8, expected_err: anyerror) void { var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable; defer output_buf.deinit(); - if (expandString(test_input, &output_buf)) { - unreachable; - } else |err| { - assert(expected_err == err); - } + testing.expectError(expected_err, expandString(test_input, &output_buf)); } test "valid inputs" { @@ -256,5 +253,5 @@ fn expectExpansion(test_input: []const u8, expected_result: []const u8) void { expandString(test_input, &result) catch unreachable; - assert(mem.eql(u8, result.toSlice(), expected_result)); + testing.expectEqualSlices(u8, expected_result, result.toSlice()); } diff --git a/test/standalone/issue_794/main.zig b/test/standalone/issue_794/main.zig index 356a106418..191bdc9b4f 100644 --- a/test/standalone/issue_794/main.zig +++ b/test/standalone/issue_794/main.zig @@ -1,7 +1,7 @@ const c = @cImport(@cInclude("foo.h")); const std = @import("std"); -const assert = std.debug.assert; +const testing = std.testing; test "c import" { - comptime assert(c.NUMBER == 1234); + comptime testing.expect(c.NUMBER == 1234); } diff --git a/test/tests.zig b/test/tests.zig index 1ca06b4b34..800ddc1ccd 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -48,13 +48,14 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compare_output.addCases(cases); @@ -62,13 +63,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-runtime-safety", "Run the runtime safety tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; runtime_safety.addCases(cases); @@ -76,13 +78,14 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompileErrorContext{ + const cases = b.allocator.create(CompileErrorContext) catch unreachable; + cases.* = CompileErrorContext{ .b = b, .step = b.step("test-compile-errors", "Run the compile error tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compile_errors.addCases(cases); @@ -90,13 +93,14 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(BuildExamplesContext{ + const cases = b.allocator.create(BuildExamplesContext) catch unreachable; + cases.* = BuildExamplesContext{ .b = b, .step = b.step("test-build-examples", "Build the examples"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; build_examples.addCases(cases); @@ -119,13 +123,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M } pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; assemble_and_link.addCases(cases); @@ -133,12 +138,13 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode } pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(TranslateCContext{ + const cases = b.allocator.create(TranslateCContext) catch unreachable; + cases.* = TranslateCContext{ .b = b, .step = b.step("test-translate-c", "Run the C transation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; translate_c.addCases(cases); @@ -146,12 +152,13 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St } pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(GenHContext{ + const cases = b.allocator.create(GenHContext) catch unreachable; + cases.* = GenHContext{ .b = b, .step = b.step("test-gen-h", "Run the C header file generation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; gen_h.addCases(cases); @@ -163,25 +170,35 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); for (modes) |mode| { - for ([]bool{ - false, - true, - }) |link_libc| { - if (link_libc and !is_native) { - // don't assume we have a cross-compiling libc set up - continue; + for ([]bool{ false, true }) |link_libc| { + for ([]bool{ false, true }) |single_threaded| { + if (link_libc and !is_native) { + // don't assume we have a cross-compiling libc set up + continue; + } + const these_tests = b.addTest(root_src); + these_tests.setNamePrefix(b.fmt( + "{}-{}-{}-{}-{}-{} ", + name, + @tagName(test_target.os), + @tagName(test_target.arch), + @tagName(mode), + if (link_libc) "c" else "bare", + if (single_threaded) "single" else "multi", + )); + these_tests.setFilter(test_filter); + these_tests.setBuildMode(mode); + if (!is_native) { + these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); + } + if (link_libc) { + these_tests.linkSystemLibrary("c"); + } + if (mem.eql(u8, name, "std")) { + these_tests.overrideStdDir("std"); + } + step.dependOn(&these_tests.step); } - const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); - these_tests.setFilter(test_filter); - these_tests.setBuildMode(mode); - if (!is_native) { - these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); - } - if (link_libc) { - these_tests.linkSystemLibrary("c"); - } - step.dependOn(&these_tests.step); } } } @@ -237,7 +254,8 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(RunCompareOutputStep{ + const ptr = allocator.create(RunCompareOutputStep) catch unreachable; + ptr.* = RunCompareOutputStep{ .context = context, .exe_path = exe_path, .name = name, @@ -245,7 +263,7 @@ pub const CompareOutputContext = struct { .test_index = context.test_index, .step = build.Step.init("RunCompareOutput", allocator, make), .cli_args = cli_args, - }) catch unreachable; + }; context.test_index += 1; return ptr; } @@ -324,13 +342,14 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; - const ptr = allocator.create(RuntimeSafetyRunStep{ + const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; + ptr.* = RuntimeSafetyRunStep{ .context = context, .exe_path = exe_path, .name = name, .test_index = context.test_index, .step = build.Step.init("RuntimeSafetyRun", allocator, make), - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -420,7 +439,10 @@ pub const CompareOutputContext = struct { pub fn addCase(self: *CompareOutputContext, case: TestCase) void { const b = self.b; - const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, case.sources.items[0].filename }, + ) catch unreachable; switch (case.special) { Special.Asm => { @@ -433,7 +455,10 @@ pub const CompareOutputContext = struct { exe.addAssemblyFile(root_src); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } @@ -457,7 +482,10 @@ pub const CompareOutputContext = struct { } for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } @@ -480,7 +508,10 @@ pub const CompareOutputContext = struct { } for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } @@ -507,6 +538,7 @@ pub const CompileErrorContext = struct { expected_errors: ArrayList([]const u8), link_libc: bool, is_exe: bool, + is_test: bool, const SourceFile = struct { filename: []const u8, @@ -535,14 +567,15 @@ pub const CompileErrorContext = struct { pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(CompileCmpOutputStep{ + const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; + ptr.* = CompileCmpOutputStep{ .step = build.Step.init("CompileCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, .build_mode = build_mode, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -552,13 +585,25 @@ pub const CompileErrorContext = struct { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; - const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable; - const obj_path = os.path.join(b.allocator, b.cache_root, "test.o") catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, self.case.sources.items[0].filename }, + ) catch unreachable; + const obj_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, "test.o" }, + ) catch unreachable; var zig_args = ArrayList([]const u8).init(b.allocator); zig_args.append(b.zig_exe) catch unreachable; - zig_args.append(if (self.case.is_exe) "build-exe" else "build-obj") catch unreachable; + if (self.case.is_exe) { + try zig_args.append("build-exe"); + } else if (self.case.is_test) { + try zig_args.append("test"); + } else { + try zig_args.append("build-obj"); + } zig_args.append(b.pathFromRoot(root_src)) catch unreachable; zig_args.append("--name") catch unreachable; @@ -654,13 +699,15 @@ pub const CompileErrorContext = struct { } pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_errors = ArrayList([]const u8).init(self.b.allocator), .link_libc = false, .is_exe = false, - }) catch unreachable; + .is_test = false, + }; tc.addSourceFile(".tmp_source.zig", source); comptime var arg_i = 0; @@ -687,6 +734,12 @@ pub const CompileErrorContext = struct { self.addCase(tc); } + pub fn addTest(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + const tc = self.create(name, source, expected_lines); + tc.is_test = true; + self.addCase(tc); + } + pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; @@ -700,7 +753,10 @@ pub const CompileErrorContext = struct { self.step.dependOn(&compile_and_cmp_errors.step); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } @@ -814,13 +870,14 @@ pub const TranslateCContext = struct { pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(TranslateCCmpOutputStep{ + 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, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -830,7 +887,10 @@ pub const TranslateCContext = struct { const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); const b = self.context.b; - const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable; + const root_src = os.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; @@ -921,12 +981,13 @@ pub const TranslateCContext = struct { } 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{ + 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, - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0; @@ -963,7 +1024,10 @@ pub const TranslateCContext = struct { self.step.dependOn(&translate_c_and_cmp.step); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } @@ -1008,14 +1072,15 @@ pub const GenHContext = struct { pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(GenHCmpOutputStep{ + const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; + ptr.* = GenHCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .h_path = h_path, .name = name, .test_index = context.test_index, .case = case, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -1055,11 +1120,12 @@ pub const GenHContext = struct { } pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(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), - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0; @@ -1076,7 +1142,10 @@ pub const GenHContext = struct { pub fn addCase(self: *GenHContext, case: *const TestCase) void { const b = self.b; - const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; + const root_src = os.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; @@ -1088,7 +1157,10 @@ pub const GenHContext = struct { obj.setBuildMode(mode); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable; + const expanded_src_path = os.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); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 13f2a964d0..6a26b99104 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -117,11 +117,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?[*]Foo, + \\ a: [*c]Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?[*]Foo, + \\ a: [*c]Foo, \\}; ); @@ -213,7 +213,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?[*]u8, + \\ y: [*c]u8, \\}; , \\pub const Foo = struct_Foo; @@ -244,7 +244,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -254,7 +254,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?[*]c_int) void; + \\pub extern fn func(array: [*c]c_int) void; ); cases.add("self referential struct with function pointer", @@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?[*]struct_Foo) void, + \\ derp: ?extern fn([*c]struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -322,11 +322,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?[*]struct_Foo, + \\ next: [*c]struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?[*]struct_Bar, + \\ next: [*c]struct_Bar, \\}; ); @@ -610,11 +610,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\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 ((b != 0) and (c != 0)) return 1; + \\ if ((a != 0) and (c != 0)) 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; + \\ if ((b != 0) or (c != 0)) return 4; + \\ if ((a != 0) or (c != 0)) return 5; \\ return 6; \\} ); @@ -710,7 +710,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?[*]struct_Foo) c_int { + \\pub export fn read_field(foo: [*c]struct_Foo) c_int { \\ return foo.?.field; \\} ); @@ -756,7 +756,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?[*]c_ushort) ?*c_void { + \\pub export fn foo(x: [*c]c_ushort) ?*c_void { \\ return @ptrCast(?*c_void, x); \\} ); @@ -777,8 +777,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?[*]c_int { - \\ return null; + \\pub export fn foo() [*c]c_int { + \\ return 0; \\} ); @@ -1086,7 +1086,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?[*]c_int) void { + \\pub export fn foo(x: [*c]c_int) void { \\ x.?.* = 1; \\} ); @@ -1114,7 +1114,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?[*]c_int = &x; + \\ var ptr: [*c]c_int = &x; \\ return ptr.?.*; \\} ); @@ -1124,7 +1124,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?[*]const u8 { + \\pub fn foo() [*c]const u8 { \\ return c"bar"; \\} ); @@ -1253,8 +1253,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?[*]c_int) ?[*]f32 { - \\ return @ptrCast(?[*]f32, a); + \\fn ptrcast(a: [*c]c_int) [*c]f32 { + \\ return @ptrCast([*c]f32, a); \\} ); @@ -1280,7 +1280,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); - \\ return !(c != null); + \\ return !(c != 0); \\} ); @@ -1297,7 +1297,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?[*]const u8 = c"0.0.0"; + \\pub var v0: [*c]const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1306,17 +1306,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: [*]const u8 = c"2.2.2"; + \\ const v2: [*c]const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE); + \\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); ); - cases.add("if on none bool", + cases.add("if on non-bool", \\enum SomeEnum { A, B, C }; \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { \\ if (a) return 0; @@ -1337,13 +1337,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; - \\ if (c != null) return 2; + \\ if (c != 0) return 2; \\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3; \\ return 4; \\} ); - cases.add("while on none bool", + cases.add("while on non-bool", \\int while_none_bool(int a, float b, void *c) { \\ while (a) return 0; \\ while (b) return 1; @@ -1354,12 +1354,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\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; + \\ while (c != 0) return 2; \\ return 3; \\} ); - cases.add("for on none bool", + cases.add("for on non-bool", \\int for_none_bool(int a, float b, void *c) { \\ for (;a;) return 0; \\ for (;b;) return 1; @@ -1370,7 +1370,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\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; + \\ while (c != 0) return 2; \\ return 3; \\} ); @@ -1416,4 +1416,34 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} ); + + // cases.add("empty array with initializer", + // "int a[4] = {};" + // , + // "pub var a: [4]c_int = [1]c_int{0} ** 4;" + // ); + + // cases.add("array with initialization", + // "int a[4] = {1, 2, 3, 4};" + // , + // "pub var a: [4]c_int = [4]c_int{1, 2, 3, 4};" + // ); + + // cases.add("array with incomplete initialization", + // "int a[4] = {3, 4};" + // , + // "pub var a: [4]c_int = [2]c_int{3, 4} ++ ([1]c_int{0} ** 2);" + // ); + + // cases.add("2D array with initialization", + // "int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };" + // , + // "pub var a: [3][3]c_int = [3][3]c_int{[3]c_int{1, 2, 3}, [3]c_int{4, 5, 6}, [3]c_int{7, 8, 9}};" + // ); + + // cases.add("2D array with incomplete initialization", + // "int a[3][3] = { {1, 2}, {4, 5, 6} };" + // , + // "pub var a: [3][3]c_int = [2][3]c_int{[2]c_int{1, 2} ++ [1]c_int{0}, [3]c_int{4, 5, 6}} ++ [1][3]c_int{[1]c_int{0} ** 3};" + // ); }