From 101f6d85654b4221ce5123752f850575adc852a7 Mon Sep 17 00:00:00 2001 From: g-w1 Date: Tue, 5 Jan 2021 15:07:32 -0500 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3e8aaee829584f4893cad5c524076d2300f45f24 Author: Andrew Kelley Date: Mon Jan 4 22:25:04 2021 -0700 std: skip more tests on Windows to save CI memory I've enabled only the tests that check things specific to Windows that are not tested by other systems. commit 16896a9d8b4015ebebd36ee558d49446a756b3f5 Author: Andrew Kelley Date: Mon Jan 4 15:57:54 2021 -0700 ci: skip crypto tests on windows Trying to buy us more time on the Windows CI. commit 66e5e92a3e958c8097a2366ff74f34ebdad40154 Merge: d95724454 2561168ad Author: Andrew Kelley Date: Mon Jan 4 14:23:01 2021 -0800 Merge pull request #7592 from LemonBoy/fix-7188 Allow variable captures on multi-prong switch arms commit d95724454c84b22e9030aec69b88d1fa9fd5175b Author: Evan Haas Date: Fri Jan 1 23:13:15 2021 -0800 Allow dollar sign $ in identifiers in translate-c In strictly conforming C, identifiers cannot container dollar signs. However GCC and Clang allow them by default, so translate-c should handle them. See http://gcc.gnu.org/onlinedocs/cpp/Tokenization.html I encountered this in the wild in windows.h Fixes #7585 commit 819f2a01a16c7db82ee407850773dabcf3ea8587 Author: Felix (xq) Queißner Date: Mon Jan 4 12:20:43 2021 +0100 Fixes missing error prong in std.os.send. commit fc3508b7e86c27effea26c1026b2c86afb487932 Author: J.C. Moyer Date: Mon Jan 4 09:15:39 2021 -0500 Fix off-by-one error in SinglyLinkedList.len() and add associated tests commit a93c123f83f8440c40fa5cf2d5a84761b37ab45d Author: xackus <14938807+xackus@users.noreply.github.com> Date: Mon Jan 4 17:41:24 2021 +0100 std.c: add some noalias commit 2fe8a482159762724df93225bd70abbd0c2c5930 Author: Andrew Kelley Date: Mon Jan 4 14:59:18 2021 -0700 ci: omit stage2 backend from stage1 on Windows to avoid out-of-memory on the CI runs. commit 462c1d8c7424547051e643648797b9097245b046 Author: Andrew Kelley Date: Mon Jan 4 14:33:32 2021 -0700 stage2: add more perf tracing points commit fc38b42521027b2ddcba688ba07bd2f6eb2737bc Author: Andrew Kelley Date: Mon Jan 4 13:49:17 2021 -0700 Revert "Fix #7296:" This broke build scripts that wanted to refer to `exe_dir` or `install_path`. There has also been some pushback and discussion on this breaking change. I think it should be re-evaluated. This reverts commit a1a1929cf4ff979bdaba17f858d4ce8647e87a65. commit ef2fa67ef018a4a49124a40e60941149efd34553 Merge: aa0906e9a 7e64dc422 Author: Andrew Kelley Date: Mon Jan 4 13:40:51 2021 -0700 Merge branch 'g-w1-stage2-evalbranch' closes #7682 commit 7e64dc42215c93a2d1d6b7fa4f5e07b885788a7d Author: Andrew Kelley Date: Mon Jan 4 13:40:01 2021 -0700 stage2: improvements to `@setEvalBranchQuota` * extract magic number into a constant * properly use result location casting for the operand * naming convention for ZIR instructions commit 638f93ebdceb860974aae54b6f8c2c9f52157305 Author: g-w1 Date: Sun Jan 3 15:45:22 2021 -0500 stage2: implementation of `@setEvalBranchQuota`: `@setEvalBranchQuota` can be called before the comptime/inline call stack is created. For example: ```zig @setEvalBranchQuota(100); comptime { while (true) {} } ``` Here we need to set the branch_quota before the comptime block creates a scope for the branch_count. commit aa0906e9aaaf36bc928b5502bdb34e7a0409b2c0 Author: joachimschmidt557 Date: Sat Jan 2 18:53:11 2021 +0100 stage2 x86_64: fix bug in Function.gen Previously, the x86_64 backend would remove code for exitlude relocs if the jump amount were 0. This causes issues as earlier jumps rely on the jump being present at the same address. commit 4400d2d7abb3be8c7b9fde9754fc58d4510c5107 Author: Frank Denis Date: Sun Jan 3 09:10:59 2021 +0100 std/crypto: add BLAKE2-160 types and tests commit e4c4a0a5f6374d6c1b2daabc52395bfc58583b2f Author: daurnimator Date: Mon Jan 4 01:27:35 2021 +1100 Improve uring definitions commit 53a0b7997d1ee51f1c93cb44e6ee967e244b0c7d Merge: c8e44d82b 807dc56fd Author: Andrew Kelley Date: Sun Jan 3 19:51:38 2021 -0800 Merge pull request #7681 from kubkon/stage2-aarch64-fn-args stage2: basic fn args for aarch64 commit c8e44d82bd4dcd8f6beeb771027d38a576674364 Author: Andrew Kelley Date: Sun Jan 3 20:34:17 2021 -0700 stage2: remove the Cache deadlock detection code It's more trouble than it's worth; it didn't even catch the most recent incident because it was across process boundaries anyway. commit 404dc9692e33099cc59925d5cf03805224fcb36e Author: Andrew Kelley Date: Sun Jan 3 20:25:04 2021 -0700 stage2: fix Cache debug deadlock code memory leak commit 5c92e24a29ca403cc66515044a25d341a724f093 Author: Andrew Kelley Date: Sun Jan 3 20:10:07 2021 -0700 drone ci: skip compile error tests to save time These are covered by other CI scripts and we're up against Drone CI time limits. commit f6644255f595fb0bbfb2fa4276804ef7031330b1 Merge: 5cc131030 0151f3b76 Author: Andrew Kelley Date: Sun Jan 3 16:09:14 2021 -0800 Merge pull request #7598 from FireFox317/more-llvm-stage2 stage2: More improvements to self-hosted LLVM backend commit 5cc131030c01e178453b12f235823124aa6a2d12 Author: Evan Haas Date: Tue Dec 29 11:07:04 2020 -0800 Static function declarations with no prototype should not be variadic If a static function is defined with no argument list and no prototype is given, it should be treated as a function that takes no arguments rather than as a variadic function. Fixes #7594 commit 807dc56fd6c9af2603e3db9c1160b0a72fc703c0 Author: Jakub Konka Date: Sun Jan 3 23:20:09 2021 +0100 stage2: add aarch64 stage2 tests Fix missing string format specifier in Mach-O used to generate path to debug symbols bundle. commit 2a410baa2b82528cf2bf8bcbfe0ce030ce972cec Author: Jakub Konka Date: Sun Jan 3 23:01:22 2021 +0100 stage2: implement basic function params aarch64 Implement missing `.register` prong for `aarch64` `genSetReg`. commit 0151f3b76ad9dca6c73d44876c263d5e27d92ab3 Author: Timon Kruiper Date: Sun Jan 3 17:10:28 2021 +0100 stage2: Add support for testing LLVM enabled builds in test-stage2 To make sure that we don't have to rebuild libc for every case, we now have a seperate cache directory for the global cache, which remains the same between test runs. Also make sure to destory the Compilation before executing a child process, otherwise the compiler deadlocks. (#7596) commit a926c91814bbfa29be7f75c7cfeeda0e2d0669d2 Author: Timon Kruiper Date: Sun Jan 3 16:48:52 2021 +0100 stage2: enable building test-stage2 with LLVM backend enabled We can now run `zig build test-stage2 -Denable-llvm`. commit 7e5aacab69b74de23ce57d2c7f3af0061c3343cf Author: Timon Kruiper Date: Sun Jan 3 16:44:53 2021 +0100 stage2: add some missing deallocations in Compilation.zig commit 3c05c60accb534e857e4ad2c1a957d439af184e4 Author: Timon Kruiper Date: Sun Jan 3 16:09:32 2021 +0100 stage2: Output the LLVM object files in the cache directory Also make sure to properly free everything. commit 0008bef1e643c190a12e13d99a21d5af7ebdaa1b Author: Timon Kruiper Date: Sun Jan 3 16:00:12 2021 +0100 stage2: add support for integers in LLVM backend Also adds support for simple operators, like add and subtract. The intcast and bitcast instruction also have been implemented. Linking with libc also works, so we can now generate working executables! `zig build-exe example.zig -fLLVM -lc`: ``` fn add(a: i32, b: i32) i32 { return a + b; } export fn main() c_int { var a: i32 = -5; const x = add(a, 7); var y = add(2, 0); y -= x; return y; } ``` commit e095ebf31254bf5eeba9c07a2ad724e880626f01 Author: Timon Kruiper Date: Tue Dec 29 22:47:52 2020 +0100 stage2: make use of proper LLVM intrinsic APIs in LLVM backend commit da545d6a3195c1cafca498d8a695082982f74676 Author: Timon Kruiper Date: Tue Dec 29 20:39:58 2020 +0100 stage2: implement argument passing and returning in LLVM backend Furthermore add the Not instruction. The following now works: ``` export fn _start() noreturn { var x: bool = true; var other: bool = foo(x); exit(); } fn foo(cond: bool) bool { return !cond; } fn exit() noreturn { unreachable; } ``` commit 47a4d43e41b07b939b840fbf8230b89e27694093 Author: Timon Kruiper Date: Tue Dec 29 20:18:17 2020 +0100 stage2: Add code generation for Load instruction in LLVM backend The following now works: ``` export fn _start() noreturn { var x: bool = true; var y: bool = x; exit(); } fn exit() noreturn { unreachable; } ``` commit 19cfd310b0d5ba7d9542d50db281035b15daad35 Author: Timon Kruiper Date: Tue Dec 29 20:09:08 2020 +0100 stage2: implement register allocation in LLVM self-hosted backend A HashMap has been added which store the LLVM values used in a function. Together with the alloc and store instructions the following now works: ``` export fn _start() noreturn { var x: bool = true; exit(); } fn exit() noreturn { unreachable; } ``` commit a5dab15edea5a82a43481504637ed51655c51680 Author: Timon Kruiper Date: Tue Dec 29 19:03:04 2020 +0100 stage2: clear `err_msg` after it has been added to `module.failed_decls` commit 0ed04aac8bc0cfaa0b2489f7392a6df4e3915b0f Author: Timon Kruiper Date: Tue Dec 29 18:52:53 2020 +0100 stage2: fix building self-hosted compiler with -Dstatic-llvm This supersedes c81ae52ee0b2e952f8ed9c5c6517af8182bb09c1 commit 5aac2fc28111e59a2a05a4fae42b6e19d4e0b7ca Author: Frank Denis Date: Sat Jan 2 20:08:27 2021 +0100 std/crypto: properly support arbitrary output sizes Fixes #7657 commit 683814190bde2340dbecf8e48ae1629900f51306 Merge: d8f3f1453 33e53d729 Author: Andrew Kelley Date: Sat Jan 2 22:05:31 2021 -0800 Merge pull request #7612 from g-w1/do-7296 Implement #7296 commit d8f3f14532c4b5d65377efaef015c3855137dccf Merge: 3d151fbfc 654832253 Author: Andrew Kelley Date: Sat Jan 2 22:01:51 2021 -0800 Merge pull request #7647 from ziglang/stage2-comptime-fn-call stage2: comptime function calls and inline function calls commit 654832253a7857e78aab85e28ed09fb16b632dd2 Author: Andrew Kelley Date: Sat Jan 2 22:42:07 2021 -0700 stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way. commit 3d151fbfc8db71f87ee84dd33c49910584708a04 Author: g-w1 Date: Sat Jan 2 23:11:34 2021 -0500 fix 7665: only add self exe path when testing commit 50a530196ca4e91b387f9937475dd8891edb3f4f Author: Andrew Kelley Date: Sat Jan 2 14:28:03 2021 -0700 stage2: fix handling compile error in inline fn call * scopes properly inherit inlining information * compile errors of inline function calls are properly attached to the caller rather than the callee. - added a test case for this * --watch still opens a repl if compile errors happen. commit 006e7f68056af62ae7713d7ef228841d11874735 Author: Andrew Kelley Date: Sat Jan 2 13:40:23 2021 -0700 stage2: re-use ZIR for comptime and inline calls Instead of freeing ZIR after semantic analysis, we keep it around so that it can be used for comptime calls, inline calls, and generic function calls. ZIR memory is now managed by the Decl arena. Debug dump() functions are conditionally compiled; only available in Debug builds of the compiler. Add a test for an inline function call. commit 9362f382ab7023592cc1d71044217b847b122406 Author: Andrew Kelley Date: Sat Jan 2 12:32:30 2021 -0700 stage2: implement function call inlining in the frontend * remove the -Ddump-zir thing. that's handled through --verbose-ir * rework Fn to have an is_inline flag without requiring any more memory on the heap per function. * implement a rough first version of dumping typed zir (tzir) which is a lot more helpful for debugging than what we had before. We don't have a way to parse it though. * keep track of whether the inline-ness of a function changes because if it does we have to go update callsites. * add compile error for inline and export used together. inline function calls and comptime function calls are implemented the same way. A block instruction is set up to capture the result, and then a scope is set up that has a flag for is_comptime and some state if the scope is being inlined. when analyzing `ret` instructions, zig looks for inlining state in the scope, and if found, treats `ret` as a `break` instruction instead, with the target block being the one set up at the inline callsite. Follow-up items: * Complete out the debug TZIR dumping code. * Don't redundantly generate ZIR for each inline/comptime function call. Instead we should add a new state enum tag to Fn. * comptime and inlining branch quotas. * Add more test cases. commit fea8659b82ea1a785f933c58ba9d65ceb05a4094 Author: Andrew Kelley Date: Fri Jan 1 19:24:02 2021 -0700 stage2: comptime function calls * Function calls that happen in a comptime scope get called at compile-time. We do this by putting the parameters in place as constant values and then running regular function analysis on the body. * Added `Scope.Block.dump()` for debugging purposes. * Fixed some code to call `identifierTokenString` rather than `tokenSlice`, making it work for `@""` syntax. * Implemented `Value.copy` for big integers. Follow-up issues to tackle: * Adding compile errors to the callsite instead of the callee Decl. * Proper error notes for "called from here". - Related: #7555 * Branch quotas. * ZIR support? commit fb37c1b0912c65d72b82f32df8bc7e780ab1ad80 Merge: db1e97d4b 974c008a0 Author: Andrew Kelley Date: Sat Jan 2 19:03:37 2021 -0700 Merge branch 'LemonBoy-revive-6680' closes #6870 commit 974c008a0ee0e0d7933e37d5ea930f712d494f6a Author: Andrew Kelley Date: Sat Jan 2 19:03:14 2021 -0700 convert more {} to {d} and {s} commit 5b981b1be7b387a3f51d60b8642064e6642b956c Author: LemonBoy Date: Sat Jan 2 12:29:37 2021 +0100 Remove some unwanted changes Leftovers after a long rebase. commit 608a73efb12df43fbc136c4439558f87a063dc31 Author: LemonBoy Date: Wed Dec 2 20:02:51 2020 +0100 Decrement max_depth when printing slice elements commit 04f37dcd8e9c32d7ce8712939fb9c8dfa6cc6bbe Author: LemonBoy Date: Thu Nov 26 22:20:44 2020 +0100 stage2: Use {z} instead of {s} in generated Zig code commit 1fbe89dc2e204defc18add72efd2bba4dbe728fc Author: LemonBoy Date: Thu Nov 26 19:14:22 2020 +0100 langref: Update langref to use {s} commit d2f6fa1608f31cbd7137f81b5dd6df2977766eae Author: LemonBoy Date: Thu Nov 26 17:06:52 2020 +0100 Fix more stray uses of {} for formatting strings commit 1ca2deca33e5ee489e12bd71a34ec3e4a2e1255f Author: LemonBoy Date: Thu Nov 26 15:43:28 2020 +0100 std: Disable the special casing of {} for u8 slices/arrays Unless {s} is specified the contents won't be treated as a string. commit 4420afe64d5ae03565b51dcec55ce9dd7351d0ee Author: LemonBoy Date: Thu Nov 26 13:28:38 2020 +0100 tests: Use {s} instead of {} when formatting strings commit 1c13ca5a05978011283ff55a586443b10b69fc85 Author: LemonBoy Date: Thu Nov 26 13:19:30 2020 +0100 stage2: Use {s} instead of {} when formatting strings commit dd973fb365dbbe11ce5beac8b4889bfab3fddc4d Author: LemonBoy Date: Thu Nov 26 09:48:12 2020 +0100 std: Use {s} instead of {} when printing strings commit 5a06fdfa5525920810005e73eaa1b6e79a6472ca Author: LemonBoy Date: Thu Nov 19 19:02:30 2020 +0100 Use same brace pairs for arrays/slices/vectors commit d4a8fc8b67bd2ae26b997ee201545cbb1eb3c15f Author: LemonBoy Date: Sat Oct 31 15:16:59 2020 +0100 Small cleanup commit 2b5e93fd3ea7ced934c7310ce68177738b1cdb81 Author: data-man Date: Sat Oct 31 15:12:05 2020 +0100 Add formatting for arrays commit 6f53653db17933fe2c8502d5bfc60857097b8bff Author: LemonBoy Date: Thu Oct 29 22:22:25 2020 +0100 std: Refactor the slice formatting code Also fix the `*` specifier for more types, print an error message if we can't show the value address. commit 5275280ede72fbc3eb702ff7f01fac07b306ca46 Author: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Wed Oct 14 18:45:46 2020 +0200 Formatting fix commit 1d97747665b993eb0c625c8d0a28e2d0c8e167a3 Author: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Wed Oct 14 18:38:59 2020 +0200 Pretty print Slices This code is adapted from pixelherodev paste from IRC I have added a new fmt option to handle printing slice values ``{v}`` or ``{V}`` While i think it can be made the default, i want your opinion about it ```zig var slicea = [0]u32{}; var sliceb = [3]u32{ 1, 2, 3 }; std.log.info("Content: {v}", .{slicea}); std.log.info("Content: {v}", .{sliceb}); ``` will print: ``` info: Content: [] info: Content: [1, 2, 3] ``` Question: Should we drop ``{v}`` and make it the default behavior? commit db1e97d4b19d8399252e0fbc85fc3563b005a892 Author: Cameron Conn Date: Sat Jan 2 18:06:51 2021 -0600 Improve documentation for ArrayList, ArrayListUnmanaged, etc. (#7624) * Improve ArrayList & co documentation - Added doc comments about the validity of references to elements in an ArrayList and how they may become invalid after resizing operations. - This should help users avoid footguns in future. * Improve ArrayListUnmanaged & co's documentation - Port improved documentation from ArrayList and ArrayList aligned to their unmanaged counterparts. - Made documentation for ArrayListUnmanaged & co more inclusive and up-to-date. - Made documentation more consistent with `ArrayList`. * Corrections on ArrayList documentation. - Remove incorrect/unpreferred wording on ArrayList vs ArrayListUnmanaged. - Fix notes about the alignment of ArrayListAligned - Be more verbose with warnings on when pointers are invalidated. - Copy+paste a few warnings * add warning to replaceRange * revert changes to append documentation commit 1856dfea6b797d852b496c2111dbb326dbe2957e Author: LemonBoy Date: Fri Jan 1 19:33:53 2021 +0100 stage1: Use correct format specifier for size_t parameters Use `Iu` on Windows, the integer width depends on the target being a 32bit or a 64bit one. commit af8eab546ef5bafc229598440200681f0fa5a1e8 Author: Sizhe Zhao Date: Sat Jan 2 22:50:49 2021 +0800 Fix usage message commit 44c9bf559bd99899e7858cc37930f91a9de932d2 Author: Andrew Kelley Date: Sat Jan 2 12:21:19 2021 -0700 std: disable a couple tests on windows They are passing but we're hitting OOM on the Windows CI server. This is to buy us more time until stage2 rescues us from the CI memory crisis. commit 5a6579611f3040015fc788d249de504f54dabfd5 Merge: a9c75a2b4 763d80737 Author: Jakub Konka Date: Sat Jan 2 20:08:37 2021 +0100 Merge pull request #7506 from kubkon/fix-6923 zig cc: detect framework include paths when native commit 763d8073774ae0b77014187a75b2167522c44079 Author: Jakub Konka Date: Sun Dec 20 11:52:25 2020 +0100 Duplicate OSAtomic.h between aarch64 and x86_64 The reason this is required is for two reasons: 1) the libc targeting `aarch64` macOS is slightly newer than that targeting `x86_64`, and 2) `OSAtomic.h` uses relative imports rather than system-wide imports for accompanying headers which clearly is an oversight on Apple's part. Until such time when `libkern` headers between `x86_64` and `aarch64` are identical, this will require a manual intervention to duplicate the relevant headers between the respective architectures. commit 91a35e1a922e865ca0e9beb43a2be2c5552fc494 Author: Jakub Konka Date: Sun Dec 20 11:45:48 2020 +0100 Detect native iframework dirs on macOS This commit adds default search paths for system frameworks on macOS while also adding `-isysroot` for OS versions at least BigSur. Since BigSur (11.0.1), neither headers nor libs exist in standard root locations (`/usr/include`, `/System/Library/Frameworks`). Instead, they are now exclusively part of the installed developer toolchain (either via XCode.app or CLT), and specifying `-isysroot` allows us to keep using universal search paths such as `/System/Library/Frameworks` while only changing the include flag from `-iframework` to `-iframeworkwithsysroot`. commit 33e53d729493bdfc76726dd2d247e1998e321930 Author: g-w1 Date: Thu Dec 31 10:21:58 2020 -0500 update .gitignore to include /release/ and /debug/ commit a1a1929cf4ff979bdaba17f858d4ce8647e87a65 Author: g-w1 Date: Wed Dec 30 21:04:27 2020 -0500 Fix #7296: * makes '$build_root/{install,debug}/' the default prefix. This makes it not '$pwd/zig-cache/'. commit 2561168adbe642287c1eccd73fc29576ff0216f3 Author: LemonBoy Date: Tue Dec 29 13:00:01 2020 +0100 std: Clean up some tests No functional changes, remove some dead code. commit 88634f0481fb1bd78f8d018b76cab6590691c8a9 Author: LemonBoy Date: Tue Dec 29 12:58:47 2020 +0100 stage1: Allow variable capture for multi-prong switch arms Handle the multi-prong case as we do with range cases. Closes #7188 --- .gitignore | 2 + CMakeLists.txt | 7 + build.zig | 221 +++---- ci/azure/windows_msvc_script.bat | 5 +- ci/drone/linux_script | 2 +- doc/docgen.zig | 108 ++-- doc/langref.html.in | 42 +- .../libkern/OSAtomic.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../x86_64-macos-gnu/libkern/OSAtomic.h | 47 ++ .../libkern/OSSpinLockDeprecated.h | 212 +++++++ lib/std/SemanticVersion.zig | 10 +- lib/std/array_list.zig | 142 +++-- lib/std/build.zig | 138 ++--- lib/std/build/check_file.zig | 4 +- lib/std/build/emit_raw.zig | 2 +- lib/std/build/run.zig | 26 +- lib/std/build/write_file.zig | 4 +- lib/std/builtin.zig | 14 +- lib/std/c.zig | 12 +- lib/std/c/ast.zig | 8 +- lib/std/c/tokenizer.zig | 6 +- lib/std/crypto.zig | 3 + lib/std/crypto/bcrypt.zig | 2 +- lib/std/crypto/blake2.zig | 190 +++++- lib/std/debug.zig | 14 +- lib/std/dwarf.zig | 10 +- lib/std/fifo.zig | 2 +- lib/std/fmt.zig | 162 ++++-- lib/std/fs/wasi.zig | 2 +- lib/std/hash/crc.zig | 10 + lib/std/heap/general_purpose_allocator.zig | 8 +- lib/std/io/fixed_buffer_stream.zig | 2 +- lib/std/json.zig | 8 +- lib/std/linked_list.zig | 6 +- lib/std/meta/trait.zig | 14 + lib/std/net.zig | 2 +- lib/std/os.zig | 5 +- lib/std/os/bits/linux.zig | 51 +- lib/std/os/linux/io_uring.zig | 10 +- lib/std/os/windows.zig | 2 +- lib/std/process.zig | 2 +- lib/std/rand/ziggurat.zig | 14 +- lib/std/special/build_runner.zig | 14 +- lib/std/special/c.zig | 2 +- lib/std/special/compiler_rt.zig | 2 +- lib/std/special/test_runner.zig | 16 +- lib/std/start.zig | 6 +- lib/std/std.zig | 38 +- lib/std/target.zig | 12 +- lib/std/testing.zig | 16 +- lib/std/thread.zig | 6 +- lib/std/zig/ast.zig | 84 +-- lib/std/zig/cross_target.zig | 10 +- lib/std/zig/parser_test.zig | 6 +- lib/std/zig/render.zig | 2 +- lib/std/zig/system.zig | 45 +- lib/std/zig/system/macos.zig | 4 +- lib/std/zig/tokenizer.zig | 4 +- src/Cache.zig | 63 +- src/Compilation.zig | 136 +++-- src/DepTokenizer.zig | 10 +- src/Module.zig | 331 +++++++---- src/astgen.zig | 71 ++- src/codegen.zig | 124 +++- src/codegen/c.zig | 18 +- src/codegen/wasm.zig | 2 +- src/config.zig.in | 2 +- src/glibc.zig | 42 +- src/libc_installation.zig | 32 +- src/link.zig | 10 +- src/link/C.zig | 2 +- src/link/Coff.zig | 17 +- src/link/Elf.zig | 39 +- src/link/MachO.zig | 26 +- src/link/MachO/DebugSymbols.zig | 10 - src/link/Wasm.zig | 6 +- src/liveness.zig | 3 +- src/llvm_backend.zig | 295 +++++++--- src/llvm_bindings.zig | 48 ++ src/main.zig | 290 +++++----- src/mingw.zig | 4 +- src/musl.zig | 8 +- src/print_env.zig | 2 +- src/print_targets.zig | 4 +- src/stage1.zig | 4 +- src/stage1/ir.cpp | 2 + src/stage1/os.hpp | 2 +- src/test.zig | 45 +- src/translate_c.zig | 94 +-- src/type.zig | 8 +- src/value.zig | 17 +- src/zig_clang.h | 20 +- src/zir.zig | 427 +++++++++++--- src/zir_sema.zig | 541 ++++++++++++++---- test/compare_output.zig | 4 +- test/run_translated_c.zig | 46 ++ test/src/compare_output.zig | 6 +- test/src/run_translated_c.zig | 8 +- test/src/translate_c.zig | 2 +- test/stage1/behavior.zig | 2 +- test/stage1/behavior/async_fn.zig | 3 +- test/stage1/behavior/switch.zig | 20 + test/stage1/behavior/union.zig | 4 +- test/stage2/aarch64.zig | 44 ++ test/stage2/cbe.zig | 15 + test/stage2/llvm_backend.zig | 30 + test/stage2/test.zig | 151 ++++- test/stage2/zir.zig | 316 ---------- test/tests.zig | 62 +- test/translate_c.zig | 10 +- 111 files changed, 3504 insertions(+), 1772 deletions(-) rename lib/libc/include/{any-macos-any => aarch64-macos-gnu}/libkern/OSAtomic.h (100%) rename lib/libc/include/{any-macos-any => aarch64-macos-gnu}/libkern/OSSpinLockDeprecated.h (100%) create mode 100644 lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h create mode 100644 lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h create mode 100644 test/stage2/llvm_backend.zig delete mode 100644 test/stage2/zir.zig diff --git a/.gitignore b/.gitignore index f85fc969b129..9fa6f71cc793 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ # -andrewrk zig-cache/ +/release/ +/debug/ /build/ /build-*/ /docgen_tmp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 291a2f7839b8..2ed25b93d215 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") set(ZIG_PREFER_LLVM_CONFIG off CACHE BOOL "(when cross compiling) use llvm-config to find target llvm dependencies if needed") set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") +set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1") find_package(llvm) find_package(clang) @@ -587,6 +588,12 @@ if(MSVC) endif() endif() +if(ZIG_OMIT_STAGE2) + set(ZIG_OMIT_STAGE2_BOOL "true") +else() + set(ZIG_OMIT_STAGE2_BOOL "false") +endif() + configure_file ( "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" "${ZIG_CONFIG_H_OUT}" diff --git a/build.zig b/build.zig index fe588ea2cb35..77d1b573fdf6 100644 --- a/build.zig +++ b/build.zig @@ -91,6 +91,7 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(bool, "have_llvm", enable_llvm); if (enable_llvm) { const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); + if (is_stage1) { exe.addIncludeDir("src"); exe.addIncludeDir("deps/SoftFloat-3e/source/include"); @@ -109,28 +110,8 @@ pub fn build(b: *Builder) !void { softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" }); exe.linkLibrary(softfloat); - const exe_cflags = [_][]const u8{ - "-std=c++14", - "-D__STDC_CONSTANT_MACROS", - "-D__STDC_FORMAT_MACROS", - "-D__STDC_LIMIT_MACROS", - "-D_GNU_SOURCE", - "-fvisibility-inlines-hidden", - "-fno-exceptions", - "-fno-rtti", - "-Werror=type-limits", - "-Wno-missing-braces", - "-Wno-comment", - }; exe.addCSourceFiles(&stage1_sources, &exe_cflags); exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" }); - if (cmake_cfg == null) { - // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling - // in a dependency on llvm::cfg::Update::dump() which is - // unavailable when LLVM is compiled in Release mode. - const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"}; - exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags); - } } if (cmake_cfg) |cfg| { // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD. @@ -139,79 +120,13 @@ pub fn build(b: *Builder) !void { if (cfg.cmake_prefix_path.len > 0) { b.addSearchPrefix(cfg.cmake_prefix_path); } - exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ - cfg.cmake_binary_dir, - "zigcpp", - b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }), - }) catch unreachable); - assert(cfg.lld_include_dir.len != 0); - exe.addIncludeDir(cfg.lld_include_dir); - addCMakeLibraryList(exe, cfg.clang_libraries); - addCMakeLibraryList(exe, cfg.lld_libraries); - addCMakeLibraryList(exe, cfg.llvm_libraries); - - const need_cpp_includes = tracy != null; - - // System -lc++ must be used because in this code path we are attempting to link - // against system-provided LLVM, Clang, LLD. - if (exe.target.getOsTag() == .linux) { - // First we try to static link against gcc libstdc++. If that doesn't work, - // we fall back to -lc++ and cross our fingers. - addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { - error.RequiredLibraryNotFound => { - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - }; - - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isFreeBSD()) { - try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); - exe.linkSystemLibrary("pthread"); - } else if (exe.target.getOsTag() == .openbsd) { - try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); - try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); - } else if (exe.target.isDarwin()) { - if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) { - // Compiler is GCC. - try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes); - exe.linkSystemLibrary("pthread"); - // TODO LLD cannot perform this link. - // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. - // See https://github.com/ziglang/zig/issues/1535 - } else |err| switch (err) { - error.RequiredLibraryNotFound => { - // System compiler, not gcc. - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - } - } - if (cfg.dia_guids_lib.len != 0) { - exe.addObjectFile(cfg.dia_guids_lib); - } + try addCmakeCfgOptionsToExe(b, cfg, tracy, exe); + try addCmakeCfgOptionsToExe(b, cfg, tracy, test_stage2); } else { // Here we are -Denable-llvm but no cmake integration. - for (clang_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - for (lld_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - for (llvm_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. - exe.linkSystemLibrary("c++"); - - if (target.getOs().tag == .windows) { - exe.linkSystemLibrary("version"); - exe.linkSystemLibrary("uuid"); - } + try addStaticLlvmOptionsToExe(exe); + try addStaticLlvmOptionsToExe(test_stage2); } } if (link_libc) { @@ -220,11 +135,10 @@ pub fn build(b: *Builder) !void { } const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; - const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); const version = if (opt_version_string) |version| version else v: { - const version_string = b.fmt("{}.{}.{}", .{ zig_version.major, zig_version.minor, zig_version.patch }); + const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch }); var code: u8 = undefined; const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{ @@ -238,7 +152,7 @@ pub fn build(b: *Builder) !void { 0 => { // Tagged release version (e.g. 0.7.0). if (!mem.eql(u8, git_describe, version_string)) { - std.debug.print("Zig version '{}' does not match Git tag '{}'\n", .{ version_string, git_describe }); + std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe }); std.process.exit(1); } break :v version_string; @@ -258,15 +172,15 @@ pub fn build(b: *Builder) !void { // Check that the commit hash is prefixed with a 'g' (a Git convention). if (commit_id.len < 1 or commit_id[0] != 'g') { - std.debug.print("Unexpected `git describe` output: {}\n", .{git_describe}); + std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); break :v version_string; } // The version is reformatted in accordance with the https://semver.org specification. - break :v b.fmt("{}-dev.{}+{}", .{ version_string, commit_height, commit_id[1..] }); + break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] }); }, else => { - std.debug.print("Unexpected `git describe` output: {}\n", .{git_describe}); + std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); break :v version_string; }, } @@ -277,9 +191,9 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(std.SemanticVersion, "semver", semver); exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); - exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); exe.addBuildOption(bool, "enable_tracy", tracy != null); exe.addBuildOption(bool, "is_stage1", is_stage1); + exe.addBuildOption(bool, "omit_stage2", false); if (tracy) |tracy_path| { const client_cpp = fs.path.join( b.allocator, @@ -302,6 +216,7 @@ pub fn build(b: *Builder) !void { test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native); test_stage2.addBuildOption(bool, "is_stage1", is_stage1); + test_stage2.addBuildOption(bool, "omit_stage2", false); test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); @@ -359,6 +274,112 @@ pub fn build(b: *Builder) !void { test_step.dependOn(docs_step); } +const exe_cflags = [_][]const u8{ + "-std=c++14", + "-D__STDC_CONSTANT_MACROS", + "-D__STDC_FORMAT_MACROS", + "-D__STDC_LIMIT_MACROS", + "-D_GNU_SOURCE", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-rtti", + "-Werror=type-limits", + "-Wno-missing-braces", + "-Wno-comment", +}; + +fn addCmakeCfgOptionsToExe( + b: *Builder, + cfg: CMakeConfig, + tracy: ?[]const u8, + exe: *std.build.LibExeObjStep, +) !void { + exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ + cfg.cmake_binary_dir, + "zigcpp", + b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }), + }) catch unreachable); + assert(cfg.lld_include_dir.len != 0); + exe.addIncludeDir(cfg.lld_include_dir); + addCMakeLibraryList(exe, cfg.clang_libraries); + addCMakeLibraryList(exe, cfg.lld_libraries); + addCMakeLibraryList(exe, cfg.llvm_libraries); + + const need_cpp_includes = tracy != null; + + // System -lc++ must be used because in this code path we are attempting to link + // against system-provided LLVM, Clang, LLD. + if (exe.target.getOsTag() == .linux) { + // First we try to static link against gcc libstdc++. If that doesn't work, + // we fall back to -lc++ and cross our fingers. + addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { + error.RequiredLibraryNotFound => { + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + }; + + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isFreeBSD()) { + try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + } else if (exe.target.getOsTag() == .openbsd) { + try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); + try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); + } else if (exe.target.isDarwin()) { + if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) { + // Compiler is GCC. + try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + // TODO LLD cannot perform this link. + // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. + // See https://github.com/ziglang/zig/issues/1535 + } else |err| switch (err) { + error.RequiredLibraryNotFound => { + // System compiler, not gcc. + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + } + } + + if (cfg.dia_guids_lib.len != 0) { + exe.addObjectFile(cfg.dia_guids_lib); + } +} + +fn addStaticLlvmOptionsToExe( + exe: *std.build.LibExeObjStep, +) !void { + // Adds the Zig C++ sources which both stage1 and stage2 need. + // + // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling + // in a dependency on llvm::cfg::Update::dump() which is + // unavailable when LLVM is compiled in Release mode. + const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"}; + exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags); + + for (clang_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + for (lld_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + for (llvm_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. + exe.linkSystemLibrary("c++"); + + if (exe.target.getOs().tag == .windows) { + exe.linkSystemLibrary("version"); + exe.linkSystemLibrary("uuid"); + } +} + fn addCxxKnownPath( b: *Builder, ctx: CMakeConfig, @@ -369,14 +390,14 @@ fn addCxxKnownPath( ) !void { const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, - b.fmt("-print-file-name={}", .{objname}), + b.fmt("-print-file-name={s}", .{objname}), }); const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { - warn("{}", .{msg}); + warn("{s}", .{msg}); } else { - warn("Unable to determine path to {}\n", .{objname}); + warn("Unable to determine path to {s}\n", .{objname}); } return error.RequiredLibraryNotFound; } diff --git a/ci/azure/windows_msvc_script.bat b/ci/azure/windows_msvc_script.bat index cd5e3d5bca39..9d28eccd0bba 100644 --- a/ci/azure/windows_msvc_script.bat +++ b/ci/azure/windows_msvc_script.bat @@ -23,11 +23,12 @@ git.exe fetch --tags mkdir %ZIGBUILDDIR% cd %ZIGBUILDDIR% -cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b +cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release -DZIG_OMIT_STAGE2=ON || exit /b msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-behavior -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-stage2 -Dskip-non-native || exit /b +REM Disabled to prevent OOM +REM "%ZIGINSTALLDIR%\bin\zig.exe" build test-stage2 -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-fmt -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-std -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-compiler-rt -Dskip-non-native || exit /b diff --git a/ci/drone/linux_script b/ci/drone/linux_script index 422949e60752..dbe13f6f19a3 100755 --- a/ci/drone/linux_script +++ b/ci/drone/linux_script @@ -22,7 +22,7 @@ cd build cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja samu install -./zig build test -Dskip-release -Dskip-non-native +./zig build test -Dskip-release -Dskip-non-native -Dskip-compile-errors if [ -z "$DRONE_PULL_REQUEST" ]; then mv ../LICENSE "$DISTDIR/" diff --git a/doc/docgen.zig b/doc/docgen.zig index e995e2f3241c..7e0bedbb6c61 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -215,9 +215,9 @@ const Tokenizer = struct { fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror { const loc = tokenizer.getTokenLocation(token); const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 }; - print("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); + print("{s}:{d}:{d}: error: " ++ fmt ++ "\n", args_prefix ++ args); if (loc.line_start <= loc.line_end) { - print("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); + print("{s}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { @@ -238,7 +238,7 @@ fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, arg fn assertToken(tokenizer: *Tokenizer, token: Token, id: Token.Id) !void { if (token.id != id) { - return parseError(tokenizer, token, "expected {}, found {}", .{ @tagName(id), @tagName(token.id) }); + return parseError(tokenizer, token, "expected {s}, found {s}", .{ @tagName(id), @tagName(token.id) }); } } @@ -374,7 +374,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, bracket_tok, - "unrecognized header_open param: {}", + "unrecognized header_open param: {s}", .{param}, ); } @@ -394,7 +394,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { }, }); if (try urls.fetchPut(urlized, tag_token)) |entry| { - parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; + parseError(tokenizer, tag_token, "duplicate header url: #{s}", .{urlized}) catch {}; parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; return error.ParseError; } @@ -411,7 +411,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { } last_columns = columns; try toc.writeByteNTimes(' ', 4 + header_stack_size * 4); - try toc.print("
  • {}", .{ urlized, urlized, content }); + try toc.print("
  • {s}", .{ urlized, urlized, content }); } else if (mem.eql(u8, tag_name, "header_close")) { if (header_stack_size == 0) { return parseError(tokenizer, tag_token, "unbalanced close header", .{}); @@ -515,7 +515,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { code_kind_id = Code.Id{ .Obj = null }; is_inline = true; } else { - return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", .{code_kind_str}); + return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str}); } var mode: builtin.Mode = .Debug; @@ -559,7 +559,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, end_code_tag, - "invalid token inside code_begin: {}", + "invalid token inside code_begin: {s}", .{end_tag_name}, ); } @@ -590,14 +590,14 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, end_syntax_tag, - "invalid token inside syntax: {}", + "invalid token inside syntax: {s}", .{end_tag_name}, ); } _ = try eatToken(tokenizer, Token.Id.BracketClose); try nodes.append(Node{ .Syntax = content_tok }); } else { - return parseError(tokenizer, tag_token, "unrecognized tag name: {}", .{tag_name}); + return parseError(tokenizer, tag_token, "unrecognized tag name: {s}", .{tag_name}); } }, else => return parseError(tokenizer, token, "invalid token", .{}), @@ -744,7 +744,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { try out.writeAll(""); } if (first_number != 0 or second_number != 0) { - try out.print("", .{ first_number, second_number }); + try out.print("", .{ first_number, second_number }); open_span_count += 1; } }, @@ -1004,9 +1004,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .Link => |info| { if (!toc.urls.contains(info.url)) { - return parseError(tokenizer, info.token, "url not found: {}", .{info.url}); + return parseError(tokenizer, info.token, "url not found: {s}", .{info.url}); } - try out.print("{}", .{ info.url, info.name }); + try out.print("{s}", .{ info.url, info.name }); }, .Nav => { try out.writeAll(toc.toc); @@ -1018,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .HeaderOpen => |info| { try out.print( - "{} §\n", + "{s} §\n", .{ info.n, info.url, info.url, info.name, info.url, info.n }, ); }, @@ -1027,9 +1027,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any for (items) |item| { const url = try urlize(allocator, item.name); if (!toc.urls.contains(url)) { - return parseError(tokenizer, item.token, "url not found: {}", .{url}); + return parseError(tokenizer, item.token, "url not found: {s}", .{url}); } - try out.print("
  • {}
  • \n", .{ url, item.name }); + try out.print("
  • {s}
  • \n", .{ url, item.name }); } try out.writeAll("\n"); }, @@ -1043,12 +1043,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); if (!code.is_inline) { - try out.print("

    {}.zig

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

    {s}.zig

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

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

    - The two arguments passed to the stdout.print() function, "Hello, {}!\n" + The two arguments passed to the stdout.print() function, "Hello, {s}!\n" and .{"world"}, are evaluated at {#link|compile-time|comptime#}. The code sample is purposely written to show how to perform {#link|string|String Literals and Character Literals#} substitution in the print function. The curly-braces inside of the first argument @@ -435,7 +435,7 @@ pub fn main() void { var optional_value: ?[]const u8 = null; assert(optional_value == null); - print("\noptional 1\ntype: {}\nvalue: {}\n", .{ + print("\noptional 1\ntype: {s}\nvalue: {s}\n", .{ @typeName(@TypeOf(optional_value)), optional_value, }); @@ -443,7 +443,7 @@ pub fn main() void { optional_value = "hi"; assert(optional_value != null); - print("\noptional 2\ntype: {}\nvalue: {}\n", .{ + print("\noptional 2\ntype: {s}\nvalue: {s}\n", .{ @typeName(@TypeOf(optional_value)), optional_value, }); @@ -451,14 +451,14 @@ pub fn main() void { // error union var number_or_error: anyerror!i32 = error.ArgNotFound; - print("\nerror union 1\ntype: {}\nvalue: {}\n", .{ + print("\nerror union 1\ntype: {s}\nvalue: {}\n", .{ @typeName(@TypeOf(number_or_error)), number_or_error, }); number_or_error = 1234; - print("\nerror union 2\ntype: {}\nvalue: {}\n", .{ + print("\nerror union 2\ntype: {s}\nvalue: {}\n", .{ @typeName(@TypeOf(number_or_error)), number_or_error, }); @@ -2339,7 +2339,7 @@ test "using slices for strings" { // You can use slice syntax on an array to convert an array into a slice. const all_together_slice = all_together[0..]; // String concatenation example. - const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world }); + const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world }); // Generally, you can use UTF-8 and not worry about whether something is a // string. If you don't need to deal with individual characters, no need @@ -2772,9 +2772,9 @@ const std = @import("std"); pub fn main() void { const Foo = struct {}; - std.debug.print("variable: {}\n", .{@typeName(Foo)}); - std.debug.print("anonymous: {}\n", .{@typeName(struct {})}); - std.debug.print("function: {}\n", .{@typeName(List(i32))}); + std.debug.print("variable: {s}\n", .{@typeName(Foo)}); + std.debug.print("anonymous: {s}\n", .{@typeName(struct {})}); + std.debug.print("function: {s}\n", .{@typeName(List(i32))}); } fn List(comptime T: type) type { @@ -6110,7 +6110,7 @@ const a_number: i32 = 1234; const a_string = "foobar"; pub fn main() void { - print("here is a string: '{}' here is a number: {}\n", .{a_string, a_number}); + print("here is a string: '{s}' here is a number: {}\n", .{a_string, a_number}); } {#code_end#} @@ -6230,7 +6230,7 @@ const a_number: i32 = 1234; const a_string = "foobar"; test "printf too many arguments" { - print("here is a string: '{}' here is a number: {}\n", .{ + print("here is a string: '{s}' here is a number: {}\n", .{ a_string, a_number, a_number, @@ -6249,7 +6249,7 @@ const print = @import("std").debug.print; const a_number: i32 = 1234; const a_string = "foobar"; -const fmt = "here is a string: '{}' here is a number: {}\n"; +const fmt = "here is a string: '{s}' here is a number: {}\n"; pub fn main() void { print(fmt, .{a_string, a_number}); @@ -6720,8 +6720,8 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.print("download_text: {}\n", .{download_text}); - std.debug.print("file_text: {}\n", .{file_text}); + std.debug.print("download_text: {s}\n", .{download_text}); + std.debug.print("file_text: {s}\n", .{file_text}); } var global_download_frame: anyframe = undefined; @@ -6790,8 +6790,8 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.print("download_text: {}\n", .{download_text}); - std.debug.print("file_text: {}\n", .{file_text}); + std.debug.print("download_text: {s}\n", .{download_text}); + std.debug.print("file_text: {s}\n", .{file_text}); } fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 { @@ -8848,7 +8848,7 @@ pub fn main() !void { var byte: u8 = 255; byte = if (math.add(u8, byte, 1)) |result| result else |err| { - print("unable to add one: {}\n", .{@errorName(err)}); + print("unable to add one: {s}\n", .{@errorName(err)}); return err; }; @@ -9078,7 +9078,7 @@ pub fn main() void { if (result) |number| { print("got number: {}\n", .{number}); } else |err| { - print("got error: {}\n", .{@errorName(err)}); + print("got error: {s}\n", .{@errorName(err)}); } } @@ -9135,7 +9135,7 @@ const Foo = enum { pub fn main() void { var a: u2 = 3; var b = @intToEnum(Foo, a); - std.debug.print("value: {}\n", .{@tagName(b)}); + std.debug.print("value: {s}\n", .{@tagName(b)}); } {#code_end#} {#header_close#} @@ -10025,7 +10025,7 @@ pub fn main() !void { defer std.process.argsFree(gpa, args); for (args) |arg, i| { - std.debug.print("{}: {}\n", .{ i, arg }); + std.debug.print("{}: {s}\n", .{ i, arg }); } } {#code_end#} diff --git a/lib/libc/include/any-macos-any/libkern/OSAtomic.h b/lib/libc/include/aarch64-macos-gnu/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/any-macos-any/libkern/OSAtomic.h rename to lib/libc/include/aarch64-macos-gnu/libkern/OSAtomic.h diff --git a/lib/libc/include/any-macos-any/libkern/OSSpinLockDeprecated.h b/lib/libc/include/aarch64-macos-gnu/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/any-macos-any/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/aarch64-macos-gnu/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h b/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h new file mode 100644 index 000000000000..37ef16ce4454 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _OSATOMIC_H_ +#define _OSATOMIC_H_ + +/*! @header + * These are deprecated legacy interfaces for atomic and synchronization + * operations. + * + * Define OSATOMIC_USE_INLINED=1 to get inline implementations of the + * OSAtomic interfaces in terms of the primitives. + * + * Define OSSPINLOCK_USE_INLINED=1 to get inline implementations of the + * OSSpinLock interfaces in terms of the primitives. + * + * These are intended as a transition convenience, direct use of those + * primitives should be preferred. + */ + +#include + +#include "OSAtomicDeprecated.h" +#include "OSSpinLockDeprecated.h" +#include "OSAtomicQueue.h" + +#endif /* _OSATOMIC_H_ */ \ No newline at end of file diff --git a/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h new file mode 100644 index 000000000000..a654a7bbc2d8 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2004-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _OSSPINLOCK_DEPRECATED_H_ +#define _OSSPINLOCK_DEPRECATED_H_ + +/*! @header + * These are deprecated legacy interfaces for userspace spinlocks. + * + * These interfaces should no longer be used, particularily in situations where + * threads of differing priorities may contend on the same spinlock. + * + * The interfaces in should be used instead in cases where a very + * low-level lock primitive is required. In general however, using higher level + * synchronization primitives such as those provided by the pthread or dispatch + * subsystems should be preferred. + * + * Define OSSPINLOCK_USE_INLINED=1 to get inline implementations of these + * interfaces in terms of the primitives. This is intended as a + * transition convenience, direct use of those primitives is preferred. + */ + +#ifndef OSSPINLOCK_DEPRECATED +#define OSSPINLOCK_DEPRECATED 1 +#define OSSPINLOCK_DEPRECATED_MSG(_r) "Use " #_r "() from instead" +#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) \ + __OS_AVAILABILITY_MSG(macosx, deprecated=10.12, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(ios, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(tvos, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(watchos, deprecated=3.0, OSSPINLOCK_DEPRECATED_MSG(_r)) +#else +#undef OSSPINLOCK_DEPRECATED +#define OSSPINLOCK_DEPRECATED 0 +#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) +#endif + +#if !(defined(OSSPINLOCK_USE_INLINED) && OSSPINLOCK_USE_INLINED) + +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +/*! @abstract The default value for an OSSpinLock. + @discussion + The convention is that unlocked is zero, locked is nonzero. + */ +#define OS_SPINLOCK_INIT 0 + + +/*! @abstract Data type for a spinlock. + @discussion + You should always initialize a spinlock to {@link OS_SPINLOCK_INIT} before + using it. + */ +typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); + + +/*! @abstract Locks a spinlock if it would not block + @result + Returns false if the lock was already held by another thread, + true if it took the lock successfully. + */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +bool OSSpinLockTry( volatile OSSpinLock *__lock ); + + +/*! @abstract Locks a spinlock + @discussion + Although the lock operation spins, it employs various strategies to back + off if the lock is held. + */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +void OSSpinLockLock( volatile OSSpinLock *__lock ); + + +/*! @abstract Unlocks a spinlock */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +void OSSpinLockUnlock( volatile OSSpinLock *__lock ); + +__END_DECLS + +#else /* OSSPINLOCK_USE_INLINED */ + +/* + * Inline implementations of the legacy OSSpinLock interfaces in terms of the + * of the primitives. Direct use of those primitives is preferred. + * + * NOTE: the locked value of os_unfair_lock is implementation defined and + * subject to change, code that relies on the specific locked value used by the + * legacy OSSpinLock interface WILL break when using these inline + * implementations in terms of os_unfair_lock. + */ + +#if !OSSPINLOCK_USE_INLINED_TRANSPARENT + +#include + +__BEGIN_DECLS + +#if __has_attribute(always_inline) +#define OSSPINLOCK_INLINE static __inline +#else +#define OSSPINLOCK_INLINE static __inline __attribute__((__always_inline__)) +#endif + +#define OS_SPINLOCK_INIT 0 +typedef int32_t OSSpinLock; + +#if __has_extension(c_static_assert) +_Static_assert(sizeof(OSSpinLock) == sizeof(os_unfair_lock), + "Incompatible os_unfair_lock type"); +#endif + +OSSPINLOCK_INLINE +void +OSSpinLockLock(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_lock(lock); +} + +OSSPINLOCK_INLINE +bool +OSSpinLockTry(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_trylock(lock); +} + +OSSPINLOCK_INLINE +void +OSSpinLockUnlock(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_unlock(lock); +} + +#undef OSSPINLOCK_INLINE + +__END_DECLS + +#else /* OSSPINLOCK_USE_INLINED_TRANSPARENT */ + +#include +#include +#include +#include +#include + +#define OS_NOSPIN_LOCK_AVAILABILITY \ + __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) \ + __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) + +__BEGIN_DECLS + +#define OS_SPINLOCK_INIT 0 +typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); +typedef volatile OSSpinLock *_os_nospin_lock_t + OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_t); + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock) +OS_NOSPIN_LOCK_AVAILABILITY +void _os_nospin_lock_lock(_os_nospin_lock_t lock); +#undef OSSpinLockLock +#define OSSpinLockLock(lock) _os_nospin_lock_lock(lock) + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock) +OS_NOSPIN_LOCK_AVAILABILITY +bool _os_nospin_lock_trylock(_os_nospin_lock_t lock); +#undef OSSpinLockTry +#define OSSpinLockTry(lock) _os_nospin_lock_trylock(lock) + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock) +OS_NOSPIN_LOCK_AVAILABILITY +void _os_nospin_lock_unlock(_os_nospin_lock_t lock); +#undef OSSpinLockUnlock +#define OSSpinLockUnlock(lock) _os_nospin_lock_unlock(lock) + +__END_DECLS + +#endif /* OSSPINLOCK_USE_INLINED_TRANSPARENT */ + +#endif /* OSSPINLOCK_USE_INLINED */ + +#endif /* _OSSPINLOCK_DEPRECATED_H_ */ \ No newline at end of file diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index 74e515f9d158..dc9e6d85728a 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -163,9 +163,9 @@ pub fn format( out_stream: anytype, ) !void { if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'"); - try std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); - if (self.pre) |pre| try std.fmt.format(out_stream, "-{}", .{pre}); - if (self.build) |build| try std.fmt.format(out_stream, "+{}", .{build}); + try std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); + if (self.pre) |pre| try std.fmt.format(out_stream, "-{s}", .{pre}); + if (self.build) |build| try std.fmt.format(out_stream, "+{s}", .{build}); } const expect = std.testing.expect; @@ -287,9 +287,9 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) ! if (std.mem.eql(u8, result, expected)) return; std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); + std.debug.warn("{s}", .{expected}); std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); + std.debug.warn("{s}", .{result}); std.debug.warn("\n======================================\n", .{}); return error.TestFailed; } diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index ccdf487f48de..3114d1b74477 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -12,10 +12,20 @@ const Allocator = mem.Allocator; /// A contiguous, growable list of items in memory. /// This is a wrapper around an array of T values. Initialize with `init`. +/// +/// This struct internally stores a `std.mem.Allocator` for memory management. +/// To manually specify an allocator with each method call see `ArrayListUnmanaged`. pub fn ArrayList(comptime T: type) type { return ArrayListAligned(T, null); } +/// A contiguous, growable list of arbitrarily aligned items in memory. +/// This is a wrapper around an array of T values aligned to `alignment`-byte +/// addresses. If the specified alignment is `null`, then `@alignOf(T)` is used. +/// Initialize with `init`. +/// +/// This struct internally stores a `std.mem.Allocator` for memory management. +/// To manually specify an allocator with each method call see `ArrayListAlignedUnmanaged`. pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { if (alignment) |a| { if (a == @alignOf(T)) { @@ -24,9 +34,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } return struct { const Self = @This(); - - /// Content of the ArrayList + /// Contents of the list. Pointers to elements in this slice are + /// **invalid after resizing operations** on the ArrayList, unless the + /// operation explicitly either: (1) states otherwise or (2) lists the + /// invalidated pointers. + /// + /// The allocator used determines how element pointers are + /// invalidated, so the behavior may vary between lists. To avoid + /// illegal behavior, take into account the above paragraph plus the + /// explicit statements given in each method. items: Slice, + /// How many T values this list can hold without allocating + /// additional memory. capacity: usize, allocator: *Allocator, @@ -42,7 +61,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } - /// Initialize with capacity to hold at least num elements. + /// Initialize with capacity to hold at least `num` elements. /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn initCapacity(allocator: *Allocator, num: usize) !Self { var self = Self.init(allocator); @@ -79,11 +98,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } + /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields + /// of this ArrayList. This ArrayList retains ownership of underlying memory. pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) { return .{ .items = self.items, .capacity = self.capacity }; } - /// The caller owns the returned memory. ArrayList becomes empty. + /// The caller owns the returned memory. Empties this ArrayList. pub fn toOwnedSlice(self: *Self) Slice { const allocator = self.allocator; const result = allocator.shrink(self.allocatedSlice(), self.items.len); @@ -91,7 +112,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { return result; } - /// The caller owns the returned memory. ArrayList becomes empty. + /// The caller owns the returned memory. Empties this ArrayList. pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) ![:sentinel]T { try self.append(sentinel); const result = self.toOwnedSlice(); @@ -118,9 +139,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { mem.copy(T, self.items[i .. i + items.len], items); } - /// Replace range of elements `list[start..start+len]` with `new_items` - /// grows list if `len < new_items.len`. may allocate - /// shrinks list if `len > new_items.len` + /// Replace range of elements `list[start..start+len]` with `new_items`. + /// Grows list if `len < new_items.len`. + /// Shrinks list if `len > new_items.len`. + /// Invalidates pointers if this ArrayList is resized. pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void { const after_range = start + len; const range = self.items[start..after_range]; @@ -151,15 +173,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { new_item_ptr.* = item; } - /// Extend the list by 1 element, but asserting `self.capacity` - /// is sufficient to hold an additional item. + /// Extend the list by 1 element, but assert `self.capacity` + /// is sufficient to hold an additional item. **Does not** + /// invalidate pointers. pub fn appendAssumeCapacity(self: *Self, item: T) void { const new_item_ptr = self.addOneAssumeCapacity(); new_item_ptr.* = item; } - /// Remove the element at index `i` from the list and return its value. + /// Remove the element at index `i`, shift elements after index + /// `i` forward, and return the removed element. /// Asserts the array has at least one item. + /// Invalidates pointers to end of list. /// This operation is O(N). pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.items.len - 1; @@ -191,7 +216,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Append the slice of items to the list, asserting the capacity is already - /// enough to store the new items. + /// enough to store the new items. **Does not** invalidate pointers. pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void { const oldlen = self.items.len; const newlen = self.items.len + items.len; @@ -227,7 +252,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Append a value to the list `n` times. - /// Asserts the capacity is enough. + /// Asserts the capacity is enough. **Does not** invalidate pointers. pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { const new_len = self.items.len + n; assert(new_len <= self.capacity); @@ -243,7 +268,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Reduce allocated capacity to `new_len`. - /// Invalidates element pointers. + /// May invalidate element pointers. pub fn shrink(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); @@ -257,13 +282,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Reduce length to `new_len`. - /// Invalidates element pointers. - /// Keeps capacity the same. + /// Invalidates pointers for the elements `items[new_len..]`. pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); self.items.len = new_len; } + /// Modify the array so that it can hold at least `new_capacity` items. + /// Invalidates pointers if additional memory is needed. pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { var better_capacity = self.capacity; if (better_capacity >= new_capacity) return; @@ -280,14 +306,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Increases the array's length to match the full capacity that is already allocated. - /// The new elements have `undefined` values. This operation does not invalidate any - /// element pointers. + /// The new elements have `undefined` values. **Does not** invalidate pointers. pub fn expandToCapacity(self: *Self) void { self.items.len = self.capacity; } /// Increase length by 1, returning pointer to the new item. - /// The returned pointer becomes invalid when the list is resized. + /// The returned pointer becomes invalid when the list resized. pub fn addOne(self: *Self) !*T { const newlen = self.items.len + 1; try self.ensureCapacity(newlen); @@ -297,6 +322,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Increase length by 1, returning pointer to the new item. /// Asserts that there is already space for the new item without allocating more. /// The returned pointer becomes invalid when the list is resized. + /// **Does not** invalidate element pointers. pub fn addOneAssumeCapacity(self: *Self) *T { assert(self.items.len < self.capacity); @@ -306,6 +332,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. + /// The returned pointer becomes invalid when the list is resized. + /// Resizes list if `self.capacity` is not large enough. pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T { const prev_len = self.items.len; try self.resize(self.items.len + n); @@ -315,6 +343,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. /// Asserts that there is already space for the new item without allocating more. + /// **Does not** invalidate element pointers. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T { assert(self.items.len + n <= self.capacity); const prev_len = self.items.len; @@ -324,21 +354,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Remove and return the last element from the list. /// Asserts the list has at least one item. + /// Invalidates pointers to the removed element. pub fn pop(self: *Self) T { const val = self.items[self.items.len - 1]; self.items.len -= 1; return val; } - /// Remove and return the last element from the list. - /// If the list is empty, returns `null`. + /// Remove and return the last element from the list, or + /// return `null` if list is empty. + /// Invalidates pointers to the removed element, if any. pub fn popOrNull(self: *Self) ?T { if (self.items.len == 0) return null; return self.pop(); } /// Returns a slice of all the items plus the extra capacity, whose memory - /// contents are undefined. + /// contents are `undefined`. pub fn allocatedSlice(self: Self) Slice { // For a nicer API, `items.len` is the length, not the capacity. // This requires "unsafe" slicing. @@ -346,7 +378,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Returns a slice of only the extra capacity after items. - /// This can be useful for writing directly into an `ArrayList`. + /// This can be useful for writing directly into an ArrayList. /// Note that such an operation must be followed up with a direct /// modification of `self.items.len`. pub fn unusedCapacitySlice(self: Self) Slice { @@ -355,12 +387,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } -/// Bring-your-own allocator with every function call. -/// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`. +/// An ArrayList, but the allocator is passed as a parameter to the relevant functions +/// rather than stored in the struct itself. The same allocator **must** be used throughout +/// the entire lifetime of an ArrayListUnmanaged. Initialize directly or with +/// `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`. pub fn ArrayListUnmanaged(comptime T: type) type { return ArrayListAlignedUnmanaged(T, null); } +/// An ArrayListAligned, but the allocator is passed as a parameter to the relevant +/// functions rather than stored in the struct itself. The same allocator **must** +/// be used throughout the entire lifetime of an ArrayListAlignedUnmanaged. +/// Initialize directly or with `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`. pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type { if (alignment) |a| { if (a == @alignOf(T)) { @@ -369,9 +407,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } return struct { const Self = @This(); - - /// Content of the ArrayList. + /// Contents of the list. Pointers to elements in this slice are + /// **invalid after resizing operations** on the ArrayList, unless the + /// operation explicitly either: (1) states otherwise or (2) lists the + /// invalidated pointers. + /// + /// The allocator used determines how element pointers are + /// invalidated, so the behavior may vary between lists. To avoid + /// illegal behavior, take into account the above paragraph plus the + /// explicit statements given in each method. items: Slice = &[_]T{}, + /// How many T values this list can hold without allocating + /// additional memory. capacity: usize = 0, pub const Slice = if (alignment) |a| ([]align(a) T) else []T; @@ -395,6 +442,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ self.* = undefined; } + /// Convert this list into an analogous memory-managed one. + /// The returned list has ownership of the underlying memory. pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) { return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator }; } @@ -414,7 +463,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Insert `item` at index `n`. Moves `list[n .. list.len]` - /// to make room. + /// to higher indices to make room. + /// This operation is O(N). pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void { try self.ensureCapacity(allocator, self.items.len + 1); self.items.len += 1; @@ -423,8 +473,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ self.items[n] = item; } - /// Insert slice `items` at index `i`. Moves - /// `list[i .. list.len]` to make room. + /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to + /// higher indicices make room. /// This operation is O(N). pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void { try self.ensureCapacity(allocator, self.items.len + items.len); @@ -435,8 +485,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Replace range of elements `list[start..start+len]` with `new_items` - /// grows list if `len < new_items.len`. may allocate - /// shrinks list if `len > new_items.len` + /// Grows list if `len < new_items.len`. + /// Shrinks list if `len > new_items.len` + /// Invalidates pointers if this ArrayList is resized. pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void { var managed = self.toManaged(allocator); try managed.replaceRange(start, len, new_items); @@ -457,7 +508,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Remove the element at index `i` from the list and return its value. - /// Asserts the array has at least one item. + /// Asserts the array has at least one item. Invalidates pointers to + /// last element. /// This operation is O(N). pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.items.len - 1; @@ -472,6 +524,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Removes the element at the specified index and returns it. /// The empty slot is filled from the end of the list. + /// Invalidates pointers to last element. /// This operation is O(1). pub fn swapRemove(self: *Self, i: usize) T { if (self.items.len - 1 == i) return self.pop(); @@ -515,6 +568,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Append a value to the list `n` times. + /// **Does not** invalidate pointers. /// Asserts the capacity is enough. pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { const new_len = self.items.len + n; @@ -524,14 +578,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Adjust the list's length to `new_len`. - /// Does not initialize added items if any. + /// Does not initialize added items, if any. pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void { try self.ensureCapacity(allocator, new_len); self.items.len = new_len; } /// Reduce allocated capacity to `new_len`. - /// Invalidates element pointers. pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void { assert(new_len <= self.items.len); @@ -545,13 +598,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Reduce length to `new_len`. - /// Invalidates element pointers. + /// Invalidates pointers to elements `items[new_len..]`. /// Keeps capacity the same. pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); self.items.len = new_len; } + /// Modify the array so that it can hold at least `new_capacity` items. + /// Invalidates pointers if additional memory is needed. pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { var better_capacity = self.capacity; if (better_capacity >= new_capacity) return; @@ -568,13 +623,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Increases the array's length to match the full capacity that is already allocated. /// The new elements have `undefined` values. - /// This operation does not invalidate any element pointers. + /// **Does not** invalidate pointers. pub fn expandToCapacity(self: *Self) void { self.items.len = self.capacity; } /// Increase length by 1, returning pointer to the new item. - /// The returned pointer becomes invalid when the list is resized. + /// The returned pointer becomes invalid when the list resized. pub fn addOne(self: *Self, allocator: *Allocator) !*T { const newlen = self.items.len + 1; try self.ensureCapacity(allocator, newlen); @@ -583,8 +638,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Increase length by 1, returning pointer to the new item. /// Asserts that there is already space for the new item without allocating more. - /// The returned pointer becomes invalid when the list is resized. - /// This operation does not invalidate any element pointers. + /// **Does not** invalidate pointers. + /// The returned pointer becomes invalid when the list resized. pub fn addOneAssumeCapacity(self: *Self) *T { assert(self.items.len < self.capacity); @@ -594,6 +649,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArray(self: *Self, allocator: *Allocator, comptime n: usize) !*[n]T { const prev_len = self.items.len; try self.resize(allocator, self.items.len + n); @@ -603,6 +659,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. /// Asserts that there is already space for the new item without allocating more. + /// **Does not** invalidate pointers. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T { assert(self.items.len + n <= self.capacity); const prev_len = self.items.len; @@ -612,7 +670,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Remove and return the last element from the list. /// Asserts the list has at least one item. - /// This operation does not invalidate any element pointers. + /// Invalidates pointers to last element. pub fn pop(self: *Self) T { const val = self.items[self.items.len - 1]; self.items.len -= 1; @@ -621,7 +679,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Remove and return the last element from the list. /// If the list is empty, returns `null`. - /// This operation does not invalidate any element pointers. + /// Invalidates pointers to last element. pub fn popOrNull(self: *Self) ?T { if (self.items.len == 0) return null; return self.pop(); diff --git a/lib/std/build.zig b/lib/std/build.zig index 3b6f8815bc86..bd097c941492 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -294,7 +294,7 @@ pub const Builder = struct { /// To run an executable built with zig build, see `LibExeObjStep.run`. pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { assert(argv.len >= 1); - const run_step = RunStep.create(self, self.fmt("run {}", .{argv[0]})); + const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); run_step.addArgs(argv); return run_step; } @@ -409,7 +409,7 @@ pub const Builder = struct { for (self.installed_files.items) |installed_file| { const full_path = self.getInstallPath(installed_file.dir, installed_file.path); if (self.verbose) { - warn("rm {}\n", .{full_path}); + warn("rm {s}\n", .{full_path}); } fs.cwd().deleteTree(full_path) catch {}; } @@ -419,7 +419,7 @@ pub const Builder = struct { fn makeOneStep(self: *Builder, s: *Step) anyerror!void { if (s.loop_flag) { - warn("Dependency loop detected:\n {}\n", .{s.name}); + warn("Dependency loop detected:\n {s}\n", .{s.name}); return error.DependencyLoopDetected; } s.loop_flag = true; @@ -427,7 +427,7 @@ pub const Builder = struct { for (s.dependencies.items) |dep| { self.makeOneStep(dep) catch |err| { if (err == error.DependencyLoopDetected) { - warn(" {}\n", .{s.name}); + warn(" {s}\n", .{s.name}); } return err; }; @@ -444,7 +444,7 @@ pub const Builder = struct { return &top_level_step.step; } } - warn("Cannot run step '{}' because it does not exist\n", .{name}); + warn("Cannot run step '{s}' because it does not exist\n", .{name}); return error.InvalidStepName; } @@ -456,7 +456,7 @@ pub const Builder = struct { .description = description, }; if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { - panic("Option '{}' declared twice", .{name}); + panic("Option '{s}' declared twice", .{name}); } self.available_options_list.append(available_option) catch unreachable; @@ -471,32 +471,32 @@ pub const Builder = struct { } else if (mem.eql(u8, s, "false")) { return false; } else { - warn("Expected -D{} to be a boolean, but received '{}'\n\n", .{ name, s }); + warn("Expected -D{s} to be a boolean, but received '{s}'\n\n", .{ name, s }); self.markInvalidUserInput(); return null; } }, .List => { - warn("Expected -D{} to be a boolean, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a boolean, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Int => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be an integer, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be an integer, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .Scalar => |s| { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { error.Overflow => { - warn("-D{} value {} cannot fit into type {}.\n\n", .{ name, s, @typeName(T) }); + warn("-D{s} value {} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); self.markInvalidUserInput(); return null; }, else => { - warn("Expected -D{} to be an integer of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be an integer of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; }, @@ -504,34 +504,34 @@ pub const Builder = struct { return n; }, .List => { - warn("Expected -D{} to be an integer, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be an integer, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Float => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a float, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a float, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .Scalar => |s| { const n = std.fmt.parseFloat(T, s) catch |err| { - warn("Expected -D{} to be a float of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be a float of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; }; return n; }, .List => { - warn("Expected -D{} to be a float, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a float, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Enum => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a string, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -539,25 +539,25 @@ pub const Builder = struct { if (std.meta.stringToEnum(T, s)) |enum_lit| { return enum_lit; } else { - warn("Expected -D{} to be of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; } }, .List => { - warn("Expected -D{} to be a string, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .String => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a string, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .List => { - warn("Expected -D{} to be a string, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -565,7 +565,7 @@ pub const Builder = struct { }, .List => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a list, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a list, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -592,7 +592,7 @@ pub const Builder = struct { if (self.release_mode != null) { @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); } - const description = self.fmt("Create a release build ({})", .{@tagName(mode)}); + const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); self.is_release = self.option(bool, "release", description) orelse false; self.release_mode = if (self.is_release) mode else builtin.Mode.Debug; } @@ -646,12 +646,12 @@ pub const Builder = struct { .diagnostics = &diags, }) catch |err| switch (err) { error.UnknownCpuModel => { - warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + warn("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':\n", .{ diags.cpu_name.?, @tagName(diags.arch.?), }); for (diags.arch.?.allCpuModels()) |cpu| { - warn(" {}\n", .{cpu.name}); + warn(" {s}\n", .{cpu.name}); } warn("\n", .{}); self.markInvalidUserInput(); @@ -659,15 +659,15 @@ pub const Builder = struct { }, error.UnknownCpuFeature => { warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': + \\Unknown CPU feature: '{s}' + \\Available CPU features for architecture '{s}': \\ , .{ diags.unknown_feature_name, @tagName(diags.arch.?), }); for (diags.arch.?.allFeaturesList()) |feature| { - warn(" {}: {}\n", .{ feature.name, feature.description }); + warn(" {s}: {s}\n", .{ feature.name, feature.description }); } warn("\n", .{}); self.markInvalidUserInput(); @@ -675,19 +675,19 @@ pub const Builder = struct { }, error.UnknownOperatingSystem => { warn( - \\Unknown OS: '{}' + \\Unknown OS: '{s}' \\Available operating systems: \\ , .{diags.os_name}); inline for (std.meta.fields(std.Target.Os.Tag)) |field| { - warn(" {}\n", .{field.name}); + warn(" {s}\n", .{field.name}); } warn("\n", .{}); self.markInvalidUserInput(); return args.default_target; }, else => |e| { - warn("Unable to parse target '{}': {}\n\n", .{ triple, @errorName(e) }); + warn("Unable to parse target '{}': {s}\n\n", .{ triple, @errorName(e) }); self.markInvalidUserInput(); return args.default_target; }, @@ -703,12 +703,12 @@ pub const Builder = struct { break :whitelist_check; } } - warn("Chosen target '{}' does not match one of the supported targets:\n", .{ + warn("Chosen target '{s}' does not match one of the supported targets:\n", .{ selected_canonicalized_triple, }); for (list) |t| { const t_triple = t.zigTriple(self.allocator) catch unreachable; - warn(" {}\n", .{t_triple}); + warn(" {s}\n", .{t_triple}); } warn("\n", .{}); self.markInvalidUserInput(); @@ -752,7 +752,7 @@ pub const Builder = struct { }) catch unreachable; }, UserValue.Flag => { - warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", .{ name, value, name }); + warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.\n", .{ name, value, name }); return true; }, } @@ -773,11 +773,11 @@ pub const Builder = struct { // option already exists switch (gop.entry.value.value) { UserValue.Scalar => |s| { - warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); + warn("Flag '-D{s}' conflicts with option '-D{s}={s}'.\n", .{ name, name, s }); return true; }, UserValue.List => { - warn("Flag '-D{}' conflicts with multiple options of the same name.\n", .{name}); + warn("Flag '-D{s}' conflicts with multiple options of the same name.\n", .{name}); return true; }, UserValue.Flag => {}, @@ -820,7 +820,7 @@ pub const Builder = struct { while (true) { const entry = it.next() orelse break; if (!entry.value.used) { - warn("Invalid option: -D{}\n\n", .{entry.key}); + warn("Invalid option: -D{s}\n\n", .{entry.key}); self.markInvalidUserInput(); } } @@ -833,9 +833,9 @@ pub const Builder = struct { } fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); + if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); for (argv) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } @@ -852,7 +852,7 @@ pub const Builder = struct { child.env_map = env_map; const term = child.spawnAndWait() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -875,7 +875,7 @@ pub const Builder = struct { pub fn makePath(self: *Builder, path: []const u8) !void { fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { - warn("Unable to create path {}: {}\n", .{ path, @errorName(err) }); + warn("Unable to create path {s}: {s}\n", .{ path, @errorName(err) }); return err; }; } @@ -959,7 +959,7 @@ pub const Builder = struct { pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { if (self.verbose) { - warn("cp {} {} ", .{ source_path, dest_path }); + warn("cp {s} {s} ", .{ source_path, dest_path }); } const cwd = fs.cwd(); const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); @@ -988,7 +988,7 @@ pub const Builder = struct { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ search_prefix, "bin", - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1002,7 +1002,7 @@ pub const Builder = struct { while (it.next()) |path| { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1015,7 +1015,7 @@ pub const Builder = struct { for (paths) |path| { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1070,19 +1070,19 @@ pub const Builder = struct { var code: u8 = undefined; return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { error.FileNotFound => { - if (src_step) |s| warn("{}...", .{s.name}); + if (src_step) |s| warn("{s}...", .{s.name}); warn("Unable to spawn the following command: file not found\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ExitCodeFailure => { - if (src_step) |s| warn("{}...", .{s.name}); - warn("The following command exited with error code {}:\n", .{code}); + if (src_step) |s| warn("{s}...", .{s.name}); + warn("The following command exited with error code {d}:\n", .{code}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ProcessTerminated => { - if (src_step) |s| warn("{}...", .{s.name}); + if (src_step) |s| warn("{s}...", .{s.name}); warn("The following command terminated unexpectedly:\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); @@ -1405,7 +1405,7 @@ pub const LibExeObjStep = struct { ver: ?Version, ) LibExeObjStep { if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { - panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); + panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } var self = LibExeObjStep{ .strip = false, @@ -1421,9 +1421,9 @@ pub const LibExeObjStep = struct { .step = Step.init(.LibExeObj, name, builder.allocator, make), .version = ver, .out_filename = undefined, - .out_h_filename = builder.fmt("{}.h", .{name}), + .out_h_filename = builder.fmt("{s}.h", .{name}), .out_lib_filename = undefined, - .out_pdb_filename = builder.fmt("{}.pdb", .{name}), + .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), .major_only_filename = undefined, .name_only_filename = undefined, .packages = ArrayList(Pkg).init(builder.allocator), @@ -1529,7 +1529,7 @@ pub const LibExeObjStep = struct { // It doesn't have to be native. We catch that if you actually try to run it. // Consider that this is declarative; the run step may not be run unless a user // option is supplied. - const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", .{exe.step.name})); + const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); run_step.addArtifactArg(exe); if (exe.vcpkg_bin_path) |path| { @@ -1680,7 +1680,7 @@ pub const LibExeObjStep = struct { } else if (mem.eql(u8, tok, "-pthread")) { self.linkLibC(); } else if (self.builder.verbose) { - warn("Ignoring pkg-config flag '{}'\n", .{tok}); + warn("Ignoring pkg-config flag '{s}'\n", .{tok}); } } } @@ -1926,7 +1926,7 @@ pub const LibExeObjStep = struct { }, else => {}, } - out.print("pub const {z}: {} = {};\n", .{ name, @typeName(T), value }) catch unreachable; + out.print("pub const {z}: {s} = {};\n", .{ name, @typeName(T), value }) catch unreachable; } /// The value is the path in the cache dir. @@ -2048,7 +2048,7 @@ pub const LibExeObjStep = struct { const builder = self.builder; if (self.root_src == null and self.link_objects.items.len == 0) { - warn("{}: linker needs 1 or more objects to link\n", .{self.step.name}); + warn("{s}: linker needs 1 or more objects to link\n", .{self.step.name}); return error.NeedAnObject; } @@ -2156,12 +2156,12 @@ pub const LibExeObjStep = struct { // Render build artifact options at the last minute, now that the path is known. for (self.build_options_artifact_args.items) |item| { const out = self.build_options_contents.writer(); - out.print("pub const {}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable; + out.print("pub const {s}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable; } const build_options_file = try fs.path.join( builder.allocator, - &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, + &[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) }, ); const path_from_root = builder.pathFromRoot(build_options_file); try fs.cwd().writeFile(path_from_root, self.build_options_contents.items); @@ -2294,16 +2294,16 @@ pub const LibExeObjStep = struct { } else { var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); - try mcpu_buffer.outStream().print("-mcpu={}", .{cross.cpu.model.name}); + try mcpu_buffer.outStream().print("-mcpu={s}", .{cross.cpu.model.name}); for (all_features) |feature, i_usize| { const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); const in_cpu_set = populated_cpu_features.isEnabled(i); const in_actual_set = cross.cpu.features.isEnabled(i); if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.outStream().print("-{}", .{feature.name}); + try mcpu_buffer.outStream().print("-{s}", .{feature.name}); } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.outStream().print("+{}", .{feature.name}); + try mcpu_buffer.outStream().print("+{s}", .{feature.name}); } } @@ -2536,7 +2536,7 @@ pub const InstallArtifactStep = struct { const self = builder.allocator.create(Self) catch unreachable; self.* = Self{ .builder = builder, - .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make), + .step = Step.init(.InstallArtifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), .artifact = artifact, .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .Obj => unreachable, @@ -2612,7 +2612,7 @@ pub const InstallFileStep = struct { builder.pushInstalledFile(dir, dest_rel_path); return InstallFileStep{ .builder = builder, - .step = Step.init(.InstallFile, builder.fmt("install {}", .{src_path}), builder.allocator, make), + .step = Step.init(.InstallFile, builder.fmt("install {s}", .{src_path}), builder.allocator, make), .src_path = src_path, .dir = dir, .dest_rel_path = dest_rel_path, @@ -2646,7 +2646,7 @@ pub const InstallDirStep = struct { builder.pushInstalledFile(options.install_dir, options.install_subdir); return InstallDirStep{ .builder = builder, - .step = Step.init(.InstallDir, builder.fmt("install {}/", .{options.source_dir}), builder.allocator, make), + .step = Step.init(.InstallDir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), .options = options, }; } @@ -2682,14 +2682,14 @@ pub const LogStep = struct { pub fn init(builder: *Builder, data: []const u8) LogStep { return LogStep{ .builder = builder, - .step = Step.init(.Log, builder.fmt("log {}", .{data}), builder.allocator, make), + .step = Step.init(.Log, builder.fmt("log {s}", .{data}), builder.allocator, make), .data = data, }; } fn make(step: *Step) anyerror!void { const self = @fieldParentPtr(LogStep, "step", step); - warn("{}", .{self.data}); + warn("{s}", .{self.data}); } }; @@ -2701,7 +2701,7 @@ pub const RemoveDirStep = struct { pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, - .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {}", .{dir_path}), builder.allocator, make), + .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), .dir_path = dir_path, }; } @@ -2711,7 +2711,7 @@ pub const RemoveDirStep = struct { const full_path = self.builder.pathFromRoot(self.dir_path); fs.cwd().deleteTree(full_path) catch |err| { - warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) }); + warn("Unable to remove {s}: {s}\n", .{ full_path, @errorName(err) }); return err; }; } @@ -2799,7 +2799,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj &[_][]const u8{ out_dir, filename_major_only }, ) catch unreachable; fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", .{ major_only_path, out_basename }); + warn("Unable to symlink {s} -> {s}\n", .{ major_only_path, out_basename }); return err; }; // sym link for libfoo.so to libfoo.so.1 @@ -2808,7 +2808,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj &[_][]const u8{ out_dir, filename_name_only }, ) catch unreachable; fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", .{ name_only_path, filename_major_only }); + warn("Unable to symlink {s} -> {s}\n", .{ name_only_path, filename_major_only }); return err; }; } diff --git a/lib/std/build/check_file.zig b/lib/std/build/check_file.zig index 565ff0692a72..31966fad521a 100644 --- a/lib/std/build/check_file.zig +++ b/lib/std/build/check_file.zig @@ -45,9 +45,9 @@ pub const CheckFileStep = struct { warn( \\ \\========= Expected to find: =================== - \\{} + \\{s} \\========= But file does not contain it: ======= - \\{} + \\{s} \\ , .{ expected_match, contents }); return error.TestFailed; diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/emit_raw.zig index 66d44fc59fae..3d2c6124c166 100644 --- a/lib/std/build/emit_raw.zig +++ b/lib/std/build/emit_raw.zig @@ -189,7 +189,7 @@ pub const InstallRawStep = struct { pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self { const self = builder.allocator.create(Self) catch unreachable; self.* = Self{ - .step = Step.init(.InstallRaw, builder.fmt("install raw binary {}", .{artifact.step.name}), builder.allocator, make), + .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), .builder = builder, .artifact = artifact, .dest_dir = switch (artifact.kind) { diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 18e05c2e0149..36a4a1a843b9 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -116,7 +116,7 @@ pub const RunStep = struct { } if (prev_path) |pp| { - const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path }); + const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); env_map.set(key, new_path) catch unreachable; } else { env_map.set(key, search_path) catch unreachable; @@ -189,7 +189,7 @@ pub const RunStep = struct { child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); child.spawn() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -216,7 +216,7 @@ pub const RunStep = struct { } const term = child.wait() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -245,9 +245,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected this stderr: ========= - \\{} + \\{s} \\========= But found: ==================== - \\{} + \\{s} \\ , .{ expected_bytes, stderr.? }); printCmd(cwd, argv); @@ -259,9 +259,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected to find in stderr: ========= - \\{} + \\{s} \\========= But stderr does not contain it: ===== - \\{} + \\{s} \\ , .{ match, stderr.? }); printCmd(cwd, argv); @@ -277,9 +277,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected this stdout: ========= - \\{} + \\{s} \\========= But found: ==================== - \\{} + \\{s} \\ , .{ expected_bytes, stdout.? }); printCmd(cwd, argv); @@ -291,9 +291,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected to find in stdout: ========= - \\{} + \\{s} \\========= But stdout does not contain it: ===== - \\{} + \\{s} \\ , .{ match, stdout.? }); printCmd(cwd, argv); @@ -304,9 +304,9 @@ pub const RunStep = struct { } fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); + if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); for (argv) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig index 04b7268e81e1..bbe6ec508629 100644 --- a/lib/std/build/write_file.zig +++ b/lib/std/build/write_file.zig @@ -80,14 +80,14 @@ pub const WriteFileStep = struct { }); // TODO replace with something like fs.makePathAndOpenDir fs.cwd().makePath(self.output_dir) catch |err| { - warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); + warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); return err; }; var dir = try fs.cwd().openDir(self.output_dir, .{}); defer dir.close(); for (self.files.items) |file| { dir.writeFile(file.basename, file.bytes) catch |err| { - warn("unable to write {} into {}: {}\n", .{ + warn("unable to write {s} into {s}: {s}\n", .{ file.basename, self.output_dir, @errorName(err), diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index fbc4a7cef599..c883e03ba909 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -67,12 +67,12 @@ pub const StackTrace = struct { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const debug_info = std.debug.getSelfDebugInfo() catch |err| { - return writer.print("\nUnable to print stack trace: Unable to open debug info: {}\n", .{@errorName(err)}); + return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); }; const tty_config = std.debug.detectTTYConfig(); try writer.writeAll("\n"); std.debug.writeStackTrace(self, writer, &arena.allocator, debug_info, tty_config) catch |err| { - try writer.print("Unable to print stack trace: {}\n", .{@errorName(err)}); + try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); }; try writer.writeAll("\n"); } @@ -529,12 +529,12 @@ pub const Version = struct { if (fmt.len == 0) { if (self.patch == 0) { if (self.minor == 0) { - return std.fmt.format(out_stream, "{}", .{self.major}); + return std.fmt.format(out_stream, "{d}", .{self.major}); } else { - return std.fmt.format(out_stream, "{}.{}", .{ self.major, self.minor }); + return std.fmt.format(out_stream, "{d}.{d}", .{ self.major, self.minor }); } } else { - return std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + return std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); } } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); @@ -683,7 +683,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn } }, .wasi => { - std.debug.warn("{}", .{msg}); + std.debug.warn("{s}", .{msg}); std.os.abort(); }, .uefi => { @@ -692,7 +692,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn }, else => { const first_trace_addr = @returnAddress(); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", .{msg}); + std.debug.panicExtra(error_return_trace, first_trace_addr, "{s}", .{msg}); }, } } diff --git a/lib/std/c.zig b/lib/std/c.zig index 7a1f47e6ac57..9b29d9a2c475 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -73,10 +73,10 @@ pub fn versionCheck(glibc_version: builtin.Version) type { pub extern "c" var environ: [*:null]?[*:0]u8; -pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE; +pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; pub extern "c" fn fclose(stream: *FILE) c_int; -pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; -pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; +pub extern "c" fn fwrite(noalias ptr: [*]const u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize; +pub extern "c" fn fread(noalias ptr: [*]u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize; pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn abort() noreturn; @@ -153,9 +153,9 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int; pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int; pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; -pub extern "c" fn accept(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t) c_int; -pub extern "c" fn accept4(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t, flags: c_uint) c_int; -pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int; +pub extern "c" fn accept(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t) c_int; +pub extern "c" fn accept4(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t, flags: c_uint) c_int; +pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, noalias optval: ?*c_void, noalias optlen: *socklen_t) c_int; pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int; pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; pub extern "c" fn sendto( diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig index c1e20f799de1..207fe8eac8cd 100644 --- a/lib/std/c/ast.zig +++ b/lib/std/c/ast.zig @@ -115,10 +115,10 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { const found_token = tree.tokens.at(self.token); if (found_token.id == .Invalid) { - return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); + return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); } else { const token_name = found_token.id.symbol(); - return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); + return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); } } }; @@ -131,7 +131,7 @@ pub const Error = union(enum) { try stream.write("invalid type specifier '"); try type_spec.spec.print(tree, stream); const token_name = tree.tokens.at(self.token).id.symbol(); - return stream.print("{}'", .{token_name}); + return stream.print("{s}'", .{token_name}); } }; @@ -140,7 +140,7 @@ pub const Error = union(enum) { name: TokenIndex, pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { - return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) }); + return stream.print("must use '{s}' tag to refer to type '{s}'", .{ tree.slice(kw), tree.slice(name) }); } }; diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig index 3fa3d1d6f82f..ea5774fe4c7f 100644 --- a/lib/std/c/tokenizer.zig +++ b/lib/std/c/tokenizer.zig @@ -446,7 +446,7 @@ pub const Tokenizer = struct { 'L' => { state = .L; }, - 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => { + 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_', '$' => { state = .Identifier; }, '=' => { @@ -776,7 +776,7 @@ pub const Tokenizer = struct { }, }, .Identifier => switch (c) { - 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, + 'a'...'z', 'A'...'Z', '_', '0'...'9', '$' => {}, else => { result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier; if (self.prev_tok_id == .Hash) @@ -1552,7 +1552,7 @@ fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void { for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); if (!std.meta.eql(token.id, expected_token_id)) { - std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); + std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); } } const last_token = tokenizer.next(); diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index f97db98b0bd3..da6ec2edf0d1 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -140,6 +140,9 @@ pub const random = &@import("crypto/tlcsprng.zig").interface; const std = @import("std.zig"); test "crypto" { + const please_windows_dont_oom = std.Target.current.os.tag == .windows; + if (please_windows_dont_oom) return error.SkipZigTest; + inline for (std.meta.declarations(@This())) |decl| { switch (decl.data) { .Type => |t| { diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index 554ba50c6824..6d333c45cb98 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -247,7 +247,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Codec.encode(ct_str[0..], ct[0 .. ct.len - 1]); var s_buf: [hash_length]u8 = undefined; - const s = fmt.bufPrint(s_buf[0..], "$2b${}{}${}{}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable; + const s = fmt.bufPrint(s_buf[0..], "$2b${d}{d}${s}{s}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable; debug.assert(s.len == s_buf.len); return s_buf; } diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index bcb925c12d21..4203a994596f 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -33,6 +33,7 @@ fn roundParam(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundP // Blake2s pub const Blake2s128 = Blake2s(128); +pub const Blake2s160 = Blake2s(160); pub const Blake2s224 = Blake2s(224); pub const Blake2s256 = Blake2s(256); @@ -137,12 +138,8 @@ pub fn Blake2s(comptime out_bits: usize) type { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); - - const rr = d.h[0 .. digest_length / 4]; - - for (rr) |s, j| { - mem.writeIntSliceLittle(u32, out[4 * j ..], s); - } + for (d.h) |*x| x.* = mem.nativeToLittle(u32, x.*); + mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h)); } fn round(d: *Self, b: *const [64]u8, last: bool) void { @@ -195,6 +192,89 @@ pub fn Blake2s(comptime out_bits: usize) type { }; } +test "blake2s160 single" { + const h1 = "354c9c33f735962418bdacb9479873429c34916f"; + htest.assertEqualHash(Blake2s160, h1, ""); + + const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17"; + htest.assertEqualHash(Blake2s160, h2, "abc"); + + const h3 = "5a604fec9713c369e84b0ed68daed7d7504ef240"; + htest.assertEqualHash(Blake2s160, h3, "The quick brown fox jumps over the lazy dog"); + + const h4 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0"; + htest.assertEqualHash(Blake2s160, h4, "a" ** 32 ++ "b" ** 32); +} + +test "blake2s160 streaming" { + var h = Blake2s160.init(.{}); + var out: [20]u8 = undefined; + + const h1 = "354c9c33f735962418bdacb9479873429c34916f"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17"; + + h = Blake2s160.init(.{}); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h = Blake2s160.init(.{}); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + const h3 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0"; + + h = Blake2s160.init(.{}); + h.update("a" ** 32); + h.update("b" ** 32); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2s160.init(.{}); + h.update("a" ** 32 ++ "b" ** 32); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + const h4 = "4667fd60791a7fe41f939bca646b4529e296bd68"; + + h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 }); + h.update("a" ** 32); + h.update("b" ** 32); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); + + h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 }); + h.update("a" ** 32 ++ "b" ** 32); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); +} + +test "comptime blake2s160" { + //comptime + { + @setEvalBranchQuota(10000); + var block = [_]u8{0} ** Blake2s160.block_length; + var out: [Blake2s160.digest_length]u8 = undefined; + + const h1 = "2c56ad9d0b2c8b474aafa93ab307db2f0940105f"; + + htest.assertEqualHash(Blake2s160, h1, block[0..]); + + var h = Blake2s160.init(.{}); + h.update(&block); + h.final(out[0..]); + + htest.assertEqual(h1, out[0..]); + } +} + test "blake2s224 single" { const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; htest.assertEqualHash(Blake2s224, h1, ""); @@ -373,6 +453,7 @@ test "comptime blake2s256" { // Blake2b pub const Blake2b128 = Blake2b(128); +pub const Blake2b160 = Blake2b(160); pub const Blake2b256 = Blake2b(256); pub const Blake2b384 = Blake2b(384); pub const Blake2b512 = Blake2b(512); @@ -480,12 +561,8 @@ pub fn Blake2b(comptime out_bits: usize) type { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); - - const rr = d.h[0 .. digest_length / 8]; - - for (rr) |s, j| { - mem.writeIntSliceLittle(u64, out[8 * j ..], s); - } + for (d.h) |*x| x.* = mem.nativeToLittle(u64, x.*); + mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h)); } fn round(d: *Self, b: *const [128]u8, last: bool) void { @@ -538,6 +615,95 @@ pub fn Blake2b(comptime out_bits: usize) type { }; } +test "blake2b160 single" { + const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2"; + htest.assertEqualHash(Blake2b160, h1, ""); + + const h2 = "384264f676f39536840523f284921cdc68b6846b"; + htest.assertEqualHash(Blake2b160, h2, "abc"); + + const h3 = "3c523ed102ab45a37d54f5610d5a983162fde84f"; + htest.assertEqualHash(Blake2b160, h3, "The quick brown fox jumps over the lazy dog"); + + const h4 = "43758f5de1740f651f1ae39de92260fe8bd5a11f"; + htest.assertEqualHash(Blake2b160, h4, "a" ** 64 ++ "b" ** 64); +} + +test "blake2b160 streaming" { + var h = Blake2b160.init(.{}); + var out: [20]u8 = undefined; + + const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "384264f676f39536840523f284921cdc68b6846b"; + + h = Blake2b160.init(.{}); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + const h3 = "43758f5de1740f651f1ae39de92260fe8bd5a11f"; + + h = Blake2b160.init(.{}); + h.update("a" ** 64 ++ "b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + const h4 = "72328f8a8200663752fc302d372b5dd9b49dd8dc"; + + h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 }); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); + + h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 }); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); +} + +test "comptime blake2b160" { + comptime { + @setEvalBranchQuota(10000); + var block = [_]u8{0} ** Blake2b160.block_length; + var out: [Blake2b160.digest_length]u8 = undefined; + + const h1 = "8d26f158f564e3293b42f5e3d34263cb173aa9c9"; + + htest.assertEqualHash(Blake2b160, h1, block[0..]); + + var h = Blake2b160.init(.{}); + h.update(&block); + h.final(out[0..]); + + htest.assertEqual(h1, out[0..]); + } +} + test "blake2b384 single" { const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; htest.assertEqualHash(Blake2b384, h1, ""); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 57a0e20d2e87..073f68da5d98 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -108,11 +108,11 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; return; }; } @@ -129,7 +129,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; const tty_config = detectTTYConfig(); @@ -199,11 +199,11 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; return; }; } @@ -611,7 +611,7 @@ fn printLineInfo( tty_config.setColor(out_stream, .White); if (line_info) |*li| { - try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); + try out_stream.print("{s}:{d}:{d}", .{ li.file_name, li.line, li.column }); } else { try out_stream.writeAll("???:?:?"); } @@ -619,7 +619,7 @@ fn printLineInfo( tty_config.setColor(out_stream, .Reset); try out_stream.writeAll(": "); tty_config.setColor(out_stream, .Dim); - try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name }); + try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); tty_config.setColor(out_stream, .Reset); try out_stream.writeAll("\n"); diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index b79dea04cbad..2e732cbc7546 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -413,10 +413,7 @@ pub const DwarfInfo = struct { var this_unit_offset: u64 = 0; while (this_unit_offset < try seekable.getEndPos()) { - seekable.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => unreachable, - else => return err, - }; + try seekable.seekTo(this_unit_offset); var is_64: bool = undefined; const unit_length = try readUnitLength(in, di.endian, &is_64); @@ -520,10 +517,7 @@ pub const DwarfInfo = struct { var this_unit_offset: u64 = 0; while (this_unit_offset < try seekable.getEndPos()) { - seekable.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => unreachable, - else => return err, - }; + try seekable.seekTo(this_unit_offset); var is_64: bool = undefined; const unit_length = try readUnitLength(in, di.endian, &is_64); diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index e9112c917834..788f8f19334b 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -466,7 +466,7 @@ test "LinearFifo(u8, .Dynamic)" { fifo.shrink(0); { - try fifo.writer().print("{}, {}!", .{ "Hello", "World" }); + try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" }); var result: [30]u8 = undefined; testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]); testing.expectEqual(@as(usize, 0), fifo.readableLength()); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 803df7da9460..bc9ac922831e 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -367,6 +367,36 @@ pub fn format( } } +pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + const T = @TypeOf(value); + + switch (@typeInfo(T)) { + .Pointer => |info| { + try writer.writeAll(@typeName(info.child) ++ "@"); + if (info.size == .Slice) + try formatInt(@ptrToInt(value.ptr), 16, false, FormatOptions{}, writer) + else + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + }, + .Optional => |info| { + if (@typeInfo(info.child) == .Pointer) { + try writer.writeAll(@typeName(info.child) ++ "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + } + }, + .Array => |info| { + try writer.writeAll(@typeName(info.child) ++ "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + }, + else => {}, + } + + @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); +} + pub fn formatType( value: anytype, comptime fmt: []const u8, @@ -375,10 +405,7 @@ pub fn formatType( max_depth: usize, ) @TypeOf(writer).Error!void { if (comptime std.mem.eql(u8, fmt, "*")) { - try writer.writeAll(@typeName(std.meta.Child(@TypeOf(value)))); - try writer.writeAll("@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); - return; + return formatAddress(value, options, writer); } const T = @TypeOf(value); @@ -436,12 +463,11 @@ pub fn formatType( try formatType(@enumToInt(value), fmt, options, writer, max_depth); try writer.writeAll(")"); }, - .Union => { + .Union => |info| { try writer.writeAll(@typeName(T)); if (max_depth == 0) { return writer.writeAll("{ ... }"); } - const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { try writer.writeAll("{ ."); try writer.writeAll(@tagName(@as(UnionTagType, value))); @@ -456,13 +482,13 @@ pub fn formatType( try format(writer, "@{x}", .{@ptrToInt(&value)}); } }, - .Struct => |StructT| { + .Struct => |info| { try writer.writeAll(@typeName(T)); if (max_depth == 0) { return writer.writeAll("{ ... }"); } try writer.writeAll("{"); - inline for (StructT.fields) |f, i| { + inline for (info.fields) |f, i| { if (i == 0) { try writer.writeAll(" ."); } else { @@ -478,69 +504,83 @@ pub fn formatType( .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { if (info.child == u8) { - return formatText(value, fmt, options, writer); + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(value, fmt, options, writer); + } } - return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); }, .Enum, .Union, .Struct => { return formatType(value.*, fmt, options, writer, max_depth); }, - else => return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }), + else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }), }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { return formatType(mem.span(value), fmt, options, writer, max_depth); } if (ptr_info.child == u8) { - if (fmt.len > 0 and fmt[0] == 's') { + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { return formatText(mem.span(value), fmt, options, writer); } } - return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); }, .Slice => { - if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, options, writer); + if (max_depth == 0) { + return writer.writeAll("{ ... }"); } if (ptr_info.child == u8) { - return formatText(value, fmt, options, writer); + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(value, fmt, options, writer); + } } - return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); + try writer.writeAll("{ "); + for (value) |elem, i| { + try formatType(elem, fmt, options, writer, max_depth - 1); + if (i != value.len - 1) { + try writer.writeAll(", "); + } + } + try writer.writeAll(" }"); }, }, .Array => |info| { - const Slice = @Type(builtin.TypeInfo{ - .Pointer = .{ - .size = .Slice, - .is_const = true, - .is_volatile = false, - .is_allowzero = false, - .alignment = @alignOf(info.child), - .child = info.child, - .sentinel = null, - }, - }); - return formatType(@as(Slice, &value), fmt, options, writer, max_depth); + if (max_depth == 0) { + return writer.writeAll("{ ... }"); + } + if (info.child == u8) { + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(&value, fmt, options, writer); + } + } + try writer.writeAll("{ "); + for (value) |elem, i| { + try formatType(elem, fmt, options, writer, max_depth - 1); + if (i < value.len - 1) { + try writer.writeAll(", "); + } + } + try writer.writeAll(" }"); }, - .Vector => { - const len = @typeInfo(T).Vector.len; + .Vector => |info| { try writer.writeAll("{ "); var i: usize = 0; - while (i < len) : (i += 1) { + while (i < info.len) : (i += 1) { try formatValue(value[i], fmt, options, writer); - if (i < len - 1) { + if (i < info.len - 1) { try writer.writeAll(", "); } } try writer.writeAll(" }"); }, .Fn => { - return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(T), @ptrToInt(value) }); }, .Type => return formatBuf(@typeName(value), options, writer), .EnumLiteral => { const buffer = [_]u8{'.'} ++ @tagName(value); - return formatType(buffer, fmt, options, writer, max_depth); + return formatBuf(buffer, options, writer); }, .Null => return formatBuf("null", options, writer), else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), @@ -657,7 +697,7 @@ pub fn formatText( options: FormatOptions, writer: anytype, ) !void { - if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) { + if (comptime std.mem.eql(u8, fmt, "s")) { return formatBuf(bytes, options, writer); } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { for (bytes) |c| { @@ -1521,8 +1561,9 @@ test "buffer" { test "array" { { const value: [3]u8 = "abc".*; - try testFmt("array: abc\n", "array: {}\n", .{value}); - try testFmt("array: abc\n", "array: {}\n", .{&value}); + try testFmt("array: abc\n", "array: {s}\n", .{value}); + try testFmt("array: abc\n", "array: {s}\n", .{&value}); + try testFmt("array: { 97, 98, 99 }\n", "array: {d}\n", .{value}); var buf: [100]u8 = undefined; try testFmt( @@ -1536,12 +1577,12 @@ test "array" { test "slice" { { const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", .{value}); + try testFmt("slice: abc\n", "slice: {s}\n", .{value}); } { var runtime_zero: usize = 0; const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); + try testFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value}); } { const null_term_slice: [:0]const u8 = "\x00hello\x00"; @@ -1550,6 +1591,15 @@ test "slice" { try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); + + { + var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; + var runtime_zero: usize = 0; + try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + } } test "escape non-printable" { @@ -1854,9 +1904,9 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) ! if (mem.eql(u8, result, expected)) return; std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); + std.debug.warn("{s}", .{expected}); std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); + std.debug.warn("{s}", .{result}); std.debug.warn("\n======================================\n", .{}); return error.TestFailed; } @@ -2013,24 +2063,24 @@ test "vector" { } test "enum-literal" { - try testFmt(".hello_world", "{}", .{.hello_world}); + try testFmt(".hello_world", "{s}", .{.hello_world}); } test "padding" { - try testFmt("Simple", "{}", .{"Simple"}); + try testFmt("Simple", "{s}", .{"Simple"}); try testFmt(" true", "{:10}", .{true}); try testFmt(" true", "{:>10}", .{true}); try testFmt("======true", "{:=>10}", .{true}); try testFmt("true======", "{:=<10}", .{true}); try testFmt(" true ", "{:^10}", .{true}); try testFmt("===true===", "{:=^10}", .{true}); - try testFmt(" Minimum width", "{:18} width", .{"Minimum"}); - try testFmt("==================Filled", "{:=>24}", .{"Filled"}); - try testFmt(" Centered ", "{:^24}", .{"Centered"}); - try testFmt("-", "{:-^1}", .{""}); - try testFmt("==crêpe===", "{:=^10}", .{"crêpe"}); - try testFmt("=====crêpe", "{:=>10}", .{"crêpe"}); - try testFmt("crêpe=====", "{:=<10}", .{"crêpe"}); + try testFmt(" Minimum width", "{s:18} width", .{"Minimum"}); + try testFmt("==================Filled", "{s:=>24}", .{"Filled"}); + try testFmt(" Centered ", "{s:^24}", .{"Centered"}); + try testFmt("-", "{s:-^1}", .{""}); + try testFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); + try testFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); + try testFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); } test "decimal float padding" { @@ -2059,15 +2109,15 @@ test "type" { } test "named arguments" { - try testFmt("hello world!", "{} world{c}", .{ "hello", '!' }); - try testFmt("hello world!", "{[greeting]} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); - try testFmt("hello world!", "{[1]} world{[0]c}", .{ '!', "hello" }); + try testFmt("hello world!", "{s} world{c}", .{ "hello", '!' }); + try testFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); + try testFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" }); } test "runtime width specifier" { var width: usize = 9; - try testFmt("~~hello~~", "{:~^[1]}", .{ "hello", width }); - try testFmt("~~hello~~", "{:~^[width]}", .{ .string = "hello", .width = width }); + try testFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); + try testFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); } test "runtime precision specifier" { diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 761f6e846610..900adc5e2d5a 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -38,7 +38,7 @@ pub const PreopenType = union(PreopenTypeTag) { pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void { try out_stream.print("PreopenType{{ ", .{}); switch (self) { - PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}), + PreopenType.Dir => |path| try out_stream.print(".Dir = '{z}'", .{path}), } return out_stream.print(" }}", .{}); } diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index b132713a7e93..a2d6ed429ccd 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -102,7 +102,11 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type { }; } +const please_windows_dont_oom = std.Target.current.os.tag == .windows; + test "crc32 ieee" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Ieee = Crc32WithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); @@ -111,6 +115,8 @@ test "crc32 ieee" { } test "crc32 castagnoli" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Castagnoli = Crc32WithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); @@ -167,6 +173,8 @@ pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { } test "small crc32 ieee" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Ieee = Crc32SmallWithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); @@ -175,6 +183,8 @@ test "small crc32 ieee" { } test "small crc32 castagnoli" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index e7766f6445b1..415b206b6a65 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -314,7 +314,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { if (is_used) { const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index); const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc); - log.err("Memory leak detected: {}", .{stack_trace}); + log.err("Memory leak detected: {s}", .{stack_trace}); leaks = true; } if (bit_index == math.maxInt(u3)) @@ -342,7 +342,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { } var it = self.large_allocations.iterator(); while (it.next()) |large_alloc| { - log.err("Memory leak detected: {}", .{large_alloc.value.getStackTrace()}); + log.err("Memory leak detected: {s}", .{large_alloc.value.getStackTrace()}); leaks = true; } return leaks; @@ -443,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { .index = 0, }; std.debug.captureStackTrace(ret_addr, &free_stack_trace); - log.err("Allocation size {} bytes does not match free size {}. Allocation: {} Free: {}", .{ + log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {s} Free: {s}", .{ entry.value.bytes.len, old_mem.len, entry.value.getStackTrace(), @@ -526,7 +526,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { .index = 0, }; std.debug.captureStackTrace(ret_addr, &second_free_stack_trace); - log.err("Double free detected. Allocation: {} First free: {} Second free: {}", .{ + log.err("Double free detected. Allocation: {s} First free: {s} Second free: {s}", .{ alloc_stack_trace, free_stack_trace, second_free_stack_trace, diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index e698ce061ef7..b36949e53889 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -147,7 +147,7 @@ test "FixedBufferStream output" { var fbs = fixedBufferStream(&buf); const stream = fbs.writer(); - try stream.print("{}{}!", .{ "Hello", "World" }); + try stream.print("{s}{s}!", .{ "Hello", "World" }); testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten()); } diff --git a/lib/std/json.zig b/lib/std/json.zig index e412676fa390..f5be3a2094d2 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2642,9 +2642,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions if (self.expected_remaining.len < bytes.len) { std.debug.warn( \\====== expected this output: ========= - \\{} + \\{s} \\======== instead found this: ========= - \\{} + \\{s} \\====================================== , .{ self.expected_remaining, @@ -2655,9 +2655,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { std.debug.warn( \\====== expected this output: ========= - \\{} + \\{s} \\======== instead found this: ========= - \\{} + \\{s} \\====================================== , .{ self.expected_remaining[0..bytes.len], diff --git a/lib/std/linked_list.zig b/lib/std/linked_list.zig index b30a99f70823..2a6b58c8c9af 100644 --- a/lib/std/linked_list.zig +++ b/lib/std/linked_list.zig @@ -62,7 +62,7 @@ pub fn SinglyLinkedList(comptime T: type) type { /// This operation is O(N). pub fn countChildren(node: *const Node) usize { var count: usize = 0; - var it: ?*const Node = node; + var it: ?*const Node = node.next; while (it) |n| : (it = n.next) { count += 1; } @@ -123,6 +123,8 @@ test "basic SinglyLinkedList test" { const L = SinglyLinkedList(u32); var list = L{}; + testing.expect(list.len() == 0); + var one = L.Node{ .data = 1 }; var two = L.Node{ .data = 2 }; var three = L.Node{ .data = 3 }; @@ -135,6 +137,8 @@ test "basic SinglyLinkedList test" { two.insertAfter(&three); // {1, 2, 3, 5} three.insertAfter(&four); // {1, 2, 3, 4, 5} + testing.expect(list.len() == 5); + // Traverse forwards. { var it = list.first; diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index 8c8b26cf4595..8e542935339e 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -298,6 +298,20 @@ pub fn isNumber(comptime T: type) bool { }; } +pub fn isIntegerNumber(comptime T: type) bool { + return switch (@typeInfo(T)) { + .Int, .ComptimeInt => true, + else => false, + }; +} + +pub fn isFloatingNumber(comptime T: type) bool { + return switch (@typeInfo(T)) { + .Float, .ComptimeFloat => true, + else => false, + }; +} + test "std.meta.trait.isNumber" { const NotANumber = struct { number: u8, diff --git a/lib/std/net.zig b/lib/std/net.zig index 5b17714fae1a..b8f48b20202d 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -154,7 +154,7 @@ pub const Address = extern union { unreachable; } - try std.fmt.format(out_stream, "{}", .{&self.un.path}); + try std.fmt.format(out_stream, "{s}", .{&self.un.path}); }, else => unreachable, } diff --git a/lib/std/os.zig b/lib/std/os.zig index fb187252a18b..e9bf6379ccac 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4256,7 +4256,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { }, .linux => { var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; - const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; + const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{d}\x00", .{fd}) catch unreachable; const target = readlinkZ(std.meta.assumeSentinel(proc_path.ptr, 0), out_buffer) catch |err| { switch (err) { @@ -4487,7 +4487,7 @@ pub const UnexpectedError = error{ /// and you get an unexpected error. pub fn unexpectedErrno(err: usize) UnexpectedError { if (unexpected_error_tracing) { - std.debug.warn("unexpected errno: {}\n", .{err}); + std.debug.warn("unexpected errno: {d}\n", .{err}); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; @@ -4853,6 +4853,7 @@ pub fn send( error.NotDir => unreachable, error.NetworkUnreachable => unreachable, error.AddressNotAvailable => unreachable, + error.SocketNotConnected => unreachable, else => |e| return e, }; } diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 2d46b34bf702..73258ee1aa10 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1284,6 +1284,9 @@ pub const IORING_SETUP_CLAMP = 1 << 4; /// attach to existing wq pub const IORING_SETUP_ATTACH_WQ = 1 << 5; +/// start with ring disabled +pub const IORING_SETUP_R_DISABLED = 1 << 6; + pub const io_sqring_offsets = extern struct { /// offset of ring head head: u32, @@ -1430,6 +1433,11 @@ pub const io_uring_cqe = extern struct { flags: u32, }; +// io_uring_cqe.flags + +/// If set, the upper 16 bits are the buffer ID +pub const IORING_CQE_F_BUFFER = 1 << 0; + pub const IORING_OFF_SQ_RING = 0; pub const IORING_OFF_CQ_RING = 0x8000000; pub const IORING_OFF_SQES = 0x10000000; @@ -1439,7 +1447,7 @@ pub const IORING_ENTER_GETEVENTS = 1 << 0; pub const IORING_ENTER_SQ_WAKEUP = 1 << 1; // io_uring_register opcodes and arguments -pub const IORING_REGISTER = extern enum(u32) { +pub const IORING_REGISTER = extern enum(u8) { REGISTER_BUFFERS, UNREGISTER_BUFFERS, REGISTER_FILES, @@ -1451,11 +1459,13 @@ pub const IORING_REGISTER = extern enum(u32) { REGISTER_PROBE, REGISTER_PERSONALITY, UNREGISTER_PERSONALITY, + REGISTER_RESTRICTIONS, + REGISTER_ENABLE_RINGS, _, }; -pub const io_uring_files_update = struct { +pub const io_uring_files_update = extern struct { offset: u32, resv: u32, fds: u64, @@ -1463,7 +1473,7 @@ pub const io_uring_files_update = struct { pub const IO_URING_OP_SUPPORTED = 1 << 0; -pub const io_uring_probe_op = struct { +pub const io_uring_probe_op = extern struct { op: IORING_OP, resv: u8, @@ -1474,7 +1484,7 @@ pub const io_uring_probe_op = struct { resv2: u32, }; -pub const io_uring_probe = struct { +pub const io_uring_probe = extern struct { /// last opcode supported last_op: IORING_OP, @@ -1487,6 +1497,39 @@ pub const io_uring_probe = struct { // Followed by up to `ops_len` io_uring_probe_op structures }; +pub const io_uring_restriction = extern struct { + opcode: u16, + arg: extern union { + /// IORING_RESTRICTION_REGISTER_OP + register_op: IORING_REGISTER, + + /// IORING_RESTRICTION_SQE_OP + sqe_op: IORING_OP, + + /// IORING_RESTRICTION_SQE_FLAGS_* + sqe_flags: u8, + }, + resv: u8, + resv2: u32[3], +}; + +/// io_uring_restriction->opcode values +pub const IORING_RESTRICTION = extern enum(u8) { + /// Allow an io_uring_register(2) opcode + REGISTER_OP = 0, + + /// Allow an sqe opcode + SQE_OP = 1, + + /// Allow sqe flags + SQE_FLAGS_ALLOWED = 2, + + /// Require sqe flags (these flags must be set on each submission) + SQE_FLAGS_REQUIRED = 3, + + _, +}; + pub const utsname = extern struct { sysname: [64:0]u8, nodename: [64:0]u8, diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 32edbe6ac8fb..415d2192c59a 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -28,7 +28,7 @@ pub const IO_Uring = struct { /// call on how many entries the submission and completion queues will ultimately have, /// see https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L8027-L8050. /// Matches the interface of io_uring_queue_init() in liburing. - pub fn init(entries: u12, flags: u32) !IO_Uring { + pub fn init(entries: u13, flags: u32) !IO_Uring { var params = mem.zeroInit(io_uring_params, .{ .flags = flags, .sq_thread_idle = 1000, @@ -39,17 +39,15 @@ pub const IO_Uring = struct { /// A powerful way to setup an io_uring, if you want to tweak io_uring_params such as submission /// queue thread cpu affinity or thread idle timeout (the kernel and our default is 1 second). /// `params` is passed by reference because the kernel needs to modify the parameters. - /// You may only set the `flags`, `sq_thread_cpu` and `sq_thread_idle` parameters. - /// Every other parameter belongs to the kernel and must be zeroed. /// Matches the interface of io_uring_queue_init_params() in liburing. - pub fn init_params(entries: u12, p: *io_uring_params) !IO_Uring { + pub fn init_params(entries: u13, p: *io_uring_params) !IO_Uring { if (entries == 0) return error.EntriesZero; if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo; assert(p.sq_entries == 0); - assert(p.cq_entries == 0); + assert(p.cq_entries == 0 or p.flags & linux.IORING_SETUP_CQSIZE != 0); assert(p.features == 0); - assert(p.wq_fd == 0); + assert(p.wq_fd == 0 or p.flags & linux.IORING_SETUP_ATTACH_WQ != 0); assert(p.resv[0] == 0); assert(p.resv[1] == 0); assert(p.resv[2] == 0); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 40271cdc3880..1c6e5202a368 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1618,7 +1618,7 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { null, ); _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable; - std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] }); + std.debug.warn("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_u8[0..len] }); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; diff --git a/lib/std/process.zig b/lib/std/process.zig index 3f944f10b6d1..3529bf52cb12 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -596,7 +596,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u16, expected_args: []const []con for (expected_args) |expected_arg| { const arg = it.next(std.testing.allocator).? catch unreachable; defer std.testing.allocator.free(arg); - testing.expectEqualSlices(u8, expected_arg, arg); + testing.expectEqualStrings(expected_arg, arg); } testing.expect(it.next(std.testing.allocator) == null); } diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index c84667603e3f..fe120943d7ea 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -131,7 +131,11 @@ fn norm_zero_case(random: *Random, u: f64) f64 { } } -test "ziggurant normal dist sanity" { +const please_windows_dont_oom = std.Target.current.os.tag == .windows; + +test "normal dist sanity" { + if (please_windows_dont_oom) return error.SkipZigTest; + var prng = std.rand.DefaultPrng.init(0); var i: usize = 0; while (i < 1000) : (i += 1) { @@ -158,7 +162,9 @@ fn exp_zero_case(random: *Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } -test "ziggurant exp dist sanity" { +test "exp dist sanity" { + if (please_windows_dont_oom) return error.SkipZigTest; + var prng = std.rand.DefaultPrng.init(0); var i: usize = 0; while (i < 1000) : (i += 1) { @@ -166,6 +172,8 @@ test "ziggurant exp dist sanity" { } } -test "ziggurat table gen" { +test "table gen" { + if (please_windows_dont_oom) return error.SkipZigTest; + const table = NormDist; } diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index e3e90e7574f1..f2fa2dc3b806 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -98,7 +98,7 @@ pub fn main() !void { return usageAndErr(builder, false, stderr_stream); }; builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { - warn("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + warn("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); return usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--override-lib-dir")) { @@ -126,7 +126,7 @@ pub fn main() !void { builder.args = argsRest(args, arg_idx); break; } else { - warn("Unrecognized argument: {}\n\n", .{arg}); + warn("Unrecognized argument: {s}\n\n", .{arg}); return usageAndErr(builder, false, stderr_stream); } } else { @@ -168,7 +168,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void } try out_stream.print( - \\Usage: {} build [steps] [options] + \\Usage: {s} build [steps] [options] \\ \\Steps: \\ @@ -177,10 +177,10 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void const allocator = builder.allocator; for (builder.top_level_steps.items) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) - try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) + try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) else top_level_step.step.name; - try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description }); + try out_stream.print(" {s:<27} {s}\n", .{ name, top_level_step.description }); } try out_stream.writeAll( @@ -200,12 +200,12 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void try out_stream.print(" (none)\n", .{}); } else { for (builder.available_options_list.items) |option| { - const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{ + const name = try fmt.allocPrint(allocator, " -D{s}=[{s}]", .{ option.name, Builder.typeIdName(option.type_id), }); defer allocator.free(name); - try out_stream.print("{s:<29} {}\n", .{ name, option.description }); + try out_stream.print("{s:<29} {s}\n", .{ name, option.description }); } } diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index c92ffa21ac88..51cbafc133f4 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -172,7 +172,7 @@ test "strncmp" { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { if (builtin.is_test) { @setCold(true); - std.debug.panic("{}", .{msg}); + std.debug.panic("{s}", .{msg}); } if (builtin.os.tag != .freestanding and builtin.os.tag != .other) { std.os.abort(); diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index e0743cd4b70e..a06c568b2e78 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -324,7 +324,7 @@ pub usingnamespace @import("compiler_rt/atomics.zig"); pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - std.debug.panic("{}", .{msg}); + std.debug.panic("{s}", .{msg}); } else { unreachable; } diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 6c1a298759b0..f5a93298b531 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -48,7 +48,7 @@ pub fn main() anyerror!void { test_node.activate(); progress.refresh(); if (progress.terminal == null) { - std.debug.print("{}/{} {}... ", .{ i + 1, test_fn_list.len, test_fn.name }); + std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name }); } const result = if (test_fn.async_frame_size) |size| switch (io_mode) { .evented => blk: { @@ -62,7 +62,7 @@ pub fn main() anyerror!void { .blocking => { skip_count += 1; test_node.end(); - progress.log("{}...SKIP (async test)\n", .{test_fn.name}); + progress.log("{s}...SKIP (async test)\n", .{test_fn.name}); if (progress.terminal == null) std.debug.print("SKIP (async test)\n", .{}); continue; }, @@ -75,7 +75,7 @@ pub fn main() anyerror!void { error.SkipZigTest => { skip_count += 1; test_node.end(); - progress.log("{}...SKIP\n", .{test_fn.name}); + progress.log("{s}...SKIP\n", .{test_fn.name}); if (progress.terminal == null) std.debug.print("SKIP\n", .{}); }, else => { @@ -86,15 +86,15 @@ pub fn main() anyerror!void { } root_node.end(); if (ok_count == test_fn_list.len) { - std.debug.print("All {} tests passed.\n", .{ok_count}); + std.debug.print("All {d} tests passed.\n", .{ok_count}); } else { - std.debug.print("{} passed; {} skipped.\n", .{ ok_count, skip_count }); + std.debug.print("{d} passed; {d} skipped.\n", .{ ok_count, skip_count }); } if (log_err_count != 0) { - std.debug.print("{} errors were logged.\n", .{log_err_count}); + std.debug.print("{d} errors were logged.\n", .{log_err_count}); } if (leaks != 0) { - std.debug.print("{} tests leaked memory.\n", .{leaks}); + std.debug.print("{d} tests leaked memory.\n", .{leaks}); } if (leaks != 0 or log_err_count != 0) { std.process.exit(1); @@ -111,6 +111,6 @@ pub fn log( log_err_count += 1; } if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) { - std.debug.print("[{}] ({}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args); + std.debug.print("[{s}] ({s}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args); } } diff --git a/lib/std/start.zig b/lib/std/start.zig index a8fbf251f7c9..718d48130e3b 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -266,7 +266,7 @@ inline fn initEventLoopAndCallMain() u8 { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -295,7 +295,7 @@ inline fn initEventLoopAndCallWinMain() std.os.windows.INT { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -343,7 +343,7 @@ pub fn callMain() u8 { }, .ErrorUnion => { const result = root.main() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } diff --git a/lib/std/std.zig b/lib/std/std.zig index a1f3621023bf..091095bdb556 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -93,5 +93,41 @@ comptime { } test "" { - testing.refAllDecls(@This()); + if (builtin.os.tag == .windows) { + // We only test the Windows-relevant stuff to save memory because the CI + // server is hitting OOM. TODO revert this after stage2 arrives. + _ = ChildProcess; + _ = DynLib; + _ = mutex; + _ = Mutex; + _ = Progress; + _ = ResetEvent; + _ = SpinLock; + _ = StaticResetEvent; + _ = Target; + _ = Thread; + + _ = atomic; + _ = build; + _ = builtin; + _ = debug; + _ = event; + _ = fs; + _ = heap; + _ = io; + _ = log; + _ = macho; + _ = net; + _ = os; + _ = once; + _ = pdb; + _ = process; + _ = testing; + _ = time; + _ = unicode; + _ = zig; + _ = start; + } else { + testing.refAllDecls(@This()); + } } diff --git a/lib/std/target.zig b/lib/std/target.zig index 165a8fcae735..2b3518bc92f4 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -136,14 +136,14 @@ pub const Target = struct { ) !void { if (fmt.len > 0 and fmt[0] == 's') { if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { - try std.fmt.format(out_stream, ".{}", .{@tagName(self)}); + try std.fmt.format(out_stream, ".{s}", .{@tagName(self)}); } else { // TODO this code path breaks zig triples, but it is used in `builtin` try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, 0x{X:0>8})", .{@enumToInt(self)}); } } else { if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { - try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)}); + try std.fmt.format(out_stream, "WindowsVersion.{s}", .{@tagName(self)}); } else { try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)}); } @@ -1177,7 +1177,7 @@ pub const Target = struct { } pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { @@ -1381,7 +1381,7 @@ pub const Target = struct { if (self.abi == .android) { const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; - return print(&result, "/system/bin/linker{}", .{suffix}); + return print(&result, "/system/bin/linker{s}", .{suffix}); } if (self.abi.isMusl()) { @@ -1395,7 +1395,7 @@ pub const Target = struct { else => |arch| @tagName(arch), }; const arch_suffix = if (is_arm and self.abi.floatAbi() == .hard) "hf" else ""; - return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); + return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { @@ -1434,7 +1434,7 @@ pub const Target = struct { }; const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; - return print(&result, "/lib{}/{}", .{ lib_suffix, loader }); + return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader }); }, .powerpc => return copy(&result, "/lib/ld.so.1"), diff --git a/lib/std/testing.zig b/lib/std/testing.zig index a6f749b158d4..4f4a46dbf7c2 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -29,10 +29,10 @@ pub var zig_exe_path: []const u8 = undefined; /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void { if (actual_error_union) |actual_payload| { - std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload }); + std.debug.panic("expected error.{s}, found {}", .{ @errorName(expected_error), actual_payload }); } else |actual_error| { if (expected_error != actual_error) { - std.debug.panic("expected error.{}, found error.{}", .{ + std.debug.panic("expected error.{s}, found error.{s}", .{ @errorName(expected_error), @errorName(actual_error), }); @@ -60,7 +60,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { .Type => { if (actual != expected) { - std.debug.panic("expected type {}, found type {}", .{ @typeName(expected), @typeName(actual) }); + std.debug.panic("expected type {s}, found type {s}", .{ @typeName(expected), @typeName(actual) }); } }, @@ -258,7 +258,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const // If the child type is u8 and no weird bytes, we could print it as strings // Even for the length difference, it would be useful to see the values of the slices probably. if (expected.len != actual.len) { - std.debug.panic("slice lengths differ. expected {}, found {}", .{ expected.len, actual.len }); + std.debug.panic("slice lengths differ. expected {d}, found {d}", .{ expected.len, actual.len }); } var i: usize = 0; while (i < expected.len) : (i += 1) { @@ -360,7 +360,7 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void { for (expected[0..diff_index]) |value| { if (value == '\n') diff_line_number += 1; } - print("First difference occurs on line {}:\n", .{diff_line_number}); + print("First difference occurs on line {d}:\n", .{diff_line_number}); print("expected:\n", .{}); printIndicatorLine(expected, diff_index); @@ -416,15 +416,15 @@ fn printWithVisibleNewlines(source: []const u8) void { while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) { printLine(source[i .. i + nl]); } - print("{}␃\n", .{source[i..]}); // End of Text symbol (ETX) + print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX) } fn printLine(line: []const u8) void { if (line.len != 0) switch (line[line.len - 1]) { - ' ', '\t' => print("{}⏎\n", .{line}), // Carriage return symbol, + ' ', '\t' => print("{s}⏎\n", .{line}), // Carriage return symbol, else => {}, }; - print("{}\n", .{line}); + print("{s}\n", .{line}); } test "" { diff --git a/lib/std/thread.zig b/lib/std/thread.zig index 7d63b5f6441b..0fee13b05768 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -186,7 +186,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -247,7 +247,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -281,7 +281,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index c0e72ea82221..eb894fa1f640 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -281,41 +281,41 @@ pub const Error = union(enum) { } } - pub const InvalidToken = SingleTokenError("Invalid token '{}'"); - pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{}'"); - pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{}'"); - pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{}'"); - pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{}'"); - pub const ExpectedStatement = SingleTokenError("Expected statement, found '{}'"); - pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{}'"); - pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{}'"); - pub const ExpectedFn = SingleTokenError("Expected function, found '{}'"); - pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{}'"); - pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{}'"); - pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{}'"); - pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{}'"); - pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{}'"); - pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{}'"); - pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{}'"); - pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{}'"); - pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{}'"); - pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{}'"); - pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{}'"); - pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{}'"); - pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{}'"); - pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{}'"); - pub const ExpectedExpr = SingleTokenError("Expected expression, found '{}'"); - pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{}'"); - pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{}'"); - pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{}'"); - pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{}'"); - pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{}'"); - pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{}'"); - pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{}'"); - pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{}'"); - pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{}'"); - pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'"); - pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{}'"); + pub const InvalidToken = SingleTokenError("Invalid token '{s}'"); + pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'"); + pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'"); + pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'"); + pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'"); + pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'"); + pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'"); + pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'"); + pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'"); + pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{s}'"); + pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{s}'"); + pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'"); + pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'"); + pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'"); + pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'"); + pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'"); + pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'"); + pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'"); + pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'"); + pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{s}'"); + pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'"); + pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'"); + pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'"); + pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'"); + pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'"); + pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'"); + pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'"); + pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'"); + pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'"); + pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'"); + pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'"); + pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'"); + pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'"); + pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'"); + pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'"); pub const ExpectedParamType = SimpleError("Expected parameter type"); pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); @@ -332,7 +332,7 @@ pub const Error = union(enum) { node: *Node, pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void { - return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{ + return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {s}", .{ @tagName(self.node.tag), }); } @@ -343,7 +343,7 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void { return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++ - @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)}); + @tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(self.node.tag)}); } }; @@ -355,11 +355,11 @@ pub const Error = union(enum) { const found_token = tokens[self.token]; switch (found_token) { .Invalid => { - return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); + return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); }, else => { const token_name = found_token.symbol(); - return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); + return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); }, } } @@ -371,7 +371,7 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void { const actual_token = tokens[self.token]; - return stream.print("expected ',' or '{}', found '{}'", .{ + return stream.print("expected ',' or '{s}', found '{s}'", .{ self.end_id.symbol(), actual_token.symbol(), }); @@ -843,7 +843,7 @@ pub const Node = struct { std.debug.warn(" ", .{}); } } - std.debug.warn("{}\n", .{@tagName(self.tag)}); + std.debug.warn("{s}\n", .{@tagName(self.tag)}); var child_i: usize = 0; while (self.iterate(child_i)) |child| : (child_i += 1) { @@ -1418,7 +1418,7 @@ pub const Node = struct { @alignOf(ParamDecl), @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len, ); - std.debug.print("{*} flags: {b} name_token: {} {*} params_len: {}\n", .{ + std.debug.print("{*} flags: {b} name_token: {s} {*} params_len: {d}\n", .{ self, self.trailer_flags.bits, self.getNameToken(), diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index e00f83e422ed..060dd83caf7f 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -519,7 +519,7 @@ pub const CrossTarget = struct { var result = std.ArrayList(u8).init(allocator); defer result.deinit(); - try result.outStream().print("{}-{}", .{ arch_name, os_name }); + try result.outStream().print("{s}-{s}", .{ arch_name, os_name }); // The zig target syntax does not allow specifying a max os version with no min, so // if either are present, we need the min. @@ -539,9 +539,9 @@ pub const CrossTarget = struct { } if (self.glibc_version) |v| { - try result.outStream().print("-{}.{}", .{ @tagName(self.getAbi()), v }); + try result.outStream().print("-{s}.{}", .{ @tagName(self.getAbi()), v }); } else if (self.abi) |abi| { - try result.outStream().print("-{}", .{@tagName(abi)}); + try result.outStream().print("-{s}", .{@tagName(abi)}); } return result.toOwnedSlice(); @@ -595,7 +595,7 @@ pub const CrossTarget = struct { .Dynamic => "", }; - return std.fmt.allocPrint(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix }); } pub const Executor = union(enum) { @@ -790,7 +790,7 @@ test "CrossTarget.parse" { var buf: [256]u8 = undefined; const triple = std.fmt.bufPrint( buf[0..], - "native-native-{}.2.1.1", + "native-native-{s}.2.1.1", .{@tagName(std.Target.current.abi)}, ) catch unreachable; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2879f2652b18..63d04f9a80ba 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3742,9 +3742,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b for (tree.errors) |*parse_error| { const token = tree.token_locs[parse_error.loc()]; const loc = tree.tokenLocation(0, parse_error.loc()); - try stderr.print("(memory buffer):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 }); + try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); try tree.renderError(parse_error, stderr); - try stderr.print("\n{}\n", .{source[loc.line_start..loc.line_end]}); + try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { @@ -3800,7 +3800,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { error.OutOfMemory => { if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { warn( - "\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", .{ fail_index, needed_alloc_count, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index b8b0d8c1f14c..6931b1925010 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -41,7 +41,7 @@ fn renderRoot( for (tree.token_ids) |token_id, i| { if (token_id != .LineComment) break; const token_loc = tree.token_locs[i]; - try ais.writer().print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")}); + try ais.writer().print("{s}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")}); const next_token = tree.token_locs[i + 1]; const loc = tree.tokenLocationLoc(token_loc.end, next_token); if (loc.line >= 2) { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 574ccfb32d80..a052c81a7daa 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -22,6 +22,7 @@ pub const getSDKPath = macos.getSDKPath; pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), lib_dirs: ArrayList([:0]u8), + framework_dirs: ArrayList([:0]u8), rpaths: ArrayList([:0]u8), warnings: ArrayList([:0]u8), @@ -29,6 +30,7 @@ pub const NativePaths = struct { var self: NativePaths = .{ .include_dirs = ArrayList([:0]u8).init(allocator), .lib_dirs = ArrayList([:0]u8).init(allocator), + .framework_dirs = ArrayList([:0]u8).init(allocator), .rpaths = ArrayList([:0]u8).init(allocator), .warnings = ArrayList([:0]u8).init(allocator), }; @@ -49,7 +51,7 @@ pub const NativePaths = struct { }; try self.addIncludeDir(include_path); } else { - try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}", .{word}); + try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); break; } } @@ -75,7 +77,7 @@ pub const NativePaths = struct { const lib_path = word[2..]; try self.addLibDir(lib_path); } else { - try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {}", .{word}); + try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {s}", .{word}); break; } } @@ -88,6 +90,19 @@ pub const NativePaths = struct { return self; } + if (comptime Target.current.isDarwin()) { + try self.addIncludeDir("/usr/include"); + try self.addIncludeDir("/usr/local/include"); + + try self.addLibDir("/usr/lib"); + try self.addLibDir("/usr/local/lib"); + + try self.addFrameworkDir("/Library/Frameworks"); + try self.addFrameworkDir("/System/Library/Frameworks"); + + return self; + } + if (!is_windows) { const triple = try Target.current.linuxTriple(allocator); const qual = Target.current.cpu.arch.ptrBitWidth(); @@ -98,22 +113,22 @@ pub const NativePaths = struct { // TODO: some of these are suspect and should only be added on some systems. audit needed. try self.addIncludeDir("/usr/local/include"); - try self.addLibDirFmt("/usr/local/lib{}", .{qual}); + try self.addLibDirFmt("/usr/local/lib{d}", .{qual}); try self.addLibDir("/usr/local/lib"); - try self.addIncludeDirFmt("/usr/include/{}", .{triple}); - try self.addLibDirFmt("/usr/lib/{}", .{triple}); + try self.addIncludeDirFmt("/usr/include/{s}", .{triple}); + try self.addLibDirFmt("/usr/lib/{s}", .{triple}); try self.addIncludeDir("/usr/include"); - try self.addLibDirFmt("/lib{}", .{qual}); + try self.addLibDirFmt("/lib{d}", .{qual}); try self.addLibDir("/lib"); - try self.addLibDirFmt("/usr/lib{}", .{qual}); + try self.addLibDirFmt("/usr/lib{d}", .{qual}); try self.addLibDir("/usr/lib"); // example: on a 64-bit debian-based linux distro, with zlib installed from apt: // zlib.h is in /usr/include (added above) // libz.so.1 is in /lib/x86_64-linux-gnu (added here) - try self.addLibDirFmt("/lib/{}", .{triple}); + try self.addLibDirFmt("/lib/{s}", .{triple}); } return self; @@ -122,6 +137,7 @@ pub const NativePaths = struct { pub fn deinit(self: *NativePaths) void { deinitArray(&self.include_dirs); deinitArray(&self.lib_dirs); + deinitArray(&self.framework_dirs); deinitArray(&self.rpaths); deinitArray(&self.warnings); self.* = undefined; @@ -158,6 +174,16 @@ pub const NativePaths = struct { return self.appendArray(&self.warnings, s); } + pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void { + return self.appendArray(&self.framework_dirs, s); + } + + pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { + const item = try std.fmt.allocPrint0(self.framework_dirs.allocator, fmt, args); + errdefer self.framework_dirs.allocator.free(item); + try self.framework_dirs.append(item); + } + pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args); errdefer self.warnings.allocator.free(item); @@ -237,8 +263,7 @@ pub const NativeTargetInfo = struct { // `---` `` ``--> Sub-version (Starting from Windows 10 onwards) // \ `--> Service pack (Always zero in the constants defined) // `--> OS version (Major & minor) - const os_ver: u16 = - @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | + const os_ver: u16 = @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | @intCast(u16, version_info.dwMinorVersion & 0xff); const sp_ver: u8 = 0; const sub_ver: u8 = if (os_ver >= 0x0A00) subver: { diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/macos.zig index fbc0b5149bdf..7c4e255cc592 100644 --- a/lib/std/zig/system/macos.zig +++ b/lib/std/zig/system/macos.zig @@ -450,7 +450,7 @@ test "version_from_build" { for (known) |pair| { var buf: [32]u8 = undefined; const ver = try version_from_build(pair[0]); - const sver = try std.fmt.bufPrint(buf[0..], "{}.{}.{}", .{ ver.major, ver.minor, ver.patch }); + const sver = try std.fmt.bufPrint(buf[0..], "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); std.testing.expect(std.mem.eql(u8, sver, pair[1])); } } @@ -468,7 +468,7 @@ pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { allocator.free(result.stdout); } if (result.stderr.len != 0) { - std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {}", .{result.stderr}); + std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr}); } if (result.term.Exited != 0) { return error.ProcessTerminated; diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index 4e50b558160b..dcbf71763822 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -334,7 +334,7 @@ pub const Tokenizer = struct { /// For debugging purposes pub fn dump(self: *Tokenizer, token: *const Token) void { - std.debug.warn("{} \"{}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); + std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); } pub fn init(buffer: []const u8) Tokenizer { @@ -2046,7 +2046,7 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); if (token.id != expected_token_id) { - std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); + std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); } } const last_token = tokenizer.next(); diff --git a/src/Cache.zig b/src/Cache.zig index 3d33226f27b3..9f8beaabc72d 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -12,14 +12,6 @@ const mem = std.mem; const fmt = std.fmt; const Allocator = std.mem.Allocator; -/// Process-scoped map keeping track of all locked Cache hashes, to detect deadlocks. -/// This protection is conditionally compiled depending on `want_debug_deadlock`. -var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{}; -var all_cache_digest_lock: std.Mutex = .{}; -const want_debug_deadlock = std.debug.runtime_safety; -const DebugBinDigest = if (want_debug_deadlock) BinDigest else void; -const null_debug_bin_digest = if (want_debug_deadlock) ([1]u8{0} ** bin_digest_len) else {}; - /// Be sure to call `Manifest.deinit` after successful initialization. pub fn obtain(cache: *const Cache) Manifest { return Manifest{ @@ -168,15 +160,8 @@ pub const HashHelper = struct { pub const Lock = struct { manifest_file: fs.File, - debug_bin_digest: DebugBinDigest, pub fn release(lock: *Lock) void { - if (want_debug_deadlock) { - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - all_cache_digest_set.removeAssertDiscard(lock.debug_bin_digest); - } lock.manifest_file.close(); lock.* = undefined; } @@ -193,7 +178,6 @@ pub const Manifest = struct { manifest_dirty: bool, files: std.ArrayListUnmanaged(File) = .{}, hex_digest: [hex_digest_len]u8, - debug_bin_digest: DebugBinDigest = null_debug_bin_digest, /// Populated when hit() returns an error because of one /// of the files listed in the manifest. failed_file_index: ?usize = null, @@ -266,23 +250,6 @@ pub const Manifest = struct { var bin_digest: BinDigest = undefined; self.hash.hasher.final(&bin_digest); - if (want_debug_deadlock) { - self.debug_bin_digest = bin_digest; - - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - const gop = try all_cache_digest_set.getOrPut(self.cache.gpa, bin_digest); - if (gop.found_existing) { - std.debug.print("Cache deadlock detected in Cache.hit. Manifest has {d} files:\n", .{self.files.items.len}); - for (self.files.items) |file| { - const p: []const u8 = file.path orelse "(null)"; - std.debug.print(" file: {s}\n", .{p}); - } - @panic("Cache deadlock detected"); - } - } - _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable; self.hash.hasher = hasher_init; @@ -549,7 +516,7 @@ pub const Manifest = struct { .target, .target_must_resolve, .prereq => {}, else => |err| { try err.printError(error_buf.writer()); - std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + std.log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); return error.InvalidDepFile; }, } @@ -561,7 +528,7 @@ pub const Manifest = struct { .prereq => |bytes| try self.addFilePost(bytes), else => |err| { try err.printError(error_buf.writer()); - std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + std.log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); return error.InvalidDepFile; }, } @@ -619,10 +586,8 @@ pub const Manifest = struct { pub fn toOwnedLock(self: *Manifest) Lock { const lock: Lock = .{ .manifest_file = self.manifest_file.?, - .debug_bin_digest = self.debug_bin_digest, }; self.manifest_file = null; - self.debug_bin_digest = null_debug_bin_digest; return lock; } @@ -630,14 +595,6 @@ pub const Manifest = struct { /// `Manifest.hit` must be called first. /// Don't forget to call `writeManifest` before this! pub fn deinit(self: *Manifest) void { - if (want_debug_deadlock) { - if (!mem.eql(u8, &self.debug_bin_digest, &null_debug_bin_digest)) { - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - all_cache_digest_set.removeAssertDiscard(self.debug_bin_digest); - } - } if (self.manifest_file) |file| { file.close(); } @@ -721,10 +678,6 @@ test "cache file and then recall it" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); @@ -803,10 +756,6 @@ test "check that changing a file makes cache fail" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_file = "cache_hash_change_file_test.txt"; @@ -883,10 +832,6 @@ test "no file inputs" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_manifest_dir = "no_file_inputs_manifest_dir"; defer cwd.deleteTree(temp_manifest_dir) catch {}; @@ -932,10 +877,6 @@ test "Manifest with files added after initial hash work" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_file1 = "cache_hash_post_file_test1.txt"; diff --git a/src/Compilation.zig b/src/Compilation.zig index cd3db84ec286..991252043743 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1173,6 +1173,7 @@ pub fn destroy(self: *Compilation) void { const gpa = self.gpa; self.work_queue.deinit(); + self.c_object_work_queue.deinit(); { var it = self.crt_files.iterator(); @@ -1202,6 +1203,10 @@ pub fn destroy(self: *Compilation) void { crt_file.deinit(gpa); } + if (self.glibc_so_files) |*glibc_file| { + glibc_file.deinit(gpa); + } + for (self.c_object_table.items()) |entry| { entry.key.destroy(gpa); } @@ -1456,27 +1461,34 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor => continue, .complete, .codegen_failure_retryable => { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { const func = payload.data; - switch (func.analysis) { + switch (func.state) { .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { error.AnalysisFail => { - assert(func.analysis != .in_progress); + assert(func.state != .in_progress); continue; }, error.OutOfMemory => return error.OutOfMemory, }, .in_progress => unreachable, + .inline_only => unreachable, // don't queue work for this .sema_failure, .dependency_failure => continue, .success => {}, } - // Here we tack on additional allocations to the Decl's arena. The allocations are - // lifetime annotations in the ZIR. + // Here we tack on additional allocations to the Decl's arena. The allocations + // are lifetime annotations in the ZIR. var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; - log.debug("analyze liveness of {}\n", .{decl.name}); - try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success); + log.debug("analyze liveness of {s}\n", .{decl.name}); + try liveness.analyze(module.gpa, &decl_arena.allocator, func.body); + + if (std.builtin.mode == .Debug and self.verbose_ir) { + func.dump(module.*); + } } assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); @@ -1492,7 +1504,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to codegen: {}", + "unable to codegen: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1512,7 +1524,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to generate C header: {}", + "unable to generate C header: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1522,6 +1534,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }, }, .analyze_decl => |decl| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; module.ensureDeclAnalyzed(decl) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -1529,13 +1543,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .update_line_number => |decl| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; self.bin_file.updateDeclLineNumber(module, decl) catch |err| { try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to update line number: {}", + "unable to update line number: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1544,56 +1560,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .glibc_crt_file => |crt_file| { glibc.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc CRT file: {}", .{@errorName(err)}); + fatal("unable to build glibc CRT file: {s}", .{@errorName(err)}); }; }, .glibc_shared_objects => { glibc.buildSharedObjects(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc shared objects: {}", .{@errorName(err)}); + fatal("unable to build glibc shared objects: {s}", .{@errorName(err)}); }; }, .musl_crt_file => |crt_file| { musl.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build musl CRT file: {}", .{@errorName(err)}); + fatal("unable to build musl CRT file: {s}", .{@errorName(err)}); }; }, .mingw_crt_file => |crt_file| { mingw.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)}); + fatal("unable to build mingw-w64 CRT file: {s}", .{@errorName(err)}); }; }, .windows_import_lib => |index| { const link_lib = self.bin_file.options.system_libs.items()[index].key; mingw.buildImportLib(self, link_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)}); + fatal("unable to generate DLL import .lib file: {s}", .{@errorName(err)}); }; }, .libunwind => { libunwind.buildStaticLib(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libunwind: {}", .{@errorName(err)}); + fatal("unable to build libunwind: {s}", .{@errorName(err)}); }; }, .libcxx => { libcxx.buildLibCXX(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxx: {}", .{@errorName(err)}); + fatal("unable to build libcxx: {s}", .{@errorName(err)}); }; }, .libcxxabi => { libcxx.buildLibCXXABI(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxxabi: {}", .{@errorName(err)}); + fatal("unable to build libcxxabi: {s}", .{@errorName(err)}); }; }, .libtsan => { libtsan.buildTsan(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build TSAN library: {}", .{@errorName(err)}); + fatal("unable to build TSAN library: {s}", .{@errorName(err)}); }; }, .compiler_rt_lib => { @@ -1611,20 +1627,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .libssp => { self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libssp: {}", .{@errorName(err)}); + fatal("unable to build libssp: {s}", .{@errorName(err)}); }; }, .zig_libc => { self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)}); + fatal("unable to build zig's multitarget libc: {s}", .{@errorName(err)}); }; }, .generate_builtin_zig => { // This Job is only queued up if there is a zig module. self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to update builtin.zig file: {}", .{@errorName(err)}); + fatal("unable to update builtin.zig file: {s}", .{@errorName(err)}); }; }, .stage1_module => { @@ -1704,11 +1720,11 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ tmp_dir_sub_path, cimport_basename, }); - const out_dep_path = try std.fmt.allocPrint(arena, "{}.d", .{out_h_path}); + const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path}); try zig_cache_tmp_dir.writeFile(cimport_basename, c_src); if (comp.verbose_cimport) { - log.info("C import source: {}", .{out_h_path}); + log.info("C import source: {s}", .{out_h_path}); } var argv = std.ArrayList([]const u8).init(comp.gpa); @@ -1755,7 +1771,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { defer tree.deinit(); if (comp.verbose_cimport) { - log.info("C import .d file: {}", .{out_dep_path}); + log.info("C import .d file: {s}", .{out_dep_path}); } const dep_basename = std.fs.path.basename(out_dep_path); @@ -1775,7 +1791,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { try bos.flush(); man.writeManifest() catch |err| { - log.warn("failed to write cache manifest for C import: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); }; break :digest digest; @@ -1785,7 +1801,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { "o", &digest, cimport_zig_basename, }); if (comp.verbose_cimport) { - log.info("C import output: {}\n", .{out_zig_path}); + log.info("C import output: {s}\n", .{out_zig_path}); } return CImportResult{ .out_zig_path = out_zig_path, @@ -1946,7 +1962,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * child.stderr_behavior = .Inherit; const term = child.spawnAndWait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); }; switch (term) { .Exited => |code| { @@ -1974,7 +1990,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); const term = child.wait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); }; switch (term) { @@ -1982,12 +1998,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * if (code != 0) { // TODO parse clang stderr and turn it into an error message // and then call failCObjWithOwnedErrorMsg - log.err("clang failed with stderr: {}", .{stderr}); - return comp.failCObj(c_object, "clang exited with code {}", .{code}); + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {d}", .{code}); } }, else => { - log.err("clang terminated with stderr: {}", .{stderr}); + log.err("clang terminated with stderr: {s}", .{stderr}); return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); }, } @@ -1999,7 +2015,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { - log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); }; } @@ -2015,7 +2031,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename); man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) }); + log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); }; break :blk digest; }; @@ -2034,7 +2050,7 @@ pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) er const s = std.fs.path.sep_str; const rand_int = std.crypto.random.int(u64); if (comp.local_cache_directory.path) |p| { - return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); + return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); } else { return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); } @@ -2079,12 +2095,6 @@ pub fn addCCArgs( try argv.append("-ffunction-sections"); } - try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2); - for (comp.bin_file.options.framework_dirs) |framework_dir| { - argv.appendAssumeCapacity("-iframework"); - argv.appendAssumeCapacity(framework_dir); - } - if (comp.bin_file.options.link_libcpp) { const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "libcxx", "include", @@ -2150,7 +2160,7 @@ pub fn addCCArgs( } const mcmodel = comp.bin_file.options.machine_code_model; if (mcmodel != .default) { - try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)})); + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)})); } switch (target.os.tag) { @@ -2503,22 +2513,22 @@ fn detectLibCIncludeDirs( const s = std.fs.path.sep_str; const arch_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{ zig_lib_dir, arch_name, os_name, abi_name }, ); const generic_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", .{ zig_lib_dir, generic_name }, ); const arch_os_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, ); const generic_os_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", .{ zig_lib_dir, os_name }, ); @@ -2637,9 +2647,9 @@ fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void { pub fn dump_argv(argv: []const []const u8) void { for (argv[0 .. argv.len - 1]) |arg| { - std.debug.print("{} ", .{arg}); + std.debug.print("{s} ", .{arg}); } - std.debug.print("{}\n", .{argv[argv.len - 1]}); + std.debug.print("{s}\n", .{argv[argv.len - 1]}); } pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 { @@ -2659,15 +2669,15 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\pub const arch = Target.current.cpu.arch; \\/// Deprecated \\pub const endian = Target.current.cpu.arch.endian(); - \\pub const output_mode = OutputMode.{}; - \\pub const link_mode = LinkMode.{}; + \\pub const output_mode = OutputMode.{z}; + \\pub const link_mode = LinkMode.{z}; \\pub const is_test = {}; \\pub const single_threaded = {}; - \\pub const abi = Abi.{}; + \\pub const abi = Abi.{z}; \\pub const cpu: Cpu = Cpu{{ - \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ .arch = .{z}, + \\ .model = &Target.{z}.cpu.{z}, + \\ .features = Target.{z}.featureSet(&[_]Target.{z}.Feature{{ \\ , .{ @tagName(comp.bin_file.options.output_mode), @@ -2698,7 +2708,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\ }}), \\}}; \\pub const os = Os{{ - \\ .tag = .{}, + \\ .tag = .{z}, \\ .version_range = .{{ , .{@tagName(target.os.tag)}, @@ -2784,8 +2794,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_link_libc); try buffer.writer().print( - \\pub const object_format = ObjectFormat.{}; - \\pub const mode = Mode.{}; + \\pub const object_format = ObjectFormat.{z}; + \\pub const mode = Mode.{z}; \\pub const link_libc = {}; \\pub const link_libcpp = {}; \\pub const have_error_return_tracing = {}; @@ -2793,7 +2803,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\pub const position_independent_code = {}; \\pub const position_independent_executable = {}; \\pub const strip_debug_info = {}; - \\pub const code_model = CodeModel.{}; + \\pub const code_model = CodeModel.{z}; \\ , .{ @tagName(comp.bin_file.options.object_format), @@ -3019,7 +3029,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("stage1 {} new_digest={} error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); + log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -3027,7 +3037,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) break :hit; - log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); + log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); var flags_bytes: [1]u8 = undefined; _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { log.warn("bad cache stage1 digest: '{s}'", .{prev_digest}); @@ -3050,7 +3060,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); return; } - log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); + log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); man.unhit(prev_hash_state, input_file_count); } @@ -3195,7 +3205,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node // Update the small file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); - log.debug("stage1 {} final digest={} flags={x}", .{ + log.debug("stage1 {s} final digest={} flags={x}", .{ mod.root_pkg.root_src_path, digest, stage1_flags_byte, }); var digest_plus_flags: [digest.len + 2]u8 = undefined; @@ -3208,11 +3218,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup, }); Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| { - log.warn("failed to save stage1 hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)}); }; // Failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig index 99db6e4b3ca6..0fe1310cd8a4 100644 --- a/src/DepTokenizer.zig +++ b/src/DepTokenizer.zig @@ -366,14 +366,14 @@ pub const Token = union(enum) { .incomplete_quoted_prerequisite, .incomplete_target, => |index_and_bytes| { - try writer.print("{} '", .{self.errStr()}); + try writer.print("{s} '", .{self.errStr()}); if (self == .incomplete_target) { const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; try tmp.resolve(writer); } else { try printCharValues(writer, index_and_bytes.bytes); } - try writer.print("' at position {}", .{index_and_bytes.index}); + try writer.print("' at position {d}", .{index_and_bytes.index}); }, .invalid_target, .bad_target_escape, @@ -383,7 +383,7 @@ pub const Token = union(enum) { => |index_and_char| { try writer.writeAll("illegal char "); try printUnderstandableChar(writer, index_and_char.char); - try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() }); + try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() }); }, } } @@ -927,7 +927,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { try out.writeAll("\n"); try printSection(out, "<<<< input", input); try printSection(out, "==== expect", expect); - try printSection(out, ">>>> got", got); + try printSection(out, ">>>> got", buffer.items); try printRuler(out); testing.expect(false); @@ -943,7 +943,7 @@ fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void { fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; - var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); + var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len }); try out.writeAll(text); var i: usize = text.len; const end = 79; diff --git a/src/Module.zig b/src/Module.zig index ca5d6dbe7acc..fdb782ee1a1b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -23,6 +23,8 @@ const trace = @import("tracy.zig").trace; const astgen = @import("astgen.zig"); const zir_sema = @import("zir_sema.zig"); +const default_eval_branch_quota = 1000; + /// General-purpose allocator. Used for both temporary and long-term storage. gpa: *Allocator, comp: *Compilation, @@ -248,7 +250,7 @@ pub const Decl = struct { pub fn dump(self: *Decl) void { const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src); - std.debug.print("{}:{}:{} name={} status={}", .{ + std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ self.scope.sub_file_path, loc.line + 1, loc.column + 1, @@ -268,6 +270,11 @@ pub const Decl = struct { } } + /// Asserts that the `Decl` is part of AST and not ZIRModule. + pub fn getFileScope(self: *Decl) *Scope.File { + return self.scope.cast(Scope.Container).?.file_scope; + } + fn removeDependant(self: *Decl, other: *Decl) void { self.dependants.removeAssertDiscard(other); } @@ -281,46 +288,32 @@ pub const Decl = struct { /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. pub const Fn = struct { - /// This memory owned by the Decl's TypedValue.Managed arena allocator. - analysis: union(enum) { - queued: *ZIR, + owner_decl: *Decl, + /// Contains un-analyzed ZIR instructions generated from Zig source AST. + /// Even after we finish analysis, the ZIR is kept in memory, so that + /// comptime and inline function calls can happen. + zir: zir.Module.Body, + /// undefined unless analysis state is `success`. + body: Body, + state: Analysis, + + pub const Analysis = enum { + queued, + /// This function intentionally only has ZIR generated because it is marked + /// inline, which means no runtime version of the function will be generated. + inline_only, in_progress, /// There will be a corresponding ErrorMsg in Module.failed_decls sema_failure, - /// This Fn might be OK but it depends on another Decl which did not successfully complete - /// semantic analysis. + /// This Fn might be OK but it depends on another Decl which did not + /// successfully complete semantic analysis. dependency_failure, - success: Body, - }, - owner_decl: *Decl, - - /// This memory is temporary and points to stack memory for the duration - /// of Fn analysis. - pub const Analysis = struct { - inner_block: Scope.Block, - }; - - /// Contains un-analyzed ZIR instructions generated from Zig source AST. - pub const ZIR = struct { - body: zir.Module.Body, - arena: std.heap.ArenaAllocator.State, + success, }; /// For debugging purposes. pub fn dump(self: *Fn, mod: Module) void { - std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name}); - switch (self.analysis) { - .queued => { - std.debug.print("queued\n", .{}); - }, - .in_progress => { - std.debug.print("in_progress\n", .{}); - }, - else => { - std.debug.print("\n", .{}); - zir.dumpFn(mod, self); - }, - } + zir.dumpFn(mod, self); } }; @@ -632,7 +625,7 @@ pub const Scope = struct { pub fn dumpSrc(self: *File, src: usize) void { const loc = std.zig.findLineColumn(self.source.bytes, src); - std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); + std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); } pub fn getSource(self: *File, module: *Module) ![:0]const u8 { @@ -730,7 +723,7 @@ pub const Scope = struct { pub fn dumpSrc(self: *ZIRModule, src: usize) void { const loc = std.zig.findLineColumn(self.source.bytes, src); - std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); + std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); } pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { @@ -761,21 +754,61 @@ pub const Scope = struct { /// during semantic analysis of the block. pub const Block = struct { pub const base_tag: Tag = .block; + base: Scope = Scope{ .tag = base_tag }, parent: ?*Block, + /// Maps ZIR to TZIR. Shared to sub-blocks. + inst_table: *InstTable, func: ?*Fn, decl: *Decl, instructions: ArrayListUnmanaged(*Inst), /// Points to the arena allocator of DeclAnalysis arena: *Allocator, label: ?Label = null, + inlining: ?*Inlining, is_comptime: bool, + /// Shared to sub-blocks. + branch_quota: *u32, + pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst); + + /// This `Block` maps a block ZIR instruction to the corresponding + /// TZIR instruction for break instruction analysis. pub const Label = struct { zir_block: *zir.Inst.Block, + merges: Merges, + }; + + /// This `Block` indicates that an inline function call is happening + /// and return instructions should be analyzed as a break instruction + /// to this TZIR block instruction. + /// It is shared among all the blocks in an inline or comptime called + /// function. + pub const Inlining = struct { + /// Shared state among the entire inline/comptime call stack. + shared: *Shared, + /// We use this to count from 0 so that arg instructions know + /// which parameter index they are, without having to store + /// a parameter index with each arg instruction. + param_index: usize, + casted_args: []*Inst, + merges: Merges, + + pub const Shared = struct { + caller: ?*Fn, + branch_count: u32, + }; + }; + + pub const Merges = struct { results: ArrayListUnmanaged(*Inst), block_inst: *Inst.Block, }; + + /// For debugging purposes. + pub fn dump(self: *Block, mod: Module) void { + zir.dumpBlock(mod, self); + } }; /// This is a temporary structure, references to it are valid only @@ -918,7 +951,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { .complete => return, .outdated => blk: { - log.debug("re-analyzing {}\n", .{decl.name}); + log.debug("re-analyzing {s}\n", .{decl.name}); // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. @@ -953,7 +986,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( self.gpa, decl.src(), - "unable to analyze: {}", + "unable to analyze: {s}", .{@errorName(err)}, )); decl.analysis = .sema_failure_retryable; @@ -992,11 +1025,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer tracy.end(); const container_scope = decl.scope.cast(Scope.Container).?; - const tree = try self.getAstTree(container_scope); + const tree = try self.getAstTree(container_scope.file_scope); const ast_node = tree.root_node.decls()[decl.src_index]; switch (ast_node.tag) { .FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node); + const fn_proto = ast_node.castTag(.FnProto).?; decl.analysis = .in_progress; @@ -1062,7 +1095,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .param_types = param_types, }, .{}); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; } @@ -1071,13 +1104,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, + .inlining = null, .is_comptime = false, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); @@ -1113,14 +1154,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); - const fn_zir = blk: { - // This scope's arena memory is discarded after the ZIR generation - // pass completes, and semantic analysis of it completes. - var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer gen_scope_arena.deinit(); + const fn_zir: zir.Module.Body = blk: { + // We put the ZIR inside the Decl arena. var gen_scope: Scope.GenZIR = .{ .decl = decl, - .arena = &gen_scope_arena.allocator, + .arena = &decl_arena.allocator, .parent = decl.scope, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1131,8 +1169,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { for (fn_proto.params()) |param, i| { const name_token = param.name_token.?; const src = tree.token_locs[name_token].start; - const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString - const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg); + const param_name = try self.identifierTokenString(&gen_scope.base, name_token); + const arg = try decl_arena.allocator.create(zir.Inst.Arg); arg.* = .{ .base = .{ .tag = .arg, @@ -1144,7 +1182,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .kw_args = .{}, }; gen_scope.instructions.items[i] = &arg.base; - const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); + const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); sub_scope.* = .{ .parent = params_scope, .gen_zir = &gen_scope, @@ -1165,22 +1203,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); } - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; } - const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), - }, - .arena = gen_scope_arena.state, + break :blk .{ + .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), }; - break :blk fn_zir; }; + const is_inline = blk: { + if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { + if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { + break :blk true; + } + } + break :blk false; + }; + const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; + new_func.* = .{ - .analysis = .{ .queued = fn_zir }, + .state = anal_state, + .zir = fn_zir, + .body = undefined, .owner_decl = decl, }; fn_payload.* = .{ @@ -1189,11 +1234,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { }; var prev_type_has_bits = false; + var prev_is_inline = false; var type_changed = true; if (decl.typedValueManaged()) |tvm| { prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); type_changed = !tvm.typed_value.ty.eql(fn_type); + if (tvm.typed_value.val.castTag(.function)) |payload| { + const prev_func = payload.data; + prev_is_inline = prev_func.state == .inline_only; + } tvm.deinit(self.gpa); } @@ -1211,18 +1261,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { decl.analysis = .complete; decl.generation = self.generation; - if (fn_type.hasCodeGenBits()) { + if (!is_inline and fn_type.hasCodeGenBits()) { // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency order, // increasing how many computations can be done in parallel. try self.comp.bin_file.allocateDeclIndexes(decl); try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - } else if (prev_type_has_bits) { + } else if (!prev_is_inline and prev_type_has_bits) { self.comp.bin_file.freeDecl(decl); } if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { if (tree.token_ids[maybe_export_token] == .Keyword_export) { + if (is_inline) { + return self.failTok( + &block_scope.base, + maybe_export_token, + "export of inline function", + .{}, + ); + } const export_src = tree.token_locs[maybe_export_token].start; const name_loc = tree.token_locs[fn_proto.getNameToken().?]; const name = tree.tokenSliceLoc(name_loc); @@ -1230,7 +1288,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { try self.analyzeExport(&block_scope.base, export_src, name, decl); } } - return type_changed; + return type_changed or is_inline != prev_is_inline; }, .VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); @@ -1242,13 +1300,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + var decl_inst_table = Scope.Block.InstTable.init(self.gpa); + defer decl_inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &decl_inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); @@ -1303,23 +1369,32 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const src = tree.token_locs[init_node.firstToken()].start; const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; } + var var_inst_table = Scope.Block.InstTable.init(self.gpa); + defer var_inst_table.deinit(); + + var branch_quota_vi: u32 = default_eval_branch_quota; var inner_block: Scope.Block = .{ .parent = null, + .inst_table = &var_inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &gen_scope_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota_vi, }; defer inner_block.instructions.deinit(self.gpa); - try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items }); + try zir_sema.analyzeBody(self, &inner_block, .{ + .instructions = gen_scope.instructions.items, + }); // The result location guarantees the type coercion. - const analyzed_init_inst = init_inst.analyzed_inst.?; + const analyzed_init_inst = var_inst_table.get(init_inst).?; // The is_comptime in the Scope.Block guarantees the result is comptime-known. const val = analyzed_init_inst.value().?; @@ -1347,7 +1422,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .val = Value.initTag(.type_type), }); const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; } @@ -1423,21 +1498,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer gen_scope.instructions.deinit(self.gpa); _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; } + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &analysis_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); - _ = try zir_sema.analyzeBody(self, &block_scope.base, .{ + _ = try zir_sema.analyzeBody(self, &block_scope, .{ .instructions = gen_scope.instructions.items, }); @@ -1475,7 +1558,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { if (zir_module.error_msg) |src_err_msg| { self.failed_files.putAssumeCapacityNoClobber( &root_scope.base, - try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), + try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{s}", .{src_err_msg.msg}), ); root_scope.status = .unloaded_parse_failure; return error.AnalysisFail; @@ -1496,12 +1579,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { } } -fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { +pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { const tracy = trace(@src()); defer tracy.end(); - const root_scope = container_scope.file_scope; - switch (root_scope.status) { .never_loaded, .unloaded_success => { try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); @@ -1549,7 +1630,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // We may be analyzing it for the first time, or this may be // an incremental update. This code handles both cases. - const tree = try self.getAstTree(container_scope); + const tree = try self.getAstTree(container_scope.file_scope); const decls = tree.root_node.decls(); try self.comp.work_queue.ensureUnusedCapacity(decls.len); @@ -1581,7 +1662,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{s}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else { @@ -1623,7 +1704,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{s}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else if (!srcHashEql(decl.contents_hash, contents_hash)) { @@ -1641,7 +1722,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void } } else if (src_decl.castTag(.Comptime)) |comptime_node| { const name_index = self.getNextAnonNameIndex(); - const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index}); + const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index}); defer self.gpa.free(name); const name_hash = container_scope.fullyQualifiedNameHash(name); @@ -1663,7 +1744,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { - log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); + log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); try self.deleteDecl(entry.key); } } @@ -1716,7 +1797,7 @@ pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { - log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); + log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); try self.deleteDecl(entry.key); } } @@ -1728,7 +1809,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { // not be present in the set, and this does nothing. decl.scope.removeDecl(decl); - log.debug("deleting decl '{}'\n", .{decl.name}); + log.debug("deleting decl '{s}'\n", .{decl.name}); const name_hash = decl.fullyQualifiedNameHash(); self.decl_table.removeAssertDiscard(name_hash); // Remove itself from its dependencies, because we are about to destroy the decl pointer. @@ -1806,30 +1887,36 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { // Use the Decl's arena for function memory. var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); defer decl.typed_value.most_recent.arena.?.* = arena.state; + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + var branch_quota: u32 = default_eval_branch_quota; + var inner_block: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = func, .decl = decl, .instructions = .{}, .arena = &arena.allocator, + .inlining = null, .is_comptime = false, + .branch_quota = &branch_quota, }; defer inner_block.instructions.deinit(self.gpa); - const fn_zir = func.analysis.queued; - defer fn_zir.arena.promote(self.gpa).deinit(); - func.analysis = .{ .in_progress = {} }; - log.debug("set {} to in_progress\n", .{decl.name}); + func.state = .in_progress; + log.debug("set {s} to in_progress\n", .{decl.name}); - try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); + try zir_sema.analyzeBody(self, &inner_block, func.zir); const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); - func.analysis = .{ .success = .{ .instructions = instructions } }; - log.debug("set {} to success\n", .{decl.name}); + func.state = .success; + func.body = .{ .instructions = instructions }; + log.debug("set {s} to success\n", .{decl.name}); } fn markOutdatedDecl(self: *Module, decl: *Decl) !void { - log.debug("mark {} outdated\n", .{decl.name}); + log.debug("mark {s} outdated\n", .{decl.name}); try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); if (self.failed_decls.remove(decl)) |entry| { entry.value.destroy(self.gpa); @@ -1991,7 +2078,7 @@ pub fn analyzeExport( self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, - "exported symbol collision: {}", + "exported symbol collision: {s}", .{symbol_name}, )); // TODO: add a note @@ -2007,7 +2094,7 @@ pub fn analyzeExport( self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, - "unable to export: {}", + "unable to export: {s}", .{@errorName(err)}, )); new_export.status = .failed_retryable; @@ -2277,7 +2364,7 @@ pub fn createAnonymousDecl( ) !*Decl { const name_index = self.getNextAnonNameIndex(); const scope_decl = scope.decl().?; - const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index }); + const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); defer self.gpa.free(name); const name_hash = scope.namespace().fullyQualifiedNameHash(name); const src_hash: std.zig.SrcHash = undefined; @@ -2321,7 +2408,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn self.ensureDeclAnalyzed(decl) catch |err| { if (scope.cast(Scope.Block)) |block| { if (block.func) |func| { - func.analysis = .dependency_failure; + func.state = .dependency_failure; } else { block.decl.analysis = .dependency_failure; } @@ -2384,7 +2471,7 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { const decl = self.lookupDeclName(scope, decl_name) orelse - return self.fail(scope, src, "decl '{}' not found", .{decl_name}); + return self.fail(scope, src, "decl '{s}' not found", .{decl_name}); return self.analyzeDeclRef(scope, src, decl); } @@ -2555,7 +2642,7 @@ pub fn cmpNumeric( if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return self.fail(scope, src, "vector length mismatch: {} and {}", .{ + return self.fail(scope, src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -2700,7 +2787,7 @@ pub fn cmpNumeric( const dest_type = if (dest_float_type) |ft| ft else blk: { const max_bits = std.math.max(lhs_bits, rhs_bits); const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { - error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}), + error.Overflow => return self.fail(scope, src, "{d} exceeds maximum integer bit count", .{max_bits}), }; break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits); }; @@ -3029,11 +3116,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com }, .block => { const block = scope.cast(Scope.Block).?; - if (block.func) |func| { - func.analysis = .sema_failure; + if (block.inlining) |inlining| { + if (inlining.shared.caller) |func| { + func.state = .sema_failure; + } else { + block.decl.analysis = .sema_failure; + block.decl.generation = self.generation; + } } else { - block.decl.analysis = .sema_failure; - block.decl.generation = self.generation; + if (block.func) |func| { + func.state = .sema_failure; + } else { + block.decl.analysis = .sema_failure; + block.decl.generation = self.generation; + } } self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); }, @@ -3328,7 +3424,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); const loc = std.zig.findLineColumn(source, inst.src); if (inst.tag == .constant) { - std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{ + std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{ inst.ty, inst.castTag(.constant).?.val, zir_module.subFilePath(), @@ -3336,7 +3432,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { loc.column + 1, }); } else if (inst.deaths == 0) { - std.debug.print("{} ty={} src={}:{}:{}\n", .{ + std.debug.print("{s} ty={} src={s}:{d}:{d}\n", .{ @tagName(inst.tag), inst.ty, zir_module.subFilePath(), @@ -3344,7 +3440,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { loc.column + 1, }); } else { - std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{ + std.debug.print("{s} ty={} deaths={b} src={s}:{d}:{d}\n", .{ @tagName(inst.tag), inst.ty, inst.deaths, @@ -3389,12 +3485,16 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic var fail_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; + defer fail_block.instructions.deinit(mod.gpa); _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); @@ -3436,3 +3536,34 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty}); } } + +/// Identifier token -> String (allocated in scope.arena()) +pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { + const tree = scope.tree(); + + const ident_name = tree.tokenSlice(token); + if (mem.startsWith(u8, ident_name, "@")) { + const raw_string = ident_name[1..]; + var bad_index: usize = undefined; + return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { + error.InvalidCharacter => { + const bad_byte = raw_string[bad_index]; + const src = tree.token_locs[token].start; + return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); + }, + else => |e| return e, + }; + } + return ident_name; +} + +pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void { + const shared = block.inlining.?.shared; + shared.branch_count += 1; + if (shared.branch_count > block.branch_quota.*) { + // TODO show the "called from here" stack + return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{ + block.branch_quota.*, + }); + } +} diff --git a/src/astgen.zig b/src/astgen.zig index 3670cb260eee..8275a05d774c 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -384,8 +384,8 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, else => if (node.getLabel()) |break_label| { - const label_name = try identifierTokenString(mod, parent_scope, break_label); - return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); + const label_name = try mod.identifierTokenString(parent_scope, break_label); + return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); } else { return mod.failTok(parent_scope, src, "break expression outside loop", .{}); }, @@ -426,8 +426,8 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, else => if (node.getLabel()) |break_label| { - const label_name = try identifierTokenString(mod, parent_scope, break_label); - return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); + const label_name = try mod.identifierTokenString(parent_scope, break_label); + return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); } else { return mod.failTok(parent_scope, src, "continue expression outside loop", .{}); }, @@ -551,7 +551,7 @@ fn varDecl( } const tree = scope.tree(); const name_src = tree.token_locs[node.name_token].start; - const ident_name = try identifierTokenString(mod, scope, node.name_token); + const ident_name = try mod.identifierTokenString(scope, node.name_token); // Local variables shadowing detection, including function parameters. { @@ -560,14 +560,14 @@ fn varDecl( .local_val => { const local_val = s.cast(Scope.LocalVal).?; if (mem.eql(u8, local_val.name, ident_name)) { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } s = local_val.parent; }, .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (mem.eql(u8, local_ptr.name, ident_name)) { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } s = local_ptr.parent; }, @@ -578,7 +578,7 @@ fn varDecl( // Namespace vars shadowing detection if (mod.lookupDeclName(scope, ident_name)) |_| { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } const init_node = node.getInitNode() orelse return mod.fail(scope, name_src, "variables must be initialized", .{}); @@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.name].start; - const name = try identifierTokenString(mod, scope, node.name); + const name = try mod.identifierTokenString(scope, node.name); return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); } @@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro for (decls) |decl, i| { const tag = decl.castTag(.ErrorTag).?; - fields[i] = try identifierTokenString(mod, scope, tag.name_token); + fields[i] = try mod.identifierTokenString(scope, tag.name_token); } // analyzing the error set results in a decl ref, so we might need to dereference it @@ -988,36 +988,16 @@ fn orelseCatchExpr( /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { - const ident_name_1 = try identifierTokenString(mod, scope, token1); - const ident_name_2 = try identifierTokenString(mod, scope, token2); + const ident_name_1 = try mod.identifierTokenString(scope, token1); + const ident_name_2 = try mod.identifierTokenString(scope, token2); return mem.eql(u8, ident_name_1, ident_name_2); } -/// Identifier token -> String (allocated in scope.arena()) -fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { - const tree = scope.tree(); - - const ident_name = tree.tokenSlice(token); - if (mem.startsWith(u8, ident_name, "@")) { - const raw_string = ident_name[1..]; - var bad_index: usize = undefined; - return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { - error.InvalidCharacter => { - const bad_byte = raw_string[bad_index]; - const src = tree.token_locs[token].start; - return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); - }, - else => |e| return e, - }; - } - return ident_name; -} - pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.token].start; - const ident_name = try identifierTokenString(mod, scope, node.token); + const ident_name = try mod.identifierTokenString(scope, node.token); return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); } @@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo defer tracy.end(); const tree = scope.tree(); - const ident_name = try identifierTokenString(mod, scope, ident.token); + const ident_name = try mod.identifierTokenString(scope, ident.token); const src = tree.token_locs[ident.token].start; if (mem.eql(u8, ident_name, "_")) { return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{}); @@ -1955,7 +1935,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo error.Overflow => return mod.failNode( scope, &ident.base, - "primitive integer type '{}' exceeds maximum bit width of 65535", + "primitive integer type '{s}' exceeds maximum bit width of 65535", .{ident_name}, ), error.InvalidCharacter => break :integer, @@ -2010,7 +1990,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); } - return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); + return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name}); } fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst { @@ -2204,7 +2184,7 @@ fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinC return; const s = if (count == 1) "" else "s"; - return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len }); + return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len }); } fn simpleCast( @@ -2337,6 +2317,19 @@ fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerE return addZIRUnOp(mod, scope, src, .compileerror, target); } +fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { + try ensureBuiltinParamCount(mod, scope, call, 1); + const tree = scope.tree(); + const src = tree.token_locs[call.builtin_token].start; + const params = call.params(); + const u32_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u32_type), + }); + const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]); + return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota); +} + fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { const tree = scope.tree(); const arena = scope.arena(); @@ -2382,8 +2375,10 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built return rlWrap(mod, scope, rl, try import(mod, scope, call)); } else if (mem.eql(u8, builtin_name, "@compileError")) { return compileError(mod, scope, call); + } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) { + return setEvalBranchQuota(mod, scope, call); } else { - return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name}); + return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name}); } } diff --git a/src/codegen.zig b/src/codegen.zig index 9e6de711d485..c2537a1ca0fb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -228,7 +228,7 @@ pub fn generateSymbol( .fail = try ErrorMsg.create( bin_file.allocator, src, - "TODO implement generateSymbol for type '{}'", + "TODO implement generateSymbol for type '{s}'", .{@tagName(t)}, ), }; @@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.items.len += 4; try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); const stack_end = self.max_end_stack; if (stack_end > math.maxInt(i32)) @@ -543,13 +543,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (self.code.items.len >= math.maxInt(i32)) { return self.fail(self.src, "unable to perform relocation: jump too far", .{}); } - for (self.exitlude_jump_relocs.items) |jmp_reloc| { + if (self.exitlude_jump_relocs.items.len == 1) { + self.code.items.len -= 5; + } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { const amt = self.code.items.len - (jmp_reloc + 4); - // If it wouldn't jump at all, elide it. - if (amt == 0) { - self.code.items.len -= 5; - continue; - } const s32_amt = @intCast(i32, amt); mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt); } @@ -576,7 +573,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, @@ -593,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); // Backpatch stack offset const stack_end = self.max_end_stack; @@ -638,13 +635,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, else => { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); }, } @@ -2029,7 +2026,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); break :blk 0x84; }, - else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), + else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), }; self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode }); const reloc = Reloc{ .rel32 = self.code.items.len }; @@ -2376,11 +2373,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .arm, .armeb => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2393,11 +2390,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2406,11 +2403,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .aarch64 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2425,11 +2422,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2438,11 +2435,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .riscv64 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2455,11 +2452,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2468,11 +2465,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x86_64, .i386 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2485,11 +2482,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2837,7 +2834,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32()); } }, - .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), + .register => |src_reg| { + // If the registers are the same, nothing to do. + if (src_reg.id() == reg.id()) + return; + + // mov reg, src_reg + writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr( + reg, + .xzr, + src_reg, + Instruction.Shift.none, + ).toU32()); + }, .memory => |addr| { if (self.bin_file.options.pie) { // For MachO, the binary, with the exception of object files, has to be a PIE. @@ -3417,7 +3426,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { next_int_reg += 1; } }, - else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}), + else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), } } result.stack_byte_count = next_stack_offset; @@ -3475,6 +3484,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}), } }, + .aarch64 => { + switch (cc) { + .Naked => { + assert(result.args.len == 0); + result.return_value = .{ .unreach = {} }; + result.stack_byte_count = 0; + result.stack_align = 1; + return result; + }, + .Unspecified, .C => { + // ARM64 Procedure Call Standard + var ncrn: usize = 0; // Next Core Register Number + var nsaa: u32 = 0; // Next stacked argument address + + for (param_types) |ty, i| { + // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned + // values to spread across odd-numbered registers. + if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) { + // Round up NCRN to the next even number + ncrn += ncrn % 2; + } + + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { + if (param_size <= 8) { + result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] }; + ncrn += 1; + } else { + return self.fail(src, "TODO MCValues with multiple registers", .{}); + } + } else if (ncrn < 8 and nsaa == 0) { + return self.fail(src, "TODO MCValues split between registers and stack", .{}); + } else { + ncrn = 8; + // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided + // that the entire stack space consumed by the arguments is 8-byte aligned. + if (ty.abiAlignment(self.target.*) == 8) { + if (nsaa % 8 != 0) { + nsaa += 8 - (nsaa % 8); + } + } + + result.args[i] = .{ .stack_offset = nsaa }; + nsaa += param_size; + } + } + + result.stack_byte_count = nsaa; + result.stack_align = 16; + }, + else => return self.fail(src, "TODO implement function parameters for {} on aarch64", .{cc}), + } + }, else => if (param_types.len != 0) return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c6c29942d98c..1a89e22d4878 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -235,7 +235,7 @@ fn renderFunctionSignature( try writer.writeAll(", "); } try renderType(ctx, writer, tv.ty.fnParamType(index)); - try writer.print(" arg{}", .{index}); + try writer.print(" arg{d}", .{index}); } } try writer.writeByte(')'); @@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void { try writer.writeAll(" {"); const func: *Module.Fn = func_payload.data; - const instructions = func.analysis.success.instructions; + const instructions = func.body.instructions; if (instructions.len > 0) { try writer.writeAll("\n"); for (instructions) |inst| { @@ -383,7 +383,7 @@ const Context = struct { } fn name(self: *Context) ![]u8 { - const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index}); + const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{d}", .{self.unnamed_index}); self.unnamed_index += 1; return val; } @@ -420,7 +420,7 @@ fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 { } fn genArg(ctx: *Context) !?[]u8 { - const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); + const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex}); ctx.argdex += 1; return name; } @@ -528,7 +528,7 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { try renderValue(ctx, writer, arg.ty, val); } else { const val = try ctx.resolveInst(arg); - try writer.print("{}", .{val}); + try writer.print("{s}", .{val}); } } } @@ -587,7 +587,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { const arg = as.args[index]; try writer.writeAll("register "); try renderType(ctx, writer, arg.ty); - try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); + try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); // TODO merge constant handling into inst_map as well if (arg.castTag(.constant)) |c| { try renderValue(ctx, writer, arg.ty, c.val); @@ -597,13 +597,13 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { if (!gop.found_existing) { return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); } - try writer.print("{};\n ", .{gop.entry.value}); + try writer.print("{s};\n ", .{gop.entry.value}); } } else { return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); } } - try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); + try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); if (as.output) |o| { return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); } @@ -619,7 +619,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { if (index > 0) { try writer.writeAll(", "); } - try writer.print("\"\"({}_constant)", .{reg}); + try writer.print("\"\"({s}_constant)", .{reg}); } else { // This is blocked by the earlier test unreachable; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index c7ad59f5d178..036243dccac5 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { // TODO: check for and handle death of instructions const tv = decl.typed_value.most_recent.typed_value; const mod_fn = tv.val.castTag(.function).?.data; - for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst); + for (mod_fn.body.instructions) |inst| try genInst(buf, decl, inst); // Write 'end' opcode try writer.writeByte(0x0B); diff --git a/src/config.zig.in b/src/config.zig.in index 9d16cf382441..b672581ea4a8 100644 --- a/src/config.zig.in +++ b/src/config.zig.in @@ -2,7 +2,7 @@ pub const have_llvm = true; pub const version: [:0]const u8 = "@ZIG_VERSION@"; pub const semver = try @import("std").SemanticVersion.parse(version); pub const log_scopes: []const []const u8 = &[_][]const u8{}; -pub const zir_dumps: []const []const u8 = &[_][]const u8{}; pub const enable_tracy = false; pub const is_stage1 = true; pub const skip_non_native = false; +pub const omit_stage2: bool = @ZIG_OMIT_STAGE2_BOOL@; diff --git a/src/glibc.zig b/src/glibc.zig index 0c2787272057..e7b7b1b1cf03 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -72,7 +72,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! errdefer version_table.deinit(gpa); var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| { - std.log.err("unable to open glibc dir: {}", .{@errorName(err)}); + std.log.err("unable to open glibc dir: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }; defer glibc_dir.close(); @@ -81,7 +81,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read vers.txt: {}", .{@errorName(err)}); + std.log.err("unable to read vers.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -91,7 +91,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read fns.txt: {}", .{@errorName(err)}); + std.log.err("unable to read fns.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -99,7 +99,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read abi.txt: {}", .{@errorName(err)}); + std.log.err("unable to read abi.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -111,12 +111,12 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (it.next()) |line| : (line_i += 1) { const prefix = "GLIBC_"; if (!mem.startsWith(u8, line, prefix)) { - std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i}); + std.log.err("vers.txt:{d}: expected 'GLIBC_' prefix", .{line_i}); return error.ZigInstallationCorrupt; } const adjusted_line = line[prefix.len..]; const ver = std.builtin.Version.parse(adjusted_line) catch |err| { - std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) }); + std.log.err("vers.txt:{d}: unable to parse glibc version '{s}': {s}", .{ line_i, line, @errorName(err) }); return error.ZigInstallationCorrupt; }; try all_versions.append(arena, ver); @@ -128,15 +128,15 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (file_it.next()) |line| : (line_i += 1) { var line_it = mem.tokenize(line, " "); const fn_name = line_it.next() orelse { - std.log.err("fns.txt:{}: expected function name", .{line_i}); + std.log.err("fns.txt:{d}: expected function name", .{line_i}); return error.ZigInstallationCorrupt; }; const lib_name = line_it.next() orelse { - std.log.err("fns.txt:{}: expected library name", .{line_i}); + std.log.err("fns.txt:{d}: expected library name", .{line_i}); return error.ZigInstallationCorrupt; }; const lib = findLib(lib_name) orelse { - std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name }); + std.log.err("fns.txt:{d}: unknown library name: {s}", .{ line_i, lib_name }); return error.ZigInstallationCorrupt; }; try all_functions.append(arena, .{ @@ -158,27 +158,27 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (line_it.next()) |target_string| { var component_it = mem.tokenize(target_string, "-"); const arch_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected arch name", .{line_i}); + std.log.err("abi.txt:{d}: expected arch name", .{line_i}); return error.ZigInstallationCorrupt; }; const os_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected OS name", .{line_i}); + std.log.err("abi.txt:{d}: expected OS name", .{line_i}); return error.ZigInstallationCorrupt; }; const abi_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected ABI name", .{line_i}); + std.log.err("abi.txt:{d}: expected ABI name", .{line_i}); return error.ZigInstallationCorrupt; }; const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { - std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name }); + std.log.err("abi.txt:{d}: unrecognized arch: '{s}'", .{ line_i, arch_name }); return error.ZigInstallationCorrupt; }; if (!mem.eql(u8, os_name, "linux")) { - std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name }); + std.log.err("abi.txt:{d}: expected OS 'linux', found '{s}'", .{ line_i, os_name }); return error.ZigInstallationCorrupt; } const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse { - std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name }); + std.log.err("abi.txt:{d}: unrecognized ABI: '{s}'", .{ line_i, abi_name }); return error.ZigInstallationCorrupt; }; @@ -193,7 +193,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! }; for (ver_list_base) |*ver_list| { const line = file_it.next() orelse { - std.log.err("abi.txt:{}: missing version number line", .{line_i}); + std.log.err("abi.txt:{d}: missing version number line", .{line_i}); return error.ZigInstallationCorrupt; }; line_i += 1; @@ -206,12 +206,12 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (line_it.next()) |version_index_string| { if (ver_list.len >= ver_list.versions.len) { // If this happens with legit data, increase the array len in the type. - std.log.err("abi.txt:{}: too many versions", .{line_i}); + std.log.err("abi.txt:{d}: too many versions", .{line_i}); return error.ZigInstallationCorrupt; } const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| { // If this happens with legit data, increase the size of the integer type in the struct. - std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) }); + std.log.err("abi.txt:{d}: unable to parse version: {s}", .{ line_i, @errorName(err) }); return error.ZigInstallationCorrupt; }; @@ -531,7 +531,7 @@ fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList( try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" })); try args.append("-I"); - try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{ + try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{ comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), })); @@ -539,7 +539,7 @@ fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList( try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); try args.append("-I"); - try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{ + try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{ comp.zig_lib_directory.path.?, @tagName(arch), })); @@ -881,7 +881,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { if (o_directory.handle.createFile(ok_basename, .{})) |file| { file.close(); } else |err| { - std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)}); + std.log.warn("glibc shared objects: failed to mark completion: {s}", .{@errorName(err)}); } } diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 0b1029eeb8e0..cc96146e0b19 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -83,7 +83,7 @@ pub const LibCInstallation = struct { } inline for (fields) |field, i| { if (!found_keys[i].found) { - log.err("missing field: {}\n", .{field.name}); + log.err("missing field: {s}\n", .{field.name}); return error.ParseError; } } @@ -96,18 +96,18 @@ pub const LibCInstallation = struct { return error.ParseError; } if (self.crt_dir == null and !is_darwin) { - log.err("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)}); + log.err("crt_dir may not be empty for {s}\n", .{@tagName(Target.current.os.tag)}); return error.ParseError; } if (self.msvc_lib_dir == null and is_windows and !is_gnu) { - log.err("msvc_lib_dir may not be empty for {}-{}\n", .{ + log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(Target.current.os.tag), @tagName(Target.current.abi), }); return error.ParseError; } if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { - log.err("kernel32_lib_dir may not be empty for {}-{}\n", .{ + log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(Target.current.os.tag), @tagName(Target.current.abi), }); @@ -128,25 +128,25 @@ pub const LibCInstallation = struct { try out.print( \\# The directory that contains `stdlib.h`. \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` - \\include_dir={} + \\include_dir={s} \\ \\# The system-specific include directory. May be the same as `include_dir`. \\# On Windows it's the directory that includes `vcruntime.h`. \\# On POSIX it's the directory that includes `sys/errno.h`. - \\sys_include_dir={} + \\sys_include_dir={s} \\ \\# The directory that contains `crt1.o` or `crt2.o`. \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. \\# Not needed when targeting MacOS. - \\crt_dir={} + \\crt_dir={s} \\ \\# The directory that contains `vcruntime.lib`. \\# Only needed when targeting MSVC on Windows. - \\msvc_lib_dir={} + \\msvc_lib_dir={s} \\ \\# The directory that contains `kernel32.lib`. \\# Only needed when targeting MSVC on Windows. - \\kernel32_lib_dir={} + \\kernel32_lib_dir={s} \\ , .{ include_dir, @@ -338,7 +338,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); - try result_buf.outStream().print("{}\\Include\\{}\\ucrt", .{ search.path, search.version }); + try result_buf.outStream().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -384,7 +384,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); - try result_buf.outStream().print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir }); + try result_buf.outStream().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -439,7 +439,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); const stream = result_buf.outStream(); - try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir }); + try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -520,7 +520,7 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { const allocator = args.allocator; const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; - const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); + const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename}); defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; @@ -584,17 +584,17 @@ fn printVerboseInvocation( if (!verbose) return; if (search_basename) |s| { - std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s}); + std.debug.warn("Zig attempted to find the file '{s}' by executing this command:\n", .{s}); } else { std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{}); } for (argv) |arg, i| { if (i != 0) std.debug.warn(" ", .{}); - std.debug.warn("{}", .{arg}); + std.debug.warn("{s}", .{arg}); } std.debug.warn("\n", .{}); if (stderr) |s| { - std.debug.warn("Output:\n==========\n{}\n==========\n", .{s}); + std.debug.warn("Output:\n==========\n{s}\n==========\n", .{s}); } } diff --git a/src/link.zig b/src/link.zig index 468f23bffd7e..3dbfb3b92202 100644 --- a/src/link.zig +++ b/src/link.zig @@ -523,7 +523,7 @@ pub const File = struct { id_symlink_basename, &prev_digest_buf, ) catch |err| b: { - log.debug("archive new_digest={} readFile error: {}", .{ digest, @errorName(err) }); + log.debug("archive new_digest={} readFile error: {s}", .{ digest, @errorName(err) }); break :b prev_digest_buf[0..0]; }; if (mem.eql(u8, prev_digest, &digest)) { @@ -560,9 +560,9 @@ pub const File = struct { const full_out_path_z = try arena.dupeZ(u8, full_out_path); if (base.options.verbose_link) { - std.debug.print("ar rcs {}", .{full_out_path_z}); + std.debug.print("ar rcs {s}", .{full_out_path_z}); for (object_files.items) |arg| { - std.debug.print(" {}", .{arg}); + std.debug.print(" {s}", .{arg}); } std.debug.print("\n", .{}); } @@ -574,11 +574,11 @@ pub const File = struct { if (!base.options.disable_lld_caching) { Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save archive hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); }; man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); }; base.lock = man.toOwnedLock(); diff --git a/src/link/C.zig b/src/link/C.zig index 1059e52115a7..c3ab506b2cfb 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -112,7 +112,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { try writer.writeByte('\n'); } if (self.constants.items.len > 0) { - try writer.print("{}\n", .{self.constants.items}); + try writer.print("{s}\n", .{self.constants.items}); } if (self.main.items.len > 1) { const last_two = self.main.items[self.main.items.len - 2 ..]; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 096fa2cd0bb4..117bdef4a758 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -686,7 +686,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { if (need_realloc) { const curr_vaddr = self.getDeclVAddr(decl); const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); if (vaddr != curr_vaddr) { log.debug(" (writing new offset table entry)\n", .{}); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; @@ -697,7 +697,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } } else { const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); errdefer self.freeTextBlock(&decl.link.coff); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); @@ -811,8 +811,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; - if (use_stage1) { + // Both stage1 and stage2 LLVM backend put the object file in the cache directory. + if (self.base.options.use_llvm) { + // Stage2 has to call flushModule since that outputs the LLVM object file. + if (!build_options.is_stage1) try self.flushModule(comp); + const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -880,7 +883,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("COFF LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("COFF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -1236,11 +1239,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 4b2b95fc7220..b6b8fd775024 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1251,8 +1251,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; - if (use_stage1) { + // Both stage1 and stage2 LLVM backend put the object file in the cache directory. + if (self.base.options.use_llvm) { + // Stage2 has to call flushModule since that outputs the LLVM object file. + if (!build_options.is_stage1) try self.flushModule(comp); + const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -1362,7 +1365,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("ELF LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("ELF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -1396,7 +1399,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.output_mode == .Exe) { try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); + try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); } if (self.base.options.image_base_override) |image_base| { @@ -1438,7 +1441,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (getLDMOption(target)) |ldm| { // Any target ELF will use the freebsd osabi if suffixed with "_fbsd". const arg = if (target.os.tag == .freebsd) - try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm}) + try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm}) else ldm; try argv.append("-m"); @@ -1599,7 +1602,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // (the check for that needs to be earlier), but they could be full paths to .so files, in which // case we want to avoid prepending "-l". const ext = Compilation.classifyFileExt(link_lib); - const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); argv.appendAssumeCapacity(arg); } @@ -1733,11 +1736,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. @@ -2082,10 +2085,10 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.local_symbol_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {} for {}\n", .{ i, decl.name }); + log.debug("reusing symbol index {d} for {s}\n", .{ i, decl.name }); decl.link.elf.local_sym_index = i; } else { - log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); + log.debug("allocating symbol index {d} for {s}\n", .{ self.local_symbols.items.len, decl.name }); decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len); _ = self.local_symbols.addOneAssumeCapacity(); } @@ -2178,16 +2181,6 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { else => false, }; if (is_fn) { - const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps; - if (zir_dumps.len != 0) { - for (zir_dumps) |fn_name| { - if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) { - std.debug.print("\n{}\n", .{decl.name}); - typed_value.val.castTag(.function).?.data.dump(module.*); - } - } - } - // For functions we need to add a prologue to the debug line program. try dbg_line_buffer.ensureCapacity(26); @@ -2300,7 +2293,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); if (need_realloc) { const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); if (vaddr != local_sym.st_value) { local_sym.st_value = vaddr; @@ -2322,7 +2315,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { const decl_name = mem.spanZ(decl.name); const name_str_index = try self.makeString(decl_name); const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr }); + log.debug("allocated text block for {s} at 0x{x}\n", .{ decl_name, vaddr }); errdefer self.freeTextBlock(&decl.link.elf); local_sym.* = .{ @@ -2432,7 +2425,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { const new_offset = self.findFreeSpace(needed_size, 1); const existing_size = last_src_fn.off; - log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{ + log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}\n", .{ existing_size, debug_line_sect.sh_offset, new_offset, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6abbae2c268c..efb16e4d1c7e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -298,7 +298,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.base.file = file; // Create dSYM bundle. - const d_sym_path = try fmt.allocPrint(allocator, "{}.dSYM/Contents/Resources/DWARF/", .{sub_path}); + const d_sym_path = try fmt.allocPrint(allocator, "{s}.dSYM/Contents/Resources/DWARF/", .{sub_path}); defer allocator.free(d_sym_path); var d_sym_bundle = try options.emit.?.directory.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); @@ -520,7 +520,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("MachO LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("MachO LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -620,7 +620,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try argv.append(cur_vers); } - const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{}", .{self.base.options.emit.?.sub_path}); + const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{self.base.options.emit.?.sub_path}); try argv.append("-install_name"); try argv.append(dylib_install_name); } @@ -706,7 +706,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which // case we want to avoid prepending "-l". const ext = Compilation.classifyFileExt(link_lib); - const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); argv.appendAssumeCapacity(arg); } @@ -759,15 +759,15 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { self.base.allocator.free(result.stderr); } if (result.stdout.len != 0) { - log.warn("unexpected LD stdout: {}", .{result.stdout}); + log.warn("unexpected LD stdout: {s}", .{result.stdout}); } if (result.stderr.len != 0) { - log.warn("unexpected LD stderr: {}", .{result.stderr}); + log.warn("unexpected LD stderr: {s}", .{result.stderr}); } if (result.term != .Exited or result.term.Exited != 0) { // TODO parse this output and surface with the Compilation API rather than // directly outputting to stderr here. - log.err("{}", .{result.stderr}); + log.err("{s}", .{result.stderr}); return error.LDReportedFailure; } } else { @@ -980,11 +980,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. @@ -1088,10 +1088,10 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.local_symbol_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {} for {}", .{ i, decl.name }); + log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); decl.link.macho.local_sym_index = i; } else { - log.debug("allocating symbol index {} for {}", .{ self.local_symbols.items.len, decl.name }); + log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name }); decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); _ = self.local_symbols.addOneAssumeCapacity(); } @@ -1165,7 +1165,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); if (need_realloc) { const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); if (vaddr != symbol.n_value) { symbol.n_value = vaddr; log.debug(" (writing new offset table entry)", .{}); @@ -1188,7 +1188,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const decl_name = mem.spanZ(decl.name); const name_str_index = try self.makeString(decl_name); const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x}", .{ decl_name, addr }); + log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr }); errdefer self.freeTextBlock(&decl.link.macho); symbol.* = .{ diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index c70fcc5825ce..11f87d5495ba 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers( const typed_value = decl.typed_value.most_recent.typed_value; switch (typed_value.ty.zigTypeTag()) { .Fn => { - const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps; - if (zir_dumps.len != 0) { - for (zir_dumps) |fn_name| { - if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) { - std.debug.print("\n{}\n", .{decl.name}); - typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*); - } - } - } - // For functions we need to add a prologue to the debug line program. try dbg_line_buffer.ensureCapacity(26); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 374fa2230bb2..cbb3e83147a6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -321,7 +321,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("WASM LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("WASM LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -463,11 +463,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/liveness.zig b/src/liveness.zig index 0d759f83123a..b0aafa28f114 100644 --- a/src/liveness.zig +++ b/src/liveness.zig @@ -1,6 +1,7 @@ const std = @import("std"); const ir = @import("ir.zig"); const trace = @import("tracy.zig").trace; +const log = std.log.scoped(.liveness); /// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated. pub fn analyze( @@ -248,5 +249,5 @@ fn analyzeInst( @panic("Handle liveness analysis for instructions with many parameters"); } - std.log.scoped(.liveness).debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths }); + log.debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths }); } diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig index 294e6f540072..b0e59e3ffa0a 100644 --- a/src/llvm_backend.zig +++ b/src/llvm_backend.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Compilation = @import("Compilation.zig"); const llvm = @import("llvm_bindings.zig"); @@ -132,7 +133,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .macabi => "macabi", }; - return std.fmt.allocPrintZ(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); + return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); } pub const LLVMIRModule = struct { @@ -141,17 +142,36 @@ pub const LLVMIRModule = struct { target_machine: *const llvm.TargetMachineRef, builder: *const llvm.BuilderRef, - output_path: []const u8, + object_path: []const u8, gpa: *Allocator, err_msg: ?*Compilation.ErrorMsg = null, + /// This stores the LLVM values used in a function, such that they can be + /// referred to in other instructions. This table is cleared before every function is generated. + func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.ValueRef) = .{}, + + /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. + args: []*const llvm.ValueRef = &[_]*const llvm.ValueRef{}, + arg_index: usize = 0, + pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { const self = try allocator.create(LLVMIRModule); errdefer allocator.destroy(self); const gpa = options.module.?.gpa; + const obj_basename = try std.zig.binNameAlloc(gpa, .{ + .root_name = options.root_name, + .target = options.target, + .output_mode = .Obj, + }); + defer gpa.free(obj_basename); + + const o_directory = options.module.?.zig_cache_artifact_directory; + const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); + errdefer gpa.free(object_path); + initializeLLVMTargets(); const root_nameZ = try gpa.dupeZ(u8, options.root_name); @@ -203,7 +223,7 @@ pub const LLVMIRModule = struct { .llvm_module = llvm_module, .target_machine = target_machine, .builder = builder, - .output_path = sub_path, + .object_path = object_path, .gpa = gpa, }; return self; @@ -213,6 +233,10 @@ pub const LLVMIRModule = struct { self.builder.disposeBuilder(); self.target_machine.disposeTargetMachine(); self.llvm_module.disposeModule(); + + self.func_inst_table.deinit(self.gpa); + self.gpa.free(self.object_path); + allocator.destroy(self); } @@ -245,15 +269,13 @@ pub const LLVMIRModule = struct { } } - const output_pathZ = try self.gpa.dupeZ(u8, self.output_path); - defer self.gpa.free(output_pathZ); + const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); + defer self.gpa.free(object_pathZ); var error_message: [*:0]const u8 = undefined; - // TODO: where to put the output object, zig-cache something? - // TODO: caching? if (self.target_machine.emitToFile( self.llvm_module, - output_pathZ.ptr, + object_pathZ.ptr, .ObjectFile, &error_message, )) { @@ -271,6 +293,7 @@ pub const LLVMIRModule = struct { error.CodegenFail => { decl.analysis = .codegen_failure; try module.failed_decls.put(module.gpa, decl, self.err_msg.?); + self.err_msg = null; return; }, else => |e| return e, @@ -278,47 +301,71 @@ pub const LLVMIRModule = struct { } fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void { - switch (typed_value.ty.zigTypeTag()) { - .Fn => { - const func = typed_value.val.castTag(.function).?.data; + if (typed_value.val.castTag(.function)) |func_inst| { + const func = func_inst.data; - const llvm_func = try self.resolveLLVMFunction(func); + const llvm_func = try self.resolveLLVMFunction(func, src); - // We remove all the basic blocks of a function to support incremental - // compilation! - // TODO: remove all basic blocks if functions can have more than one - if (llvm_func.getFirstBasicBlock()) |bb| { - bb.deleteBasicBlock(); - } + // This gets the LLVM values from the function and stores them in `self.args`. + const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); + var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len); + defer self.gpa.free(args); - const entry_block = llvm_func.appendBasicBlock("Entry"); - self.builder.positionBuilderAtEnd(entry_block); - - const instructions = func.analysis.success.instructions; - for (instructions) |inst| { - switch (inst.tag) { - .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), - .call => try self.genCall(inst.castTag(.call).?), - .unreach => self.genUnreach(inst.castTag(.unreach).?), - .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), - .arg => self.genArg(inst.castTag(.arg).?), - .dbg_stmt => { - // TODO: implement debug info - }, - else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), - } - } - }, - else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}), + for (args) |*arg, i| { + arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); + } + self.args = args; + self.arg_index = 0; + + // Make sure no other LLVM values from other functions can be referenced + self.func_inst_table.clearRetainingCapacity(); + + // We remove all the basic blocks of a function to support incremental + // compilation! + // TODO: remove all basic blocks if functions can have more than one + if (llvm_func.getFirstBasicBlock()) |bb| { + bb.deleteBasicBlock(); + } + + const entry_block = llvm_func.appendBasicBlock("Entry"); + self.builder.positionBuilderAtEnd(entry_block); + + const instructions = func.body.instructions; + for (instructions) |inst| { + const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) { + .add => try self.genAdd(inst.castTag(.add).?), + .alloc => try self.genAlloc(inst.castTag(.alloc).?), + .arg => try self.genArg(inst.castTag(.arg).?), + .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), + .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .call => try self.genCall(inst.castTag(.call).?), + .intcast => try self.genIntCast(inst.castTag(.intcast).?), + .load => try self.genLoad(inst.castTag(.load).?), + .not => try self.genNot(inst.castTag(.not).?), + .ret => try self.genRet(inst.castTag(.ret).?), + .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), + .store => try self.genStore(inst.castTag(.store).?), + .sub => try self.genSub(inst.castTag(.sub).?), + .unreach => self.genUnreach(inst.castTag(.unreach).?), + .dbg_stmt => blk: { + // TODO: implement debug info + break :blk null; + }, + else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), + }; + if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); + } + } else { + return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty}); } } - fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void { + fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef { if (inst.func.value()) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty; - const llvm_fn = try self.resolveLLVMFunction(func); + const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src); const num_args = inst.args.len; @@ -338,54 +385,165 @@ pub const LLVMIRModule = struct { "", ); - if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) { + const return_type = zig_fn_type.fnReturnType(); + if (return_type.tag() == .noreturn) { _ = self.builder.buildUnreachable(); } + + // No need to store the LLVM value if the return type is void or noreturn + if (!return_type.hasCodeGenBits()) return null; + + return call; } } + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); } - fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void { + fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { _ = self.builder.buildRetVoid(); + return null; + } + + fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + _ = self.builder.buildRet(try self.resolveInst(inst.operand)); + return null; + } + + fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + return self.builder.buildNot(try self.resolveInst(inst.operand), ""); } - fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void { + fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { _ = self.builder.buildUnreachable(); + return null; } - fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void { - // TODO: implement this + fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWAdd(lhs, rhs, "") + else + self.builder.buildNUWAdd(lhs, rhs, ""); } - fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void { - // TODO: Store this function somewhere such that we dont have to add it again - const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false); - const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type); - // TODO: add assertion: LLVMGetIntrinsicID - _ = self.builder.buildCall(func, null, 0, ""); + fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWSub(lhs, rhs, "") + else + self.builder.buildNUWSub(lhs, rhs, ""); + } + + fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.operand); + + const signed = inst.base.ty.isSignedInt(); + // TODO: Should we use intcast here or just a simple bitcast? + // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes + return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, ""); + } + + fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.operand); + const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); + + return self.builder.buildBitCast(val, dest_type, ""); + } + + fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef { + const arg_val = self.args[self.arg_index]; + self.arg_index += 1; + + const ptr_val = self.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), ""); + _ = self.builder.buildStore(arg_val, ptr_val); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef { + // buildAlloca expects the pointee type, not the pointer type, so assert that + // a Payload.PointerSimple is passed to the alloc instruction. + const pointee_type = inst.base.ty.castPointer().?.data; + + // TODO: figure out a way to get the name of the var decl. + // TODO: set alignment and volatile + return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), ""); + } + + fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.rhs); + const ptr = try self.resolveInst(inst.lhs); + _ = self.builder.buildStore(val, ptr); + return null; + } + + fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const ptr_val = try self.resolveInst(inst.operand); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef { + const llvn_fn = self.getIntrinsic("llvm.debugtrap"); + _ = self.builder.buildCall(llvn_fn, null, 0, ""); + return null; + } + + fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.ValueRef { + const id = llvm.lookupIntrinsicID(name.ptr, name.len); + assert(id != 0); + // TODO: add support for overload intrinsics by passing the prefix of the intrinsic + // to `lookupIntrinsicID` and then passing the correct types to + // `getIntrinsicDeclaration` + return self.llvm_module.getIntrinsicDeclaration(id, null, 0); } fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef { - if (inst.castTag(.constant)) |const_inst| { - return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); + if (inst.value()) |val| { + return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); } - return self.fail(inst.src, "TODO implement resolveInst", .{}); + if (self.func_inst_table.get(inst)) |value| return value; + + return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); } - fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef { - const llvm_type = self.getLLVMType(typed_value.ty); + fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef { + const llvm_type = try self.getLLVMType(tv.ty, src); - if (typed_value.val.isUndef()) + if (tv.val.isUndef()) return llvm_type.getUndef(); - switch (typed_value.ty.zigTypeTag()) { - .Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), - else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), + switch (tv.ty.zigTypeTag()) { + .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), + .Int => { + var bigint_space: Value.BigIntSpace = undefined; + const bigint = tv.val.toBigInt(&bigint_space); + + if (bigint.eqZero()) return llvm_type.constNull(); + + if (bigint.limbs.len != 1) { + return self.fail(src, "TODO implement bigger bigint", .{}); + } + const llvm_int = llvm_type.constInt(bigint.limbs[0], false); + if (!bigint.positive) { + return llvm.constNeg(llvm_int); + } + return llvm_int; + }, + else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), } } /// If the llvm function does not exist, create it - fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef { + fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef { // TODO: do we want to store this in our own datastructure? if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn; @@ -402,25 +560,25 @@ pub const LLVMIRModule = struct { defer self.gpa.free(llvm_param); for (fn_param_types) |fn_param, i| { - llvm_param[i] = self.getLLVMType(fn_param); + llvm_param[i] = try self.getLLVMType(fn_param, src); } const fn_type = llvm.TypeRef.functionType( - self.getLLVMType(return_type), + try self.getLLVMType(return_type, src), if (fn_param_len == 0) null else llvm_param.ptr, @intCast(c_uint, fn_param_len), false, ); const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type); - if (return_type.zigTypeTag() == .NoReturn) { + if (return_type.tag() == .noreturn) { llvm_fn.addFnAttr("noreturn"); } return llvm_fn; } - fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef { + fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef { switch (t.zigTypeTag()) { .Void => return llvm.voidType(), .NoReturn => return llvm.voidType(), @@ -429,13 +587,18 @@ pub const LLVMIRModule = struct { return llvm.intType(info.bits); }, .Bool => return llvm.intType(1), - else => unreachable, + .Pointer => { + const pointer = t.castPointer().?; + const elem_type = try self.getLLVMType(pointer.data, src); + return elem_type.pointerType(0); + }, + else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), } } pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { @setCold(true); - std.debug.assert(self.err_msg == null); + assert(self.err_msg == null); self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); return error.CodegenFail; } diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig index 388acc4ba2a1..aa9553732850 100644 --- a/src/llvm_bindings.zig +++ b/src/llvm_bindings.zig @@ -43,8 +43,14 @@ pub const TypeRef = opaque { pub const constAllOnes = LLVMConstAllOnes; extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef; + pub const constInt = LLVMConstInt; + extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef; + pub const getUndef = LLVMGetUndef; extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef; + + pub const pointerType = LLVMPointerType; + extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef; }; pub const ModuleRef = opaque { @@ -63,10 +69,16 @@ pub const ModuleRef = opaque { pub const getNamedFunction = LLVMGetNamedFunction; extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef; + pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration; + extern fn LLVMGetIntrinsicDeclaration(Mod: *const ModuleRef, ID: c_uint, ParamTypes: ?[*]*const TypeRef, ParamCount: usize) *const ValueRef; + pub const printToString = LLVMPrintModuleToString; extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8; }; +pub const lookupIntrinsicID = LLVMLookupIntrinsicID; +extern fn LLVMLookupIntrinsicID(Name: [*]const u8, NameLen: usize) c_uint; + pub const disposeMessage = LLVMDisposeMessage; extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; @@ -76,9 +88,15 @@ pub const VerifierFailureAction = extern enum { ReturnStatus, }; +pub const constNeg = LLVMConstNeg; +extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef; + pub const voidType = LLVMVoidType; extern fn LLVMVoidType() *const TypeRef; +pub const getParam = LLVMGetParam; +extern fn LLVMGetParam(Fn: *const ValueRef, Index: c_uint) *const ValueRef; + pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; @@ -117,11 +135,41 @@ pub const BuilderRef = opaque { pub const buildRetVoid = LLVMBuildRetVoid; extern fn LLVMBuildRetVoid(*const BuilderRef) *const ValueRef; + pub const buildRet = LLVMBuildRet; + extern fn LLVMBuildRet(*const BuilderRef, V: *const ValueRef) *const ValueRef; + pub const buildUnreachable = LLVMBuildUnreachable; extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef; pub const buildAlloca = LLVMBuildAlloca; extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildStore = LLVMBuildStore; + extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef; + + pub const buildLoad = LLVMBuildLoad; + extern fn LLVMBuildLoad(*const BuilderRef, PointerVal: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNot = LLVMBuildNot; + extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNSWAdd = LLVMBuildNSWAdd; + extern fn LLVMBuildNSWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNUWAdd = LLVMBuildNUWAdd; + extern fn LLVMBuildNUWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNSWSub = LLVMBuildNSWSub; + extern fn LLVMBuildNSWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNUWSub = LLVMBuildNUWSub; + extern fn LLVMBuildNUWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildIntCast2 = LLVMBuildIntCast2; + extern fn LLVMBuildIntCast2(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, IsSigned: LLVMBool, Name: [*:0]const u8) *const ValueRef; + + pub const buildBitCast = LLVMBuildBitCast; + extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef; }; pub const BasicBlockRef = opaque { diff --git a/src/main.zig b/src/main.zig index 247f8327f0f9..f026b6b4b484 100644 --- a/src/main.zig +++ b/src/main.zig @@ -104,7 +104,10 @@ pub fn log( var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; pub fn main() anyerror!void { - const gpa = if (std.builtin.link_libc) std.heap.raw_c_allocator else &general_purpose_allocator.allocator; + const gpa = if (std.builtin.link_libc) + std.heap.raw_c_allocator + else + &general_purpose_allocator.allocator; defer if (!std.builtin.link_libc) { _ = general_purpose_allocator.deinit(); }; @@ -118,7 +121,7 @@ pub fn main() anyerror!void { pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { if (args.len <= 1) { - std.log.info("{}", .{usage}); + std.log.info("{s}", .{usage}); fatal("expected command argument", .{}); } @@ -204,8 +207,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { try io.getStdOut().writeAll(usage); } else { - std.log.info("{}", .{usage}); - fatal("unknown command: {}", .{args[1]}); + std.log.info("{s}", .{usage}); + fatal("unknown command: {s}", .{args[1]}); } } @@ -332,7 +335,7 @@ const usage_build_generic = \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked \\ -Bsymbolic Bind global references locally - \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker\n" + \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image \\ -framework [name] (Darwin) link against framework @@ -615,7 +618,7 @@ fn buildOutputType( fatal("unexpected end-of-parameter mark: --", .{}); } } else if (mem.eql(u8, arg, "--pkg-begin")) { - if (i + 2 >= args.len) fatal("Expected 2 arguments after {}", .{arg}); + if (i + 2 >= args.len) fatal("Expected 2 arguments after {s}", .{arg}); i += 1; const pkg_name = args[i]; i += 1; @@ -626,7 +629,7 @@ fn buildOutputType( fs.path.dirname(pkg_path), fs.path.basename(pkg_path), ) catch |err| { - fatal("Failed to add package at path {}: {}", .{ pkg_path, @errorName(err) }); + fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); }; new_cur_pkg.parent = cur_pkg; try cur_pkg.add(gpa, pkg_name, new_cur_pkg); @@ -635,7 +638,7 @@ fn buildOutputType( cur_pkg = cur_pkg.parent orelse fatal("encountered --pkg-end with no matching --pkg-begin", .{}); } else if (mem.eql(u8, arg, "--main-pkg-path")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; main_pkg_path = args[i]; } else if (mem.eql(u8, arg, "-cflags")) { @@ -653,10 +656,10 @@ fn buildOutputType( i += 1; const next_arg = args[i]; color = std.meta.stringToEnum(Color, next_arg) orelse { - fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); }; } else if (mem.eql(u8, arg, "--subsystem")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; if (mem.eql(u8, args[i], "console")) { subsystem = .Console; @@ -689,51 +692,51 @@ fn buildOutputType( }); } } else if (mem.eql(u8, arg, "-O")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; optimize_mode_string = args[i]; } else if (mem.eql(u8, arg, "--stack")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; stack_size_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--image-base")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; image_base_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--name")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; provided_name = args[i]; } else if (mem.eql(u8, arg, "-rpath")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try rpath_list.append(args[i]); } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try lib_dirs.append(args[i]); } else if (mem.eql(u8, arg, "-F")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try framework_dirs.append(args[i]); } else if (mem.eql(u8, arg, "-framework")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try frameworks.append(args[i]); } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; linker_script = args[i]; } else if (mem.eql(u8, arg, "--version-script")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; version_script = args[i]; } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); // We don't know whether this library is part of libc or libc++ until we resolve the target. // So we simply append to the list for now. i += 1; @@ -743,7 +746,7 @@ fn buildOutputType( mem.eql(u8, arg, "-I") or mem.eql(u8, arg, "-dirafter")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try clang_argv.append(arg); try clang_argv.append(args[i]); @@ -753,19 +756,19 @@ fn buildOutputType( } i += 1; version = std.builtin.Version.parse(args[i]) catch |err| { - fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); + fatal("unable to parse --version '{s}': {s}", .{ args[i], @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "-target")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_arch_os_abi = args[i]; } else if (mem.eql(u8, arg, "-mcpu")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_mcpu = args[i]; } else if (mem.eql(u8, arg, "-mcmodel")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; machine_code_model = parseCodeModel(args[i]); } else if (mem.startsWith(u8, arg, "-ofmt=")) { @@ -777,35 +780,35 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "-O")) { optimize_mode_string = arg["-O".len..]; } else if (mem.eql(u8, arg, "--dynamic-linker")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_dynamic_linker = args[i]; } else if (mem.eql(u8, arg, "--libc")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; libc_paths_file = args[i]; } else if (mem.eql(u8, arg, "--test-filter")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; test_filter = args[i]; } else if (mem.eql(u8, arg, "--test-name-prefix")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; test_name_prefix = args[i]; } else if (mem.eql(u8, arg, "--test-cmd")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try test_exec_args.append(args[i]); } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_local_cache_dir = args[i]; } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_global_cache_dir = args[i]; } else if (mem.eql(u8, arg, "--override-lib-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_lib_dir = args[i]; } else if (mem.eql(u8, arg, "-fcompiler-rt")) { @@ -968,7 +971,7 @@ fn buildOutputType( { try clang_argv.append(arg); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else switch (Compilation.classifyFileExt(arg)) { .object, .static_library, .shared_library => { @@ -982,19 +985,19 @@ fn buildOutputType( }, .zig, .zir => { if (root_src_file) |other| { - fatal("found another zig file '{}' after root source file '{}'", .{ arg, other }); + fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); } else { root_src_file = arg; } }, .unknown => { - fatal("unrecognized file extension of parameter '{}'", .{arg}); + fatal("unrecognized file extension of parameter '{s}'", .{arg}); }, } } if (optimize_mode_string) |s| { optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse - fatal("unrecognized optimization mode: '{}'", .{s}); + fatal("unrecognized optimization mode: '{s}'", .{s}); } }, .cc, .cpp => { @@ -1018,7 +1021,7 @@ fn buildOutputType( var it = ClangArgIterator.init(arena, all_args); while (it.has_next) { it.next() catch |err| { - fatal("unable to parse command line parameters: {}", .{@errorName(err)}); + fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); }; switch (it.zig_equivalent) { .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown @@ -1038,7 +1041,7 @@ fn buildOutputType( }, .zig, .zir => { if (root_src_file) |other| { - fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other }); + fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); } else { root_src_file = it.only_arg; } @@ -1153,7 +1156,7 @@ fn buildOutputType( if (mem.eql(u8, arg, "-soname")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } const name = linker_args.items[i]; soname = .{ .yes = name }; @@ -1185,7 +1188,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-rpath")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } try rpath_list.append(linker_args.items[i]); } else if (mem.eql(u8, arg, "-I") or @@ -1194,7 +1197,7 @@ fn buildOutputType( { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } target_dynamic_linker = linker_args.items[i]; } else if (mem.eql(u8, arg, "-E") or @@ -1205,7 +1208,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--version-script")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version_script = linker_args.items[i]; } else if (mem.startsWith(u8, arg, "-O")) { @@ -1227,7 +1230,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-z")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } const z_arg = linker_args.items[i]; if (mem.eql(u8, z_arg, "nodelete")) { @@ -1235,44 +1238,44 @@ fn buildOutputType( } else if (mem.eql(u8, z_arg, "defs")) { linker_z_defs = true; } else { - warn("unsupported linker arg: -z {}", .{z_arg}); + warn("unsupported linker arg: -z {s}", .{z_arg}); } } else if (mem.eql(u8, arg, "--major-image-version")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "--minor-image-version")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "--stack")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--image-base")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else { - warn("unsupported linker arg: {}", .{arg}); + warn("unsupported linker arg: {s}", .{arg}); } } @@ -1328,7 +1331,7 @@ fn buildOutputType( } if (arg_mode == .translate_c and c_source_files.items.len != 1) { - fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); + fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len}); } if (root_src_file == null and arg_mode == .zig_test) { @@ -1373,25 +1376,25 @@ fn buildOutputType( help: { var help_text = std.ArrayList(u8).init(arena); for (diags.arch.?.allCpuModels()) |cpu| { - help_text.writer().print(" {}\n", .{cpu.name}) catch break :help; + help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; } - std.log.info("Available CPUs for architecture '{}': {}", .{ + std.log.info("Available CPUs for architecture '{s}': {s}", .{ @tagName(diags.arch.?), help_text.items, }); } - fatal("Unknown CPU: '{}'", .{diags.cpu_name.?}); + fatal("Unknown CPU: '{s}'", .{diags.cpu_name.?}); }, error.UnknownCpuFeature => { help: { var help_text = std.ArrayList(u8).init(arena); for (diags.arch.?.allFeaturesList()) |feature| { - help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help; + help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; } - std.log.info("Available CPU features for architecture '{}': {}", .{ + std.log.info("Available CPU features for architecture '{s}': {s}", .{ @tagName(diags.arch.?), help_text.items, }); } - fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name}); + fatal("Unknown CPU feature: '{s}'", .{diags.unknown_feature_name}); }, else => |e| return e, }; @@ -1431,16 +1434,40 @@ fn buildOutputType( if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena) catch |err| { - fatal("unable to detect native system paths: {}", .{@errorName(err)}); + fatal("unable to detect native system paths: {s}", .{@errorName(err)}); }; for (paths.warnings.items) |warning| { - warn("{}", .{warning}); + warn("{s}", .{warning}); } + + const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: { + const at_least_big_sur = target_info.target.os.getVersionRange().semver.min.major >= 11; + if (at_least_big_sur) { + const sdk_path = try std.zig.system.getSDKPath(arena); + try clang_argv.ensureCapacity(clang_argv.items.len + 2); + clang_argv.appendAssumeCapacity("-isysroot"); + clang_argv.appendAssumeCapacity(sdk_path); + break :outer true; + } + break :outer false; + } else false; + try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); + const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem"; for (paths.include_dirs.items) |include_dir| { - clang_argv.appendAssumeCapacity("-isystem"); + clang_argv.appendAssumeCapacity(isystem_flag); clang_argv.appendAssumeCapacity(include_dir); } + + try clang_argv.ensureCapacity(clang_argv.items.len + paths.framework_dirs.items.len * 2); + try framework_dirs.ensureCapacity(framework_dirs.items.len + paths.framework_dirs.items.len); + const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework"; + for (paths.framework_dirs.items) |framework_dir| { + clang_argv.appendAssumeCapacity(iframework_flag); + clang_argv.appendAssumeCapacity(framework_dir); + framework_dirs.appendAssumeCapacity(framework_dir); + } + for (paths.lib_dirs.items) |lib_dir| { try lib_dirs.append(lib_dir); } @@ -1468,7 +1495,7 @@ fn buildOutputType( } else if (mem.eql(u8, ofmt, "raw")) { break :blk .raw; } else { - fatal("unsupported object format: {}", .{ofmt}); + fatal("unsupported object format: {s}", .{ofmt}); } }; @@ -1538,7 +1565,7 @@ fn buildOutputType( } if (fs.path.dirname(full_path)) |dirname| { const handle = fs.cwd().openDir(dirname, .{}) catch |err| { - fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) }); + fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) }); }; cleanup_emit_bin_dir = handle; break :b Compilation.EmitLoc{ @@ -1561,19 +1588,19 @@ fn buildOutputType( }, }; - const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}); + const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name}); var emit_h_resolved = try emit_h.resolve(default_h_basename); defer emit_h_resolved.deinit(); - const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name}); + const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name}); var emit_asm_resolved = try emit_asm.resolve(default_asm_basename); defer emit_asm_resolved.deinit(); - const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name}); + const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name}); var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename); defer emit_llvm_ir_resolved.deinit(); - const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name}); + const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name}); var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename); defer emit_analysis_resolved.deinit(); @@ -1585,10 +1612,10 @@ fn buildOutputType( .yes_default_path => blk: { if (root_src_file) |rsf| { if (mem.endsWith(u8, rsf, ".zir")) { - break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name}); + break :blk try std.fmt.allocPrint(arena, "{s}.out.zir", .{root_name}); } } - break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name}); + break :blk try std.fmt.allocPrint(arena, "{s}.zir", .{root_name}); }, .yes => |p| p, }; @@ -1618,7 +1645,7 @@ fn buildOutputType( } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -1631,7 +1658,7 @@ fn buildOutputType( if (libc_paths_file) |paths_file| { libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| { - fatal("unable to parse libc paths file: {}", .{@errorName(err)}); + fatal("unable to parse libc paths file: {s}", .{@errorName(err)}); }; } @@ -1767,7 +1794,7 @@ fn buildOutputType( .disable_lld_caching = !have_enable_cache, .subsystem = subsystem, }) catch |err| { - fatal("unable to create compilation: {}", .{@errorName(err)}); + fatal("unable to create compilation: {s}", .{@errorName(err)}); }; var comp_destroyed = false; defer if (!comp_destroyed) comp.destroy(); @@ -1794,7 +1821,7 @@ fn buildOutputType( }; updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(1), + error.SemanticAnalyzeFail => if (!watch) process.exit(1), else => |e| return e, }; try comp.makeBinFileExecutable(); @@ -1829,9 +1856,16 @@ fn buildOutputType( else => unreachable, } } - try argv.appendSlice(&[_][]const u8{ - exe_path, self_exe_path, - }); + // when testing pass the zig_exe_path to argv + if (arg_mode == .zig_test) + try argv.appendSlice(&[_][]const u8{ + exe_path, self_exe_path, + }) + // when running just pass the current exe + else + try argv.appendSlice(&[_][]const u8{ + exe_path, + }); } else { for (test_exec_args.items) |arg| { if (arg) |a| { @@ -1890,12 +1924,12 @@ fn buildOutputType( if (!watch) return cleanExit(); } else { const cmd = try argvCmd(arena, argv.items); - fatal("the following test command failed with exit code {}:\n{}", .{ code, cmd }); + fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); } }, else => { const cmd = try argvCmd(arena, argv.items); - fatal("the following test command crashed:\n{}", .{cmd}); + fatal("the following test command crashed:\n{s}", .{cmd}); }, } }, @@ -1912,7 +1946,7 @@ fn buildOutputType( try stderr.print("(zig) ", .{}); try comp.makeBinFileExecutable(); if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { - try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); + try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); continue; }) |line| { const actual_line = mem.trimRight(u8, line, "\r\n "); @@ -1930,7 +1964,7 @@ fn buildOutputType( } else if (mem.eql(u8, actual_line, "help")) { try stderr.writeAll(repl_help); } else { - try stderr.print("unknown command: {}\n", .{actual_line}); + try stderr.print("unknown command: {s}\n", .{actual_line}); } } else { break; @@ -1988,14 +2022,14 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name}); + const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); defer if (enable_cache) man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects _ = man.addFile(c_source_file.src_path, null) catch |err| { - fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) }); + fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; const digest = if (try man.hit()) man.final() else digest: { @@ -2010,7 +2044,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi break :blk null; const c_src_basename = fs.path.basename(c_source_file.src_path); - const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename}); + const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename}); const out_dep_path = try comp.tmpFilePath(arena, dep_basename); break :blk out_dep_path; }; @@ -2045,7 +2079,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), error.SemanticAnalyzeFail => { for (clang_errors) |clang_err| { - std.debug.print("{}:{}:{}: {}\n", .{ + std.debug.print("{s}:{d}:{d}: {s}\n", .{ if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", clang_err.line + 1, clang_err.column + 1, @@ -2063,7 +2097,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { - warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); }; } @@ -2078,7 +2112,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi _ = try std.zig.render(comp.gpa, bos.writer(), tree); try bos.flush(); - man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)}); + man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)}); break :digest digest; }; @@ -2087,7 +2121,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename, }); - try io.getStdOut().writer().print("{}\n", .{full_zig_path}); + try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); return cleanExit(); } else { const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); @@ -2124,10 +2158,10 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { try stdout.writeAll(usage_libc); return cleanExit(); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else if (input_file != null) { - fatal("unexpected extra parameter: '{}'", .{arg}); + fatal("unexpected extra parameter: '{s}'", .{arg}); } else { input_file = arg; } @@ -2135,7 +2169,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { } if (input_file) |libc_file| { var libc = LibCInstallation.parse(gpa, libc_file) catch |err| { - fatal("unable to parse libc file: {}", .{@errorName(err)}); + fatal("unable to parse libc file: {s}", .{@errorName(err)}); }; defer libc.deinit(gpa); } else { @@ -2143,7 +2177,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { .allocator = gpa, .verbose = true, }) catch |err| { - fatal("unable to detect native libc: {}", .{@errorName(err)}); + fatal("unable to detect native libc: {s}", .{@errorName(err)}); }; defer libc.deinit(gpa); @@ -2181,16 +2215,16 @@ pub fn cmdInit( try io.getStdOut().writeAll(usage_init); return cleanExit(); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else { - fatal("unexpected extra parameter: '{}'", .{arg}); + fatal("unexpected extra parameter: '{s}'", .{arg}); } } } const self_exe_path = try fs.selfExePathAlloc(arena); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -2208,7 +2242,7 @@ pub fn cmdInit( const max_bytes = 10 * 1024 * 1024; const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { - fatal("unable to read template file 'build.zig': {}", .{@errorName(err)}); + fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)}); }; var modified_build_zig_contents = std.ArrayList(u8).init(arena); try modified_build_zig_contents.ensureCapacity(build_zig_contents.len); @@ -2220,13 +2254,13 @@ pub fn cmdInit( } } const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { - fatal("unable to read template file 'main.zig': {}", .{@errorName(err)}); + fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)}); }; if (fs.cwd().access("build.zig", .{})) |_| { fatal("existing build.zig file would be overwritten", .{}); } else |err| switch (err) { error.FileNotFound => {}, - else => fatal("unable to test existence of build.zig: {}\n", .{@errorName(err)}), + else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}), } var src_dir = try fs.cwd().makeOpenPath("src", .{}); defer src_dir.close(); @@ -2287,23 +2321,23 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--build-file")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; build_file = args[i]; continue; } else if (mem.eql(u8, arg, "--override-lib-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_lib_dir = args[i]; try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); continue; } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_local_cache_dir = args[i]; continue; } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_global_cache_dir = args[i]; continue; @@ -2320,7 +2354,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -2361,7 +2395,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else |err| switch (err) { error.FileNotFound => { dirname = fs.path.dirname(dirname) orelse { - std.log.info("{}", .{ + std.log.info("{s}", .{ \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`, \\or see `zig --help` for more options. }); @@ -2443,7 +2477,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v .self_exe_path = self_exe_path, .thread_pool = &thread_pool, }) catch |err| { - fatal("unable to create compilation: {}", .{@errorName(err)}); + fatal("unable to create compilation: {s}", .{@errorName(err)}); }; defer comp.destroy(); @@ -2469,11 +2503,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v .Exited => |code| { if (code == 0) return cleanExit(); const cmd = try argvCmd(arena, child_argv); - fatal("the following build command failed with exit code {}:\n{}", .{ code, cmd }); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); }, else => { const cmd = try argvCmd(arena, child_argv); - fatal("the following build command crashed:\n{}", .{cmd}); + fatal("the following build command crashed:\n{s}", .{cmd}); }, } } @@ -2540,14 +2574,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { i += 1; const next_arg = args[i]; color = std.meta.stringToEnum(Color, next_arg) orelse { - fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); }; } else if (mem.eql(u8, arg, "--stdin")) { stdin_flag = true; } else if (mem.eql(u8, arg, "--check")) { check_flag = true; } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else { try input_files.append(arg); @@ -2566,7 +2600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { defer gpa.free(source_code); const tree = std.zig.parse(gpa, source_code) catch |err| { - fatal("error parsing stdin: {}", .{err}); + fatal("error parsing stdin: {s}", .{err}); }; defer tree.deinit(); @@ -2605,7 +2639,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { for (input_files.items) |file_path| { // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { - fatal("unable to open '{}': {}", .{ file_path, err }); + fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) }); }; defer gpa.free(real_path); @@ -2644,7 +2678,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_ fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), else => { - warn("unable to format '{}': {}", .{ file_path, err }); + warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) }); fmt.any_error = true; return; }, @@ -2678,7 +2712,7 @@ fn fmtPathDir( try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); } else { fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { - warn("unable to format '{}': {}", .{ full_path, err }); + warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) }); fmt.any_error = true; return; }; @@ -2737,7 +2771,7 @@ fn fmtPathFile( const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); if (anything_changed) { const stdout = io.getStdOut().writer(); - try stdout.print("{}\n", .{file_path}); + try stdout.print("{s}\n", .{file_path}); fmt.any_error = true; } } else { @@ -2755,7 +2789,7 @@ fn fmtPathFile( try af.file.writeAll(fmt.out_buffer.items); try af.finish(); const stdout = io.getStdOut().writer(); - try stdout.print("{}\n", .{file_path}); + try stdout.print("{s}\n", .{file_path}); } } @@ -2788,7 +2822,7 @@ fn printErrMsgToFile( const text = text_buf.items; const stream = file.outStream(); - try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); + try stream.print("{s}:{d}:{d}: error: {s}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); if (!color_on) return; @@ -2960,7 +2994,7 @@ pub const ClangArgIterator = struct { const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit const resp_file_path = arg[1..]; const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { - fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) }); + fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) }); }; defer allocator.free(resp_contents); // TODO is there a specification for this file format? Let's find it and make this parsing more robust @@ -3033,7 +3067,7 @@ pub const ClangArgIterator = struct { const prefix_len = clang_arg.matchStartsWith(arg); if (prefix_len == arg.len) { if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.only_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3054,7 +3088,7 @@ pub const ClangArgIterator = struct { if (prefix_len != 0) { self.only_arg = arg[prefix_len..]; if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.second_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3065,7 +3099,7 @@ pub const ClangArgIterator = struct { }, .separate => if (clang_arg.matchEql(arg) > 0) { if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.only_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3091,7 +3125,7 @@ pub const ClangArgIterator = struct { }, } else { - fatal("Unknown Clang option: '{}'", .{arg}); + fatal("Unknown Clang option: '{s}'", .{arg}); } } @@ -3119,7 +3153,7 @@ pub const ClangArgIterator = struct { fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse - fatal("unsupported machine code model: '{}'", .{arg}); + fatal("unsupported machine code model: '{s}'", .{arg}); } /// Raise the open file descriptor limit. Ask and ye shall receive. @@ -3239,7 +3273,7 @@ fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !s // CPU model & feature detection is todo so here we rely on LLVM. // https://github.com/ziglang/zig/issues/4591 if (!build_options.have_llvm) - fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)}); + fatal("CPU features detection is not yet available for {s} without LLVM extensions", .{@tagName(arch)}); const llvm = @import("llvm_bindings.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); diff --git a/src/mingw.zig b/src/mingw.zig index 246b0f33dcf8..d55cc28b2ba6 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -381,7 +381,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { const term = child.wait() catch |err| { // TODO surface a proper error here - log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) }); + log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); return error.ClangPreprocessorFailed; }; @@ -395,7 +395,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }, else => { // TODO surface a proper error here - log.err("clang terminated unexpectedly with stderr: {}", .{stderr}); + log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); return error.ClangPreprocessorFailed; }, } diff --git a/src/musl.zig b/src/musl.zig index f67fe90addaf..a865e786230a 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -155,21 +155,21 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { if (!is_arch_specific) { // Look for an arch specific override. override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.s", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.s", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) continue; override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.S", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.S", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) continue; override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.c", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.c", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) @@ -322,7 +322,7 @@ fn add_cc_args( const target = comp.getTarget(); const arch_name = target_util.archMuslName(target.cpu.arch); const os_name = @tagName(target.os.tag); - const triple = try std.fmt.allocPrint(arena, "{}-{}-musl", .{ arch_name, os_name }); + const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name }); const o_arg = if (want_O3) "-O3" else "-Os"; try args.appendSlice(&[_][]const u8{ diff --git a/src/print_env.zig b/src/print_env.zig index 8aa692e64432..bcf4a983abca 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -9,7 +9,7 @@ pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: std.fs.File.Wri defer gpa.free(self_exe_path); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer gpa.free(zig_lib_directory.path.?); defer zig_lib_directory.handle.close(); diff --git a/src/print_targets.zig b/src/print_targets.zig index 724cb7a9ac39..cf55eee5168d 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -18,7 +18,7 @@ pub fn cmdTargets( native_target: Target, ) !void { var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); defer allocator.free(zig_lib_directory.path.?); @@ -61,7 +61,7 @@ pub fn cmdTargets( try jws.objectField("libc"); try jws.beginArray(); for (target.available_libcs) |libc| { - const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), }); defer allocator.free(tmp); diff --git a/src/stage1.zig b/src/stage1.zig index 1d50a71ad17c..cf3a252ce8fd 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -37,14 +37,14 @@ pub export fn main(argc: c_int, argv: [*][*:0]u8) c_int { defer arena_instance.deinit(); const arena = &arena_instance.allocator; - const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"}); + const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{s}", .{"OutOfMemory"}); for (args) |*arg, i| { arg.* = mem.spanZ(argv[i]); } if (std.builtin.mode == .Debug) { stage2.mainArgs(gpa, arena, args) catch unreachable; } else { - stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); + stage2.mainArgs(gpa, arena, args) catch |err| fatal("{s}", .{@errorName(err)}); } return 0; } diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index d3ed3cfaabe3..edda765ff901 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -24207,6 +24207,8 @@ static IrInstGen *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstSrcSwi ref_type->data.pointer.allow_zero); return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); + } else if (instruction->prongs_len > 1) { + return target_value_ptr; } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); diff --git a/src/stage1/os.hpp b/src/stage1/os.hpp index 4f38403a9eb9..1dd30804ba2c 100644 --- a/src/stage1/os.hpp +++ b/src/stage1/os.hpp @@ -46,7 +46,7 @@ #endif #if defined(ZIG_OS_WINDOWS) -#define ZIG_PRI_usize "I64u" +#define ZIG_PRI_usize "Iu" #define ZIG_PRI_i64 "I64d" #define ZIG_PRI_u64 "I64u" #define ZIG_PRI_llu "I64u" diff --git a/src/test.zig b/src/test.zig index 6d76ae39c1c1..67a30f1f325f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -135,6 +135,7 @@ pub const TestContext = struct { extension: Extension, object_format: ?std.builtin.ObjectFormat = null, emit_h: bool = false, + llvm_backend: bool = false, files: std.ArrayList(File), @@ -266,6 +267,21 @@ pub const TestContext = struct { return &ctx.cases.items[ctx.cases.items.len - 1]; } + /// Adds a test case that uses the LLVM backend to emit an executable. + /// Currently this implies linking libc, because only then we can generate a testable executable. + pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .extension = .Zig, + .files = std.ArrayList(File).init(ctx.cases.allocator), + .llvm_backend = true, + }) catch unreachable; + return &ctx.cases.items[ctx.cases.items.len - 1]; + } + pub fn addObj( ctx: *TestContext, name: []const u8, @@ -518,10 +534,30 @@ pub const TestContext = struct { try thread_pool.init(std.testing.allocator); defer thread_pool.deinit(); + // Use the same global cache dir for all the tests, such that we for example don't have to + // rebuild musl libc for every case (when LLVM backend is enabled). + var global_tmp = std.testing.tmpDir(.{}); + defer global_tmp.cleanup(); + + var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + const tmp_dir_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); + defer std.testing.allocator.free(tmp_dir_path); + + const global_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ tmp_dir_path, "zig-cache" }), + }; + defer std.testing.allocator.free(global_cache_directory.path.?); + for (self.cases.items) |case| { if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch) continue; + // Skip tests that require LLVM backend when it is not available + if (!build_options.have_llvm and case.llvm_backend) + continue; + var prg_node = root_node.start(case.name, case.updates.items.len); prg_node.activate(); defer prg_node.end(); @@ -537,6 +573,7 @@ pub const TestContext = struct { case, zig_lib_directory, &thread_pool, + global_cache_directory, ); } } @@ -548,6 +585,7 @@ pub const TestContext = struct { case: Case, zig_lib_directory: Compilation.Directory, thread_pool: *ThreadPool, + global_cache_directory: Compilation.Directory, ) !void { const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); const target = target_info.target; @@ -601,7 +639,7 @@ pub const TestContext = struct { null; const comp = try Compilation.create(allocator, .{ .local_cache_directory = zig_cache_directory, - .global_cache_directory = zig_cache_directory, + .global_cache_directory = global_cache_directory, .zig_lib_directory = zig_lib_directory, .thread_pool = thread_pool, .root_name = "test_case", @@ -619,6 +657,9 @@ pub const TestContext = struct { .object_format = case.object_format, .is_native_os = case.target.isNativeOs(), .is_native_abi = case.target.isNativeAbi(), + .link_libc = case.llvm_backend, + .use_llvm = case.llvm_backend, + .self_exe_path = std.testing.zig_exe_path, }); defer comp.destroy(); @@ -660,7 +701,7 @@ pub const TestContext = struct { } } if (comp.bin_file.cast(link.File.C)) |c_file| { - std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{ + std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{ c_file.main.items, }); } diff --git a/src/translate_c.zig b/src/translate_c.zig index 1b2aa4b2195d..7aebcf069b4e 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -136,7 +136,7 @@ const Scope = struct { var proposed_name = name_copy; while (scope.contains(proposed_name)) { scope.mangle_count += 1; - proposed_name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, scope.mangle_count }); + proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count }); } try scope.variables.append(.{ .name = name_copy, .alias = proposed_name }); return proposed_name; @@ -290,7 +290,7 @@ pub const Context = struct { const line = c.source_manager.getSpellingLineNumber(spelling_loc); const column = c.source_manager.getSpellingColumnNumber(spelling_loc); - return std.fmt.allocPrint(c.arena, "{}:{}:{}", .{ filename, line, column }); + return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); } fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call { @@ -440,7 +440,7 @@ pub fn translate( mem.copy(*ast.Node, root_node.decls(), context.root_decls.items); if (false) { - std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", .{source_buffer.items}); + std.debug.warn("debug source:\n{s}\n==EOF==\ntokens:\n", .{source_buffer.items}); for (context.token_ids.items) |token| { std.debug.warn("{}\n", .{token}); } @@ -530,7 +530,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { }, else => { const decl_name = try c.str(decl.getDeclKindName()); - try emitWarning(c, decl.getLocation(), "ignoring {} declaration", .{decl_name}); + try emitWarning(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); }, } } @@ -625,7 +625,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const param_name = if (param.name_token) |name_tok| tokenSlice(c, name_tok) else - return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}); + return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name}); const c_param = fn_decl.getParamDecl(param_id); const qual_type = c_param.getOriginalType(); @@ -634,7 +634,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const mangled_param_name = try block_scope.makeMangledName(c, param_name); if (!is_const) { - const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name}); + const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); const arg_name = try block_scope.makeMangledName(c, bare_arg_name); const mut_tok = try appendToken(c, .Keyword_var, "var"); @@ -727,7 +727,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ var_name, c.getMangle() }) else var_name; + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ var_name, c.getMangle() }) else var_name; const var_decl_loc = var_decl.getLocation(); const qual_type = var_decl.getTypeSourceInfo_getType(); @@ -808,7 +808,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -887,7 +887,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ typedef_name, c.getMangle() }) else typedef_name; + const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; if (checkForBuiltinTypedef(checked_name)) |builtin| { return transTypeDefAsBuiltin(c, typedef_decl, builtin); } @@ -945,7 +945,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as // Record declarations such as `struct {...} x` have no name but they're not // anonymous hence here isAnonymousStructOrUnion is not needed if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); is_unnamed = true; } @@ -958,11 +958,11 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as container_kind_name = "struct"; container_kind = .Keyword_struct; } else { - try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name}); + try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name}); return null; } - const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name }); + const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; @@ -1003,7 +1003,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); + try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); break :blk opaque_type; } @@ -1011,7 +1011,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name}); + try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); break :blk opaque_type; } @@ -1019,7 +1019,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) { // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. - raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{unnamed_field_count}); + raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); unnamed_field_count += 1; is_anon = true; } @@ -1030,7 +1030,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name }); + try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); break :blk opaque_type; }, else => |e| return e, @@ -1110,11 +1110,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); var is_unnamed = false; if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); is_unnamed = true; } - const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name}); + const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; @@ -1385,7 +1385,7 @@ fn transStmt( rp, error.UnsupportedTranslation, stmt.getBeginLoc(), - "TODO implement translation of stmt class {}", + "TODO implement translation of stmt class {s}", .{@tagName(sc)}, ); }, @@ -1684,7 +1684,7 @@ fn transDeclStmtOne( rp, error.UnsupportedTranslation, decl.getLocation(), - "TODO implement translation of DeclStmt kind {}", + "TODO implement translation of DeclStmt kind {s}", .{@tagName(kind)}, ), } @@ -1782,7 +1782,7 @@ fn transImplicitCastExpr( rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, expr).getBeginLoc(), - "TODO implement translation of CastKind {}", + "TODO implement translation of CastKind {s}", .{@tagName(kind)}, ), } @@ -2043,7 +2043,7 @@ fn transStringLiteral( rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), - "TODO: support string literal kind {}", + "TODO: support string literal kind {s}", .{kind}, ), } @@ -2168,7 +2168,6 @@ fn transCCast( // @boolToInt returns either a comptime_int or a u1 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast // instead of @as - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); builtin_node.params()[0] = expr; builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); @@ -2455,7 +2454,7 @@ fn transInitListExpr( ); } else { const type_name = rp.c.str(qual_type.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{}'", .{type_name}); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); } } @@ -3957,7 +3956,7 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang. const node = try rp.c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .IntegerLiteral }, - .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), + .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}), }; return &node.base; } @@ -4433,7 +4432,8 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { } fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { - const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int}); + const fmt_s = if (comptime std.meta.trait.isIntegerNumber(@TypeOf(int))) "{d}" else "{s}"; + const token = try appendTokenFmt(c, .IntegerLiteral, fmt_s, .{int}); const node = try c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .IntegerLiteral }, @@ -4442,8 +4442,8 @@ fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { return &node.base; } -fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node { - const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); +fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node { + const token = try appendTokenFmt(c, .FloatLiteral, "{s}", .{str}); const node = try c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .FloatLiteral }, @@ -4484,7 +4484,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a _ = try appendToken(c, .Comma, ","); } const param_name_tok = param.name_token orelse - try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()}); + try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()}); _ = try appendToken(c, .Colon, ":"); @@ -4916,7 +4916,7 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo }, else => { const type_name = rp.c.str(ty.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); }, } } @@ -4999,7 +4999,7 @@ fn transCC( rp, error.UnsupportedType, source_loc, - "unsupported calling convention: {}", + "unsupported calling convention: {s}", .{@tagName(clang_cc)}, ), } @@ -5027,7 +5027,7 @@ fn transFnNoProto( is_pub: bool, ) !*ast.Node.FnProto { const cc = try transCC(rp, fn_ty, source_loc); - const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true; + const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } @@ -5117,7 +5117,7 @@ fn finishTransFnProto( _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -5214,7 +5214,7 @@ fn revertAndWarn( fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { const args_prefix = .{c.locStr(loc)}; - _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args); + _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args); } pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { @@ -5228,7 +5228,7 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args); const rparen_tok = try appendToken(c, .RParen, ")"); const semi_tok = try appendToken(c, .Semicolon, ";"); - _ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)}); + _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)}); const msg_node = try c.arena.create(ast.Node.OneToken); msg_node.* = .{ @@ -5258,7 +5258,7 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex { std.debug.assert(token_id != .Identifier); // use appendIdentifier - return appendTokenFmt(c, token_id, "{}", .{bytes}); + return appendTokenFmt(c, token_id, "{s}", .{bytes}); } fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex { @@ -5329,7 +5329,7 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { } fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node { - const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name}); + const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name}); const identifier = try c.arena.create(ast.Node.OneToken); identifier.* = .{ .base = .{ .tag = .Identifier }, @@ -5390,7 +5390,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { const name = try c.str(raw_name); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, c.getMangle() }) else name; + const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, c.getMangle() }) else name; if (scope.containsNow(mangled_name)) { continue; } @@ -5468,7 +5468,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { const init_node = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) - return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); + return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); const semicolon_token = try appendToken(c, .Semicolon, ";"); const node = try ast.Node.VarDecl.create(c.arena, .{ @@ -5540,7 +5540,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const expr = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) - return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); + return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); _ = try appendToken(c, .Semicolon, ";"); const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { const stmts = expr.blockStatements(); @@ -5623,11 +5623,11 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { switch (lit_bytes[1]) { '0'...'7' => { // Octal - lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes}); }, 'X' => { // Hexadecimal with capital X, valid in C but not in Zig - lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]}); }, else => {}, } @@ -5659,7 +5659,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') - lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); if (suffix == .none) { return transCreateNodeFloat(c, lit_bytes); } @@ -5916,11 +5916,11 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* // struct Foo will be declared as struct_Foo by transRecordDecl const next_id = m.next().?; if (next_id != .Identifier) { - try m.fail(c, "unable to translate C expr: expected Identifier instead got: {}", .{@tagName(next_id)}); + try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)}); return error.ParseError; } - const ident_token = try appendTokenFmt(c, .Identifier, "{}_{}", .{ slice, m.slice() }); + const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() }); const identifier = try c.arena.create(ast.Node.OneToken); identifier.* = .{ .base = .{ .tag = .Identifier }, @@ -5937,7 +5937,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* const next_id = m.next().?; if (next_id != .RParen) { - try m.fail(c, "unable to translate C expr: expected ')' instead got: {}", .{@tagName(next_id)}); + try m.fail(c, "unable to translate C expr: expected ')' instead got: {s}", .{@tagName(next_id)}); return error.ParseError; } var saw_l_paren = false; @@ -5995,7 +5995,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* return &group_node.base; }, else => { - try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)}); + try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); return error.ParseError; }, } diff --git a/src/type.zig b/src/type.zig index 7e6294a75a8e..bacc5789793f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -602,21 +602,21 @@ pub const Type = extern union { }, .array_u8 => { const len = ty.castTag(.array_u8).?.data; - return out_stream.print("[{}]u8", .{len}); + return out_stream.print("[{d}]u8", .{len}); }, .array_u8_sentinel_0 => { const len = ty.castTag(.array_u8_sentinel_0).?.data; - return out_stream.print("[{}:0]u8", .{len}); + return out_stream.print("[{d}:0]u8", .{len}); }, .array => { const payload = ty.castTag(.array).?.data; - try out_stream.print("[{}]", .{payload.len}); + try out_stream.print("[{d}]", .{payload.len}); ty = payload.elem_type; continue; }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel }); + try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel }); ty = payload.elem_type; continue; }, diff --git a/src/value.zig b/src/value.zig index c6eac57ce272..905ac1da1e69 100644 --- a/src/value.zig +++ b/src/value.zig @@ -330,11 +330,14 @@ pub const Value = extern union { .int_type => return self.copyPayloadShallow(allocator, Payload.IntType), .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64), .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64), - .int_big_positive => { - @panic("TODO implement copying of big ints"); - }, - .int_big_negative => { - @panic("TODO implement copying of big ints"); + .int_big_positive, .int_big_negative => { + const old_payload = self.cast(Payload.BigInt).?; + const new_payload = try allocator.create(Payload.BigInt); + new_payload.* = .{ + .base = .{ .tag = self.ptr_otherwise.tag }, + .data = try allocator.dupe(std.math.big.Limb, old_payload.data), + }; + return Value{ .ptr_otherwise = &new_payload.base }; }, .function => return self.copyPayloadShallow(allocator, Payload.Function), .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl), @@ -464,7 +467,7 @@ pub const Value = extern union { .ty => return val.castTag(.ty).?.data.format("", options, out_stream), .int_type => { const int_type = val.castTag(.int_type).?.data; - return out_stream.print("{}{}", .{ + return out_stream.print("{s}{d}", .{ if (int_type.signed) "s" else "u", int_type.bits, }); @@ -507,7 +510,7 @@ pub const Value = extern union { } return out_stream.writeAll("}"); }, - .@"error" => return out_stream.print("error.{}", .{val.castTag(.@"error").?.data.name}), + .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"), }; } diff --git a/src/zig_clang.h b/src/zig_clang.h index 32879062aee8..169fbcedfb26 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -8,14 +8,32 @@ #ifndef ZIG_ZIG_CLANG_H #define ZIG_ZIG_CLANG_H -#include "stage1/stage2.h" #include #include +#include + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler file // src/clang.zig. +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + struct ZigClangSourceLocation { unsigned ID; }; diff --git a/src/zir.zig b/src/zir.zig index ce8498d7e862..8019d0e030fa 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -25,12 +25,13 @@ pub const Decl = struct { /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for /// in-memory, analyzed instructions with types and values. +/// We use a table to map these instruction to their respective semantically analyzed +/// instructions because it is possible to have multiple analyses on the same ZIR +/// happening at the same time. pub const Inst = struct { tag: Tag, /// Byte offset into the source. src: usize, - /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions. - analyzed_inst: ?*ir.Inst = null, /// These names are used directly as the instruction names in the text format. pub const Tag = enum { @@ -126,6 +127,9 @@ pub const Inst = struct { coerce_to_ptr_elem, /// Emit an error message and fail compilation. compileerror, + /// Changes the maximum number of backwards branches that compile-time + /// code execution can use before giving up and making a compile error. + set_eval_branch_quota, /// Conditional branch. Splits control flow based on a boolean condition value. condbr, /// Special case, has no textual representation. @@ -346,6 +350,7 @@ pub const Inst = struct { .anyframe_type, .bitnot, .import, + .set_eval_branch_quota, => UnOp, .add, @@ -534,6 +539,7 @@ pub const Inst = struct { .switch_range, .typeof_peer, .resolve_inferred_alloc, + .set_eval_branch_quota, => false, .@"break", @@ -793,7 +799,9 @@ pub const Inst = struct { fn_type: *Inst, body: Module.Body, }, - kw_args: struct {}, + kw_args: struct { + is_inline: bool = false, + }, }; pub const FnType = struct { @@ -1150,7 +1158,7 @@ pub const Module = struct { for (self.decls) |decl, i| { write.next_instr_index = 0; - try stream.print("@{} ", .{decl.name}); + try stream.print("@{s} ", .{decl.name}); try write.writeInstToStream(stream, decl.inst); try stream.writeByte('\n'); } @@ -1206,13 +1214,13 @@ const Writer = struct { if (@typeInfo(arg_field.field_type) == .Optional) { if (@field(inst.kw_args, arg_field.name)) |non_optional| { if (need_comma) try stream.writeAll(", "); - try stream.print("{}=", .{arg_field.name}); + try stream.print("{s}=", .{arg_field.name}); try self.writeParamToStream(stream, &non_optional); need_comma = true; } } else { if (need_comma) try stream.writeAll(", "); - try stream.print("{}=", .{arg_field.name}); + try stream.print("{s}=", .{arg_field.name}); try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name)); need_comma = true; } @@ -1257,12 +1265,12 @@ const Writer = struct { self.next_instr_index += 1; try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined }); try stream.writeByteNTimes(' ', self.indent); - try stream.print("%{} ", .{my_i}); + try stream.print("%{d} ", .{my_i}); if (inst.cast(Inst.Block)) |block| { - const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{my_i}); + const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{d}", .{my_i}); try self.block_table.put(block, name); } else if (inst.cast(Inst.Loop)) |loop| { - const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{}", .{my_i}); + const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{d}", .{my_i}); try self.loop_table.put(loop, name); } self.indent += 2; @@ -1332,18 +1340,18 @@ const Writer = struct { fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void { if (self.inst_table.get(inst)) |info| { if (info.index) |i| { - try stream.print("%{}", .{info.index}); + try stream.print("%{d}", .{info.index}); } else { - try stream.print("@{}", .{info.name}); + try stream.print("@{s}", .{info.name}); } } else if (inst.cast(Inst.DeclVal)) |decl_val| { - try stream.print("@{}", .{decl_val.positionals.name}); + try stream.print("@{s}", .{decl_val.positionals.name}); } else if (inst.cast(Inst.DeclValInModule)) |decl_val| { - try stream.print("@{}", .{decl_val.positionals.decl.name}); + try stream.print("@{s}", .{decl_val.positionals.decl.name}); } else { // This should be unreachable in theory, but since ZIR is used for debugging the compiler // we output some debug text instead. - try stream.print("?{}?", .{@tagName(inst.tag)}); + try stream.print("?{s}?", .{@tagName(inst.tag)}); } } }; @@ -1424,7 +1432,7 @@ const Parser = struct { const decl = try parseInstruction(self, &body_context, ident); const ident_index = body_context.instructions.items.len; if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{}'", .{ident}); + return self.fail("redefinition of identifier '{s}'", .{ident}); } try body_context.instructions.append(decl.inst); continue; @@ -1510,7 +1518,7 @@ const Parser = struct { const decl = try parseInstruction(self, null, ident); const ident_index = self.decls.items.len; if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{}'", .{ident}); + return self.fail("redefinition of identifier '{s}'", .{ident}); } try self.decls.append(self.allocator, decl); }, @@ -1538,7 +1546,7 @@ const Parser = struct { for (bytes) |byte| { if (self.source[self.i] != byte) { self.i = start; - return self.fail("expected '{}'", .{bytes}); + return self.fail("expected '{s}'", .{bytes}); } self.i += 1; } @@ -1585,7 +1593,7 @@ const Parser = struct { return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start); } } - return self.fail("unknown instruction '{}'", .{fn_name}); + return self.fail("unknown instruction '{s}'", .{fn_name}); } fn parseInstructionGeneric( @@ -1621,7 +1629,7 @@ const Parser = struct { self.i += 1; skipSpace(self); } else if (self.source[self.i] == ')') { - return self.fail("expected positional parameter '{}'", .{arg_field.name}); + return self.fail("expected positional parameter '{s}'", .{arg_field.name}); } @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric( self, @@ -1648,7 +1656,7 @@ const Parser = struct { break; } } else { - return self.fail("unrecognized keyword parameter: '{}'", .{name}); + return self.fail("unrecognized keyword parameter: '{s}'", .{name}); } skipSpace(self); } @@ -1660,7 +1668,6 @@ const Parser = struct { .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]), .inst = &inst_specific.base, }; - //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents }); return decl; } @@ -1672,7 +1679,7 @@ const Parser = struct { ' ', '\n', ',', ')' => { const enum_name = self.source[start..self.i]; return std.meta.stringToEnum(T, enum_name) orelse { - return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) }); + return self.fail("tag '{s}' not a member of enum '{s}'", .{ enum_name, @typeName(T) }); }; }, 0 => return self.failByte(0), @@ -1710,7 +1717,7 @@ const Parser = struct { BigIntConst => return self.parseIntegerLiteral(), usize => { const big_int = try self.parseIntegerLiteral(); - return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)}); + return big_int.to(usize) catch |err| return self.fail("integer literal: {s}", .{@errorName(err)}); }, TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), @@ -1759,7 +1766,7 @@ const Parser = struct { }, else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), } - return self.fail("TODO parse parameter {}", .{@typeName(T)}); + return self.fail("TODO parse parameter {s}", .{@typeName(T)}); } fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst { @@ -1788,7 +1795,7 @@ const Parser = struct { const src = name_start - 1; if (local_ref) { self.i = src; - return self.fail("unrecognized identifier: {}", .{bad_name}); + return self.fail("unrecognized identifier: {s}", .{bad_name}); } else { const declval = try self.arena.allocator.create(Inst.DeclVal); declval.* = .{ @@ -1805,7 +1812,7 @@ const Parser = struct { } fn generateName(self: *Parser) ![]u8 { - const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index}); + const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.unnamed_index}); self.unnamed_index += 1; return result; } @@ -1848,44 +1855,325 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { /// For debugging purposes, prints a function representation to stderr. pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { const allocator = old_module.gpa; - var ctx: EmitZIR = .{ + var ctx: DumpTzir = .{ .allocator = allocator, - .decls = .{}, .arena = std.heap.ArenaAllocator.init(allocator), .old_module = &old_module, - .next_auto_name = 0, - .names = std.StringArrayHashMap(void).init(allocator), - .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), - .indent = 0, - .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), - .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), + .module_fn = module_fn, + .indent = 2, + .inst_table = DumpTzir.InstTable.init(allocator), + .partial_inst_table = DumpTzir.InstTable.init(allocator), + .const_table = DumpTzir.InstTable.init(allocator), }; - defer ctx.metadata.deinit(); - defer ctx.body_metadata.deinit(); - defer ctx.block_table.deinit(); - defer ctx.loop_table.deinit(); - defer ctx.decls.deinit(allocator); - defer ctx.names.deinit(); - defer ctx.primitive_table.deinit(); + defer ctx.inst_table.deinit(); + defer ctx.partial_inst_table.deinit(); + defer ctx.const_table.deinit(); defer ctx.arena.deinit(); - const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty; - _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| { - std.debug.print("unable to dump function: {}\n", .{err}); - return; - }; - var module = Module{ - .decls = ctx.decls.items, - .arena = ctx.arena, - .metadata = ctx.metadata, - .body_metadata = ctx.body_metadata, - }; - - module.dump(); + switch (module_fn.state) { + .queued => std.debug.print("(queued)", .{}), + .inline_only => std.debug.print("(inline_only)", .{}), + .in_progress => std.debug.print("(in_progress)", .{}), + .sema_failure => std.debug.print("(sema_failure)", .{}), + .dependency_failure => std.debug.print("(dependency_failure)", .{}), + .success => { + const writer = std.io.getStdErr().writer(); + ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); + }, + } } +const DumpTzir = struct { + allocator: *Allocator, + arena: std.heap.ArenaAllocator, + old_module: *const IrModule, + module_fn: *IrModule.Fn, + indent: usize, + inst_table: InstTable, + partial_inst_table: InstTable, + const_table: InstTable, + next_index: usize = 0, + next_partial_index: usize = 0, + next_const_index: usize = 0, + + const InstTable = std.AutoArrayHashMap(*ir.Inst, usize); + + fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { + // First pass to pre-populate the table so that we can show even invalid references. + // Must iterate the same order we iterate the second time. + // We also look for constants and put them in the const_table. + for (body.instructions) |inst| { + try dtz.inst_table.put(inst, dtz.next_index); + dtz.next_index += 1; + switch (inst.tag) { + .alloc, + .retvoid, + .unreach, + .breakpoint, + .dbg_stmt, + => {}, + + .ref, + .ret, + .bitcast, + .not, + .isnonnull, + .isnull, + .iserr, + .ptrtoint, + .floatcast, + .intcast, + .load, + .unwrap_optional, + .wrap_optional, + => { + const un_op = inst.cast(ir.Inst.UnOp).?; + try dtz.findConst(un_op.operand); + }, + + .add, + .sub, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .store, + .booland, + .boolor, + .bitand, + .bitor, + .xor, + => { + const bin_op = inst.cast(ir.Inst.BinOp).?; + try dtz.findConst(bin_op.lhs); + try dtz.findConst(bin_op.rhs); + }, + + .arg => {}, + + .br => { + const br = inst.castTag(.br).?; + try dtz.findConst(&br.block.base); + try dtz.findConst(br.operand); + }, + + .brvoid => { + const brvoid = inst.castTag(.brvoid).?; + try dtz.findConst(&brvoid.block.base); + }, + + // TODO fill out this debug printing + .assembly, + .block, + .call, + .condbr, + .constant, + .loop, + .varptr, + .switchbr, + => {}, + } + } + + std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); + + for (dtz.const_table.items()) |entry| { + const constant = entry.key.castTag(.constant).?; + try writer.print(" @{d}: {} = {};\n", .{ + entry.value, constant.base.ty, constant.val, + }); + } + + return dtz.dumpBody(body, writer); + } + + fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { + for (body.instructions) |inst| { + const my_index = dtz.next_partial_index; + try dtz.partial_inst_table.put(inst, my_index); + dtz.next_partial_index += 1; + + try writer.writeByteNTimes(' ', dtz.indent); + try writer.print("%{d}: {} = {s}(", .{ + my_index, inst.ty, @tagName(inst.tag), + }); + switch (inst.tag) { + .alloc, + .retvoid, + .unreach, + .breakpoint, + .dbg_stmt, + => try writer.writeAll(")\n"), + + .ref, + .ret, + .bitcast, + .not, + .isnonnull, + .isnull, + .iserr, + .ptrtoint, + .floatcast, + .intcast, + .load, + .unwrap_optional, + .wrap_optional, + => { + const un_op = inst.cast(ir.Inst.UnOp).?; + if (dtz.partial_inst_table.get(un_op.operand)) |operand_index| { + try writer.print("%{d})\n", .{operand_index}); + } else if (dtz.const_table.get(un_op.operand)) |operand_index| { + try writer.print("@{d})\n", .{operand_index}); + } else if (dtz.inst_table.get(un_op.operand)) |operand_index| { + try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{ + operand_index, + }); + } else { + try writer.writeAll("!BADREF!)\n"); + } + }, + + .add, + .sub, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .store, + .booland, + .boolor, + .bitand, + .bitor, + .xor, + => { + var lhs_kinky: ?usize = null; + var rhs_kinky: ?usize = null; + + const bin_op = inst.cast(ir.Inst.BinOp).?; + if (dtz.partial_inst_table.get(bin_op.lhs)) |operand_index| { + try writer.print("%{d}, ", .{operand_index}); + } else if (dtz.const_table.get(bin_op.lhs)) |operand_index| { + try writer.print("@{d}, ", .{operand_index}); + } else if (dtz.inst_table.get(bin_op.lhs)) |operand_index| { + lhs_kinky = operand_index; + try writer.print("%{d}, ", .{operand_index}); + } else { + try writer.writeAll("!BADREF!, "); + } + if (dtz.partial_inst_table.get(bin_op.rhs)) |operand_index| { + try writer.print("%{d}", .{operand_index}); + } else if (dtz.const_table.get(bin_op.rhs)) |operand_index| { + try writer.print("@{d}", .{operand_index}); + } else if (dtz.inst_table.get(bin_op.rhs)) |operand_index| { + rhs_kinky = operand_index; + try writer.print("%{d}", .{operand_index}); + } else { + try writer.writeAll("!BADREF!"); + } + if (lhs_kinky != null or rhs_kinky != null) { + try writer.writeAll(") // Instruction does not dominate all uses!"); + if (lhs_kinky) |lhs| { + try writer.print(" %{d}", .{lhs}); + } + if (rhs_kinky) |rhs| { + try writer.print(" %{d}", .{rhs}); + } + try writer.writeAll("\n"); + } else { + try writer.writeAll(")\n"); + } + }, + + .arg => { + const arg = inst.castTag(.arg).?; + try writer.print("{s})\n", .{arg.name}); + }, + + .br => { + const br = inst.castTag(.br).?; + + var lhs_kinky: ?usize = null; + var rhs_kinky: ?usize = null; + + if (dtz.partial_inst_table.get(&br.block.base)) |operand_index| { + try writer.print("%{d}, ", .{operand_index}); + } else if (dtz.const_table.get(&br.block.base)) |operand_index| { + try writer.print("@{d}, ", .{operand_index}); + } else if (dtz.inst_table.get(&br.block.base)) |operand_index| { + lhs_kinky = operand_index; + try writer.print("%{d}, ", .{operand_index}); + } else { + try writer.writeAll("!BADREF!, "); + } + + if (dtz.partial_inst_table.get(br.operand)) |operand_index| { + try writer.print("%{d}", .{operand_index}); + } else if (dtz.const_table.get(br.operand)) |operand_index| { + try writer.print("@{d}", .{operand_index}); + } else if (dtz.inst_table.get(br.operand)) |operand_index| { + rhs_kinky = operand_index; + try writer.print("%{d}", .{operand_index}); + } else { + try writer.writeAll("!BADREF!"); + } + + if (lhs_kinky != null or rhs_kinky != null) { + try writer.writeAll(") // Instruction does not dominate all uses!"); + if (lhs_kinky) |lhs| { + try writer.print(" %{d}", .{lhs}); + } + if (rhs_kinky) |rhs| { + try writer.print(" %{d}", .{rhs}); + } + try writer.writeAll("\n"); + } else { + try writer.writeAll(")\n"); + } + }, + + .brvoid => { + const brvoid = inst.castTag(.brvoid).?; + if (dtz.partial_inst_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("%{d})\n", .{operand_index}); + } else if (dtz.const_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("@{d})\n", .{operand_index}); + } else if (dtz.inst_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{ + operand_index, + }); + } else { + try writer.writeAll("!BADREF!)\n"); + } + }, + + // TODO fill out this debug printing + .assembly, + .block, + .call, + .condbr, + .constant, + .loop, + .varptr, + .switchbr, + => { + try writer.writeAll("!TODO!)\n"); + }, + } + } + } + + fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void { + if (operand.tag == .constant) { + try dtz.const_table.put(operand, dtz.next_const_index); + dtz.next_const_index += 1; + } + } +}; + const EmitZIR = struct { allocator: *Allocator, arena: std.heap.ArenaAllocator, @@ -2073,11 +2361,12 @@ const EmitZIR = struct { var instructions = std.ArrayList(*Inst).init(self.allocator); defer instructions.deinit(); - switch (module_fn.analysis) { + switch (module_fn.state) { .queued => unreachable, .in_progress => unreachable, - .success => |body| { - try self.emitBody(body, &inst_table, &instructions); + .inline_only => unreachable, + .success => { + try self.emitBody(module_fn.body, &inst_table, &instructions); }, .sema_failure => { const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; @@ -2155,7 +2444,9 @@ const EmitZIR = struct { .fn_type = fn_type.inst, .body = .{ .instructions = arena_instrs }, }, - .kw_args = .{}, + .kw_args = .{ + .is_inline = module_fn.state == .inline_only, + }, }; return self.emitUnnamedDecl(&fn_inst.base); } @@ -2203,7 +2494,7 @@ const EmitZIR = struct { }; return self.emitStringLiteral(src, bytes); }, - else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}), + else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {s}", .{@tagName(t)}), } }, .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val), @@ -2274,7 +2565,7 @@ const EmitZIR = struct { }; return self.emitUnnamedDecl(&inst.base); }, - else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), + else => |t| std.debug.panic("TODO implement emitTypedValue for {s}", .{@tagName(t)}), } } @@ -2865,7 +3156,7 @@ const EmitZIR = struct { fn autoName(self: *EmitZIR) ![]u8 { while (true) { - const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name}); + const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.next_auto_name}); self.next_auto_name += 1; const gop = try self.names.getOrPut(proposed_name); if (!gop.found_existing) { @@ -2947,25 +3238,25 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8 try write.inst_table.ensureCapacity(@intCast(u32, instructions.len)); const stderr = std.io.getStdErr().outStream(); - try stderr.print("{} {s} {{ // unanalyzed\n", .{ kind, decl_name }); + try stderr.print("{s} {s} {{ // unanalyzed\n", .{ kind, decl_name }); for (instructions) |inst| { const my_i = write.next_instr_index; write.next_instr_index += 1; if (inst.cast(Inst.Block)) |block| { - const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{}", .{my_i}); + const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{d}", .{my_i}); try write.block_table.put(block, name); } else if (inst.cast(Inst.Loop)) |loop| { - const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{}", .{my_i}); + const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{d}", .{my_i}); try write.loop_table.put(loop, name); } try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = "inst" }); - try stderr.print(" %{} ", .{my_i}); + try stderr.print(" %{d} ", .{my_i}); try write.writeInstToStream(stderr, inst); try stderr.writeByte('\n'); } - try stderr.print("}} // {} {s}\n\n", .{ kind, decl_name }); + try stderr.print("}} // {s} {s}\n\n", .{ kind, decl_name }); } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 365b07247a50..b915a74d0a5a 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -81,11 +81,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice), .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?), .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?), + .set_eval_branch_quota => return analyzeInstSetEvalBranchQuota(mod, scope, old_inst.castTag(.set_eval_branch_quota).?), .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?), - .int => { - const big_int = old_inst.castTag(.int).?.positionals.int; - return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); - }, + .int => return analyzeInstInt(mod, scope, old_inst.castTag(.int).?), .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?), .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?), .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?), @@ -159,16 +157,14 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! } } -pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void { - for (body.instructions) |src_inst, i| { - const analyzed_inst = try analyzeInst(mod, scope, src_inst); - src_inst.analyzed_inst = analyzed_inst; +pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Module.Body) !void { + const tracy = trace(@src()); + defer tracy.end(); + + for (body.instructions) |src_inst| { + const analyzed_inst = try analyzeInst(mod, &block.base, src_inst); + try block.inst_table.putNoClobber(src_inst, analyzed_inst); if (analyzed_inst.ty.zigTypeTag() == .NoReturn) { - for (body.instructions[i..]) |unreachable_inst| { - if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| { - return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{}); - } - } break; } } @@ -180,8 +176,8 @@ pub fn analyzeBodyValueAsType( zir_result_inst: *zir.Inst, body: zir.Module.Body, ) !Type { - try analyzeBody(mod, &block_scope.base, body); - const result_inst = zir_result_inst.analyzed_inst.?; + try analyzeBody(mod, block_scope, body); + const result_inst = block_scope.inst_table.get(zir_result_inst).?; const val = try mod.resolveConstValue(&block_scope.base, result_inst); return val.toType(block_scope.base.arena()); } @@ -264,30 +260,9 @@ fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) Inne return decl; } -/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. -pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { - if (old_inst.analyzed_inst) |inst| return inst; - - // If this assert trips, the instruction that was referenced did not get properly - // analyzed before it was referenced. - const zir_module = scope.namespace().cast(Scope.ZIRModule).?; - const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { - const decl_name = declval.positionals.name; - const entry = zir_module.contents.module.findDecl(decl_name) orelse - return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name}); - break :blk entry; - } else blk: { - // If this assert trips, the instruction that was referenced did not get - // properly analyzed by a previous instruction analysis before it was - // referenced by the current one. - break :blk zir_module.contents.module.findInstDecl(old_inst).?; - }; - const decl = try resolveCompleteZirDecl(mod, scope, entry.decl); - const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl); - // Note: it would be tempting here to store the result into old_inst.analyzed_inst field, - // but this would prevent the analyzeDeclRef from happening, which is needed to properly - // detect Decl dependencies and dependency failures on updates. - return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); +pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst { + const block = scope.cast(Scope.Block).?; + return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses! } fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 { @@ -306,6 +281,24 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { return val.toType(scope.arena()); } +/// Appropriate to call when the coercion has already been done by result +/// location semantics. Asserts the value fits in the provided `Int` type. +/// Only supports `Int` types 64 bits or less. +fn resolveAlreadyCoercedInt( + mod: *Module, + scope: *Scope, + old_inst: *zir.Inst, + comptime Int: type, +) !Int { + comptime assert(@typeInfo(Int).Int.bits <= 64); + const new_inst = try resolveInst(mod, scope, old_inst); + const val = try mod.resolveConstValue(scope, new_inst); + switch (@typeInfo(Int).Int.signedness) { + .signed => return @intCast(Int, val.toSignedInt()), + .unsigned => return @intCast(Int, val.toUnsignedInt()), + } +} + fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 { const new_inst = try resolveInst(mod, scope, old_inst); const coerced = try mod.coerce(scope, dest_type, new_inst); @@ -324,6 +317,8 @@ pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE } fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions // after analysis. const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena()); @@ -343,29 +338,41 @@ fn analyzeInstCoerceResultBlockPtr( scope: *Scope, inst: *zir.Inst.CoerceResultBlockPtr, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); } fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{}); } fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{}); } fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); } /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.ptr); const operand = try resolveInst(mod, scope, inst.positionals.value); return mod.coerce(scope, ptr.ty.elemType(), operand); } fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const ret_type = fn_ty.fnReturnType(); @@ -374,6 +381,8 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr } fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One); @@ -389,6 +398,8 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError! } fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const ret_type = fn_ty.fnReturnType(); @@ -396,6 +407,8 @@ fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr } fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); switch (operand.ty.zigTypeTag()) { .Void, .NoReturn => return mod.constVoid(scope, operand.src), @@ -404,6 +417,8 @@ fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp } fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); switch (operand.ty.zigTypeTag()) { .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}), @@ -412,6 +427,8 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. } fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const elem_ty = operand.ty.elemType(); if (elem_ty.isIndexable()) { @@ -425,6 +442,8 @@ fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) } fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One); const b = try mod.requireRuntimeBlock(scope, inst.base.src); @@ -432,6 +451,8 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro } fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); try mod.validateVarType(scope, inst.base.src, var_type); const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One); @@ -445,6 +466,8 @@ fn analyzeInstAllocInferred( inst: *zir.Inst.NoOp, mut_tag: Type.Tag, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const val_payload = try scope.arena().create(Value.Payload.InferredAlloc); val_payload.* = .{ .data = .{}, @@ -471,6 +494,8 @@ fn analyzeInstResolveInferredAlloc( scope: *Scope, inst: *zir.Inst.UnOp, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.operand); const ptr_val = ptr.castTag(.constant).?.val; const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; @@ -498,6 +523,8 @@ fn analyzeInstStoreToInferredPtr( scope: *Scope, inst: *zir.Inst.BinOp, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; @@ -512,13 +539,29 @@ fn analyzeInstStoreToInferredPtr( return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); } +fn analyzeInstSetEvalBranchQuota( + mod: *Module, + scope: *Scope, + inst: *zir.Inst.UnOp, +) InnerError!*Inst { + const b = try mod.requireFunctionBlock(scope, inst.base.src); + const quota = try resolveAlreadyCoercedInt(mod, scope, inst.positionals.operand, u32); + if (b.branch_quota.* < quota) + b.branch_quota.* = quota; + return mod.constVoid(scope, inst.base.src); +} + fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); return mod.storePtr(scope, inst.base.src, ptr, value); } fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const fn_inst = try resolveInst(mod, scope, inst.positionals.func); const arg_index = inst.positionals.arg_index; @@ -535,7 +578,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) // TODO support C-style var args const param_count = fn_ty.fnParamLen(); if (arg_index >= param_count) { - return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} argument(s)", .{ + return mod.fail(scope, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ arg_index, fn_ty, param_count, @@ -548,6 +591,8 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) } fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // The bytes references memory inside the ZIR module, which can get deallocated // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); @@ -561,26 +606,44 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl); } +fn analyzeInstInt(mod: *Module, scope: *Scope, inst: *zir.Inst.Int) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int); +} + fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name); const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse - return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name}); + return mod.fail(scope, export_inst.base.src, "decl '{s}' not found", .{export_inst.positionals.decl_name}); try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl); return mod.constVoid(scope, export_inst.base.src); } fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const msg = try resolveConstString(mod, scope, inst.positionals.operand); - return mod.fail(scope, inst.base.src, "{}", .{msg}); + return mod.fail(scope, inst.base.src, "{s}", .{msg}); } fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const tracy = trace(@src()); + defer tracy.end(); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + if (b.inlining) |inlining| { + const param_index = inlining.param_index; + inlining.param_index += 1; + return inlining.casted_args[param_index]; + } const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const param_index = b.instructions.items.len; const param_count = fn_ty.fnParamLen(); if (param_index >= param_count) { - return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ + return mod.fail(scope, inst.base.src, "parameter index {d} outside list of length {d}", .{ param_index, param_count, }); @@ -591,6 +654,8 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!* } fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; // Reserve space for a Loop instruction so that generated Break instructions can @@ -608,15 +673,18 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer child_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + try analyzeBody(mod, &child_block, inst.positionals.body); // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. @@ -626,20 +694,25 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError } fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, .label = null, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime or is_comptime, + .branch_quota = parent_block.branch_quota, }; defer child_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + try analyzeBody(mod, &child_block, inst.positionals.body); try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); @@ -651,6 +724,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c } fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; // Reserve space for a Block instruction so that generated Break instructions can @@ -668,6 +743,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, @@ -675,38 +751,57 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.Block.Label, Scope.Block.Label{ .zir_block = inst, - .results = .{}, - .block_inst = block_inst, + .merges = .{ + .results = .{}, + .block_inst = block_inst, + }, }), + .inlining = parent_block.inlining, .is_comptime = is_comptime or parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; - const label = &child_block.label.?; + const merges = &child_block.label.?.merges; defer child_block.instructions.deinit(mod.gpa); - defer label.results.deinit(mod.gpa); + defer merges.results.deinit(mod.gpa); + + try analyzeBody(mod, &child_block, inst.positionals.body); + + return analyzeBlockBody(mod, scope, &child_block, merges); +} + +fn analyzeBlockBody( + mod: *Module, + scope: *Scope, + child_block: *Scope.Block, + merges: *Scope.Block.Merges, +) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + const parent_block = scope.cast(Scope.Block).?; // Blocks must terminate with noreturn instruction. assert(child_block.instructions.items.len != 0); assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); - if (label.results.items.len == 0) { - // No need for a block instruction. We can put the new instructions directly into the parent block. + if (merges.results.items.len == 0) { + // No need for a block instruction. We can put the new instructions + // directly into the parent block. const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); return copied_instructions[copied_instructions.len - 1]; } - if (label.results.items.len == 1) { + if (merges.results.items.len == 1) { const last_inst_index = child_block.instructions.items.len - 1; const last_inst = child_block.instructions.items[last_inst_index]; if (last_inst.breakBlock()) |br_block| { - if (br_block == block_inst) { + if (br_block == merges.block_inst) { // No need for a block instruction. We can put the new instructions directly into the parent block. // Here we omit the break instruction. const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); - return label.results.items[0]; + return merges.results.items[0]; } } } @@ -715,30 +810,38 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt // Need to set the type and emit the Block instruction. This allows machine code generation // to emit a jump instruction to after the block when it encounters the break. - try parent_block.instructions.append(mod.gpa, &block_inst.base); - block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items); - block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; - return &block_inst.base; + try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); + merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items); + merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; + return &merges.block_inst.base; } fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireRuntimeBlock(scope, inst.base.src); return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint); } fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const block = inst.positionals.block; return analyzeBreak(mod, scope, inst.base.src, block, operand); } fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const block = inst.positionals.block; const void_inst = try mod.constVoid(scope, inst.base.src); return analyzeBreak(mod, scope, inst.base.src, block, void_inst); } fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); if (scope.cast(Scope.Block)) |b| { if (!b.is_comptime) { return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt); @@ -748,26 +851,37 @@ fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr } fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl_name = try resolveConstString(mod, scope, inst.positionals.name); return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name); } fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); } fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl = try analyzeDeclVal(mod, scope, inst); const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl); return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); } fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl = inst.positionals.decl; return mod.analyzeDeclRef(scope, inst.base.src, decl); } fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const func = try resolveInst(mod, scope, inst.positionals.func); if (func.ty.zigTypeTag() != .Fn) return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty}); @@ -790,7 +904,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError return mod.fail( scope, inst.positionals.func.src, - "expected at least {} argument(s), found {}", + "expected at least {d} argument(s), found {d}", .{ fn_params_len, call_params_len }, ); } @@ -800,7 +914,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError return mod.fail( scope, inst.positionals.func.src, - "expected {} argument(s), found {}", + "expected {d} argument(s), found {d}", .{ fn_params_len, call_params_len }, ); } @@ -826,28 +940,105 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError const ret_type = func.ty.fnReturnType(); - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; + const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: { + // This logic will get simplified by + // https://github.com/ziglang/zig/issues/6429 + if (try mod.resolveDefinedValue(scope, func)) |func_val| { + const module_fn = switch (func_val.tag()) { + .function => func_val.castTag(.function).?.data, + else => break :blk false, + }; + break :blk module_fn.state == .inline_only; + } + break :blk false; + }; + if (is_inline_call) { + const func_val = try mod.resolveConstValue(scope, func); + const module_fn = switch (func_val.tag()) { + .function => func_val.castTag(.function).?.data, + .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{ + @as([]const u8, if (is_comptime_call) "comptime" else "inline"), + }), + else => unreachable, + }; + + // Analyze the ZIR. The same ZIR gets analyzed into a runtime function + // or an inlined call depending on what union tag the `label` field is + // set to in the `Scope.Block`. + // This block instruction will be used to capture the return value from the + // inlined function. + const block_inst = try scope.arena().create(Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = Inst.Block.base_tag, + .ty = ret_type, + .src = inst.base.src, + }, + .body = undefined, + }; + // If this is the top of the inline/comptime call stack, we use this data. + // Otherwise we pass on the shared data from the parent scope. + var shared_inlining = Scope.Block.Inlining.Shared{ + .branch_count = 0, + .caller = b.func, + }; + // This one is shared among sub-blocks within the same callee, but not + // shared among the entire inline/comptime call stack. + var inlining = Scope.Block.Inlining{ + .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining, + .param_index = 0, + .casted_args = casted_args, + .merges = .{ + .results = .{}, + .block_inst = block_inst, + }, + }; + var inst_table = Scope.Block.InstTable.init(mod.gpa); + defer inst_table.deinit(); + + var child_block: Scope.Block = .{ + .parent = null, + .inst_table = &inst_table, + .func = module_fn, + // Note that we pass the caller's Decl, not the callee. This causes + // compile errors to be attached (correctly) to the caller's Decl. + .decl = scope.decl().?, + .instructions = .{}, + .arena = scope.arena(), + .label = null, + .inlining = &inlining, + .is_comptime = is_comptime_call, + .branch_quota = b.branch_quota, + }; + + const merges = &child_block.inlining.?.merges; + + defer child_block.instructions.deinit(mod.gpa); + defer merges.results.deinit(mod.gpa); + + try mod.emitBackwardBranch(&child_block, inst.base.src); + + // This will have return instructions analyzed as break instructions to + // the block_inst above. + try analyzeBody(mod, &child_block, module_fn.zir); + + return analyzeBlockBody(mod, scope, &child_block, merges); + } + return mod.addCall(b, inst.base.src, ret_type, func, casted_args); } fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); - const fn_zir = blk: { - var fn_arena = std.heap.ArenaAllocator.init(mod.gpa); - errdefer fn_arena.deinit(); - - const fn_zir = try scope.arena().create(Module.Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = fn_inst.positionals.body.instructions, - }, - .arena = fn_arena.state, - }; - break :blk fn_zir; - }; const new_func = try scope.arena().create(Module.Fn); new_func.* = .{ - .analysis = .{ .queued = fn_zir }, + .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued, + .zir = fn_inst.positionals.body, + .body = undefined, .owner_decl = scope.decl().?, }; return mod.constInst(scope, fn_inst.base.src, .{ @@ -857,16 +1048,22 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! } fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); } fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const child_type = try resolveType(mod, scope, optional.positionals.operand); return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); } fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO these should be lazily evaluated const len = try resolveInstConst(mod, scope, array.positionals.lhs); const elem_type = try resolveType(mod, scope, array.positionals.rhs); @@ -875,6 +1072,8 @@ fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) Inn } fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO these should be lazily evaluated const len = try resolveInstConst(mod, scope, array.positionals.len); const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel); @@ -884,6 +1083,8 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar } fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const error_union = try resolveType(mod, scope, inst.positionals.lhs); const payload = try resolveType(mod, scope, inst.positionals.rhs); @@ -895,12 +1096,16 @@ fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) } fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, inst.positionals.operand); return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); } fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // The declarations arena will store the hashmap. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); errdefer new_decl_arena.deinit(); @@ -918,7 +1123,7 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In for (inst.positionals.fields) |field_name| { const entry = try mod.getErrorValue(field_name); if (payload.data.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| { - return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name}); + return mod.fail(scope, inst.base.src, "duplicate error: '{s}'", .{field_name}); } } // TODO create name in format "error:line:column" @@ -931,6 +1136,9 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In } fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const rhs_fields = (try resolveType(mod, scope, inst.positionals.rhs)).getErrs(); const lhs_fields = (try resolveType(mod, scope, inst.positionals.lhs)).getErrs(); if (lhs_fields == .anyerror or rhs_fields == .anyerror) @@ -1001,6 +1209,8 @@ fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) } fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const duped_name = try scope.arena().dupe(u8, inst.positionals.name); return mod.constInst(scope, inst.base.src, .{ .ty = Type.initTag(.enum_literal), @@ -1009,6 +1219,8 @@ fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiter } fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, unwrap.positionals.operand); assert(operand.ty.zigTypeTag() == .Pointer); @@ -1039,18 +1251,26 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp } fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); } fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{}); } fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); } fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, fntype.positionals.return_type); // Hot path for some common function types. @@ -1092,16 +1312,22 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne } fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); } fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, as.positionals.lhs); const new_inst = try resolveInst(mod, scope, as.positionals.rhs); return mod.coerce(scope, dest_type, new_inst); } fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand); if (ptr.ty.zigTypeTag() != .Pointer) { return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty}); @@ -1113,6 +1339,8 @@ fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) In } fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr); const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name); @@ -1134,7 +1362,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr return mod.fail( scope, fieldptr.positionals.field_name.src, - "no member named '{}' in '{}'", + "no member named '{s}' in '{}'", .{ field_name, elem_ty }, ); } @@ -1155,7 +1383,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr return mod.fail( scope, fieldptr.positionals.field_name.src, - "no member named '{}' in '{}'", + "no member named '{s}' in '{}'", .{ field_name, elem_ty }, ); } @@ -1173,7 +1401,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr // TODO resolve inferred error sets const entry = if (val.castTag(.error_set)) |payload| (payload.data.fields.getEntry(field_name) orelse - return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* + return mod.fail(scope, fieldptr.base.src, "no error named '{s}' in '{}'", .{ field_name, child_type })).* else try mod.getErrorValue(field_name); @@ -1201,9 +1429,9 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr } if (&container_scope.file_scope.base == mod.root_scope) { - return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{}'", .{field_name}); + return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{s}'", .{field_name}); } else { - return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{}'", .{ child_type, field_name }); + return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); } }, else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), @@ -1215,6 +1443,8 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr } fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1251,12 +1481,16 @@ fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE } fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); return mod.bitcast(scope, dest_type, operand); } fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1293,6 +1527,8 @@ fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inne } fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index); @@ -1329,6 +1565,8 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne } fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); const start = try resolveInst(mod, scope, inst.positionals.start); const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null; @@ -1338,6 +1576,8 @@ fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerErr } fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs); const start = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1345,6 +1585,8 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn } fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const start = try resolveInst(mod, scope, inst.positionals.lhs); const end = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1367,6 +1609,8 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In } fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); try validateSwitch(mod, scope, target, inst); @@ -1378,17 +1622,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In const item = try mod.resolveConstValue(scope, casted); if (target_val.eql(item)) { - try analyzeBody(mod, scope, case.body); + try analyzeBody(mod, scope.cast(Scope.Block).?, case.body); return mod.constNoReturn(scope, inst.base.src); } } - try analyzeBody(mod, scope, inst.positionals.else_body); + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); return mod.constNoReturn(scope, inst.base.src); } if (inst.positionals.cases.len == 0) { // no cases just analyze else_branch - try analyzeBody(mod, scope, inst.positionals.else_body); + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); return mod.constNoReturn(scope, inst.base.src); } @@ -1397,11 +1641,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In var case_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer case_block.instructions.deinit(mod.gpa); @@ -1413,7 +1660,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In const casted = try mod.coerce(scope, target.ty, resolved); const item = try mod.resolveConstValue(scope, casted); - try analyzeBody(mod, &case_block.base, case.body); + try analyzeBody(mod, &case_block, case.body); cases[i] = .{ .item = item, @@ -1422,7 +1669,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In } case_block.instructions.items.len = 0; - try analyzeBody(mod, &case_block.base, inst.positionals.else_body); + try analyzeBody(mod, &case_block, inst.positionals.else_body); const else_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), @@ -1595,28 +1842,34 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw } fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveConstString(mod, scope, inst.positionals.operand); const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) { error.ImportOutsidePkgPath => { - return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand}); + return mod.fail(scope, inst.base.src, "import of file outside package path: '{s}'", .{operand}); }, error.FileNotFound => { - return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand}); + return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand}); }, else => { - // TODO user friendly error to string - return mod.fail(scope, inst.base.src, "unable to open '{}': {}", .{ operand, @errorName(err) }); + // TODO: make sure this gets retried and not cached + return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); }, }; return mod.constType(scope, inst.base.src, file_scope.root_container.ty); } fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); } fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{}); } @@ -1641,7 +1894,7 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ + return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -1684,14 +1937,20 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE } fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{}); } fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); } fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{}); } @@ -1716,7 +1975,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ + return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -1733,7 +1992,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { - return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); + return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } if (casted_lhs.value()) |lhs_val| { @@ -1752,7 +2011,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn const ir_tag = switch (inst.base.tag) { .add => Inst.Tag.add, .sub => Inst.Tag.sub, - else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), + else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), }; return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); @@ -1770,24 +2029,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir } const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; - const value = try switch (inst.base.tag) { + const value = switch (inst.base.tag) { .add => blk: { const val = if (is_int) - Module.intAdd(scope.arena(), lhs_val, rhs_val) + try Module.intAdd(scope.arena(), lhs_val, rhs_val) else - mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); + try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); break :blk val; }, .sub => blk: { const val = if (is_int) - Module.intSub(scope.arena(), lhs_val, rhs_val) + try Module.intSub(scope.arena(), lhs_val, rhs_val) else - mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); + try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); break :blk val; }, - else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), + else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}), }; + log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value }); + return mod.constInst(scope, inst.base.src, .{ .ty = res_type, .val = value, @@ -1795,11 +2056,15 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir } fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, deref.positionals.operand); return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src); } fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, assembly.positionals.return_type); const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; @@ -1844,6 +2109,8 @@ fn analyzeInstCmp( inst: *zir.Inst.BinOp, op: std.math.CompareOperator, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const lhs = try resolveInst(mod, scope, inst.positionals.lhs); const rhs = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1877,7 +2144,7 @@ fn analyzeInstCmp( return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { if (!is_equality_cmp) { - return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); + return mod.fail(scope, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)}); } const lhs_val = lhs.value(); const rhs_val = rhs.value(); @@ -1892,7 +2159,7 @@ fn analyzeInstCmp( return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op); } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { if (!is_equality_cmp) { - return mod.fail(scope, inst.base.src, "{} operator not allowed for types", .{@tagName(op)}); + return mod.fail(scope, inst.base.src, "{s} operator not allowed for types", .{@tagName(op)}); } return mod.constBool(scope, inst.base.src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); } @@ -1900,11 +2167,15 @@ fn analyzeInstCmp( } fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.constType(scope, inst.base.src, operand.ty); } fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); var insts_to_res = try mod.gpa.alloc(*ir.Inst, inst.positionals.items.len); defer mod.gpa.free(insts_to_res); for (inst.positionals.items) |item, i| { @@ -1915,6 +2186,8 @@ fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer } fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand); const bool_type = Type.initTag(.bool); const operand = try mod.coerce(scope, bool_type, uncasted_operand); @@ -1926,6 +2199,8 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr } fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const bool_type = Type.initTag(.bool); const uncasted_lhs = try resolveInst(mod, scope, inst.positionals.lhs); const lhs = try mod.coerce(scope, bool_type, uncasted_lhs); @@ -1948,48 +2223,60 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr } fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); } fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsErr(scope, inst.base.src, operand); } fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition); const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond); + const parent_block = scope.cast(Scope.Block).?; + if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; - try analyzeBody(mod, scope, body.*); + try analyzeBody(mod, parent_block, body.*); return mod.constNoReturn(scope, inst.base.src); } - const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); - var true_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer true_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &true_block.base, inst.positionals.then_body); + try analyzeBody(mod, &true_block, inst.positionals.then_body); var false_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer false_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &false_block.base, inst.positionals.else_body); + try analyzeBody(mod, &false_block, inst.positionals.else_body); const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }; const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }; @@ -2002,6 +2289,8 @@ fn analyzeInstUnreachable( unreach: *zir.Inst.NoOp, safety_check: bool, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireRuntimeBlock(scope, unreach.base.src); // TODO Add compile error for @optimizeFor occurring too late in a scope. if (safety_check and mod.wantSafety(scope)) { @@ -2012,13 +2301,31 @@ fn analyzeInstUnreachable( } fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + + if (b.inlining) |inlining| { + // We are inlining a function call; rewrite the `ret` as a `break`. + try inlining.merges.results.append(mod.gpa, operand); + return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); + } + return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); } fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const tracy = trace(@src()); + defer tracy.end(); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + if (b.inlining) |inlining| { + // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. + const void_inst = try mod.constVoid(scope, inst.base.src); + try inlining.merges.results.append(mod.gpa, void_inst); + return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); + } + if (b.func) |func| { // Need to emit a compile error if returning void is not allowed. const void_inst = try mod.constVoid(scope, inst.base.src); @@ -2050,9 +2357,9 @@ fn analyzeBreak( while (opt_block) |block| { if (block.label) |*label| { if (label.zir_block == zir_block) { - try label.results.append(mod.gpa, operand); - const b = try mod.requireRuntimeBlock(scope, src); - return mod.addBr(b, src, label.block_inst, operand); + try label.merges.results.append(mod.gpa, operand); + const b = try mod.requireFunctionBlock(scope, src); + return mod.addBr(b, src, label.merges.block_inst, operand); } } opt_block = block.parent; @@ -2063,7 +2370,7 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr const decl_name = inst.positionals.name; const zir_module = scope.namespace().cast(Scope.ZIRModule).?; const src_decl = zir_module.contents.module.findDecl(decl_name) orelse - return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); + return mod.fail(scope, inst.base.src, "use of undeclared identifier '{s}'", .{decl_name}); const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl); @@ -2071,12 +2378,16 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr } fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const elem_type = try resolveType(mod, scope, inst.positionals.operand); const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size); return mod.constType(scope, inst.base.src, ty); } fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO lazy values const @"align" = if (inst.kw_args.@"align") |some| @truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32))) diff --git a/test/compare_output.zig b/test/compare_output.zig index 46c475e04609..9dc80f202d5f 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -453,7 +453,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", .{index, arg}); + \\ try stdout.print("{}: {s}\n", .{index, arg}); \\ } \\} , @@ -492,7 +492,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", .{index, arg}); + \\ try stdout.print("{}: {s}\n", .{index, arg}); \\ } \\} , diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 632601b70cc8..2097ea184272 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -657,4 +657,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("static K&R-style no prototype function declaration (empty parameter list)", + \\#include + \\static int foo() { + \\ return 42; + \\} + \\int main() { + \\ if (foo() != 42) abort(); + \\ return 0; + \\} + , ""); + + cases.add("K&R-style static function prototype for unused function", + \\static int foo(); + \\int main() { + \\ return 0; + \\} + , ""); + + cases.add("K&R-style static function prototype + separate definition", + \\#include + \\static int foo(); + \\static int foo(int a, int b) { + \\ return a + b; + \\} + \\int main() { + \\ if (foo(40, 2) != 42) abort(); + \\ return 0; + \\} + , ""); + + cases.add("dollar sign in identifiers", + \\#include + \\#define $FOO 2 + \\#define $foo bar$ + \\#define $baz($x) ($x + $FOO) + \\int $$$(int $x$) { return $x$ + $FOO; } + \\int main() { + \\ int bar$ = 42; + \\ if ($foo != 42) abort(); + \\ if (bar$ != 42) abort(); + \\ if ($baz(bar$) != 44) abort(); + \\ if ($$$(bar$) != 44) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index f78d6744e1d3..1cde470887be 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -97,7 +97,7 @@ pub const CompareOutputContext = struct { switch (case.special) { Special.Asm => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {s}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -116,7 +116,7 @@ pub const CompareOutputContext = struct { }, Special.None => { for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ "compare-output", case.name, @tagName(mode), @@ -141,7 +141,7 @@ pub const CompareOutputContext = struct { } }, Special.RuntimeSafety => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig index 06990241cc84..1de329112c14 100644 --- a/test/src/run_translated_c.zig +++ b/test/src/run_translated_c.zig @@ -77,7 +77,7 @@ pub const RunTranslatedCContext = struct { pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {s}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -92,13 +92,13 @@ pub const RunTranslatedCContext = struct { .basename = case.sources.items[0].filename, }, }); - translate_c.step.name = b.fmt("{} translate-c", .{annotated_case_name}); + translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); const exe = translate_c.addExecutable(); exe.setTarget(self.target); - exe.step.name = b.fmt("{} build-exe", .{annotated_case_name}); + exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); exe.linkLibC(); const run = exe.run(); - run.step.name = b.fmt("{} run", .{annotated_case_name}); + run.step.name = b.fmt("{s} run", .{annotated_case_name}); if (!case.allow_warnings) { run.expectStdErrEqual(""); } diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 405c5954ba5a..c43f3269269d 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -99,7 +99,7 @@ pub const TranslateCContext = struct { const b = self.b; const translate_c_cmd = "translate-c"; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s}", .{ translate_c_cmd, case.name }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index db6e5962912d..f82631cd9e19 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -141,5 +141,5 @@ comptime { _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); _ = @import("behavior/src.zig"); - _ = @import("behavior/translate_c_macros.zig"); + // _ = @import("behavior/translate_c_macros.zig"); } diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index eb9c3f5d07cc..16c7b14944a1 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const expectEqualStrings = std.testing.expectEqualStrings; const expectError = std.testing.expectError; var global_x: i32 = 1; @@ -541,7 +542,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); suspend; - expectEqual(@as([]const u8, "hello"), msg); + expectEqualStrings("hello", msg); ok = true; } }; diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index 28979b8ae8f1..20ca0d314666 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -436,6 +436,26 @@ test "switch with disjoint range" { } } +test "switch variable for range and multiple prongs" { + const S = struct { + fn doTheTest() void { + var u: u8 = 16; + doTheSwitch(u); + comptime doTheSwitch(u); + var v: u8 = 42; + doTheSwitch(v); + comptime doTheSwitch(v); + } + fn doTheSwitch(q: u8) void { + switch (q) { + 0...40 => |x| expect(x == 16), + 41, 42, 43 => |x| expect(x == 42), + else => expect(false), + } + } + }; +} + var state: u32 = 0; fn poll() void { switch (state) { diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index d350ae369a9b..544adaf979e8 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -742,7 +742,7 @@ test "@unionInit on union w/ tag but no fields" { const Data = union(Type) { no_op: void, - pub fn decode(buf: []const u8) !Data { + pub fn decode(buf: []const u8) Data { return @unionInit(Data, "no_op", {}); } }; @@ -753,7 +753,7 @@ test "@unionInit on union w/ tag but no fields" { fn doTheTest() void { var data: Data = .{ .no_op = .{} }; - var o = try Data.decode(&[_]u8{}); + var o = Data.decode(&[_]u8{}); expectEqual(Type.no_op, o); } }; diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index b71362cd3d0e..7d05c60bb87b 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -155,4 +155,48 @@ pub fn addCases(ctx: *TestContext) !void { "Hello, World!\n", ); } + + { + var case = ctx.exe("exit fn taking argument", macos_aarch64); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(0); + \\} + \\ + \\fn exit(ret: usize) noreturn { + \\ asm volatile ("svc #0x80" + \\ : + \\ : [number] "{x16}" (1), + \\ [arg1] "{x0}" (ret) + \\ : "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("exit fn taking argument", linux_aarch64); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(0); + \\} + \\ + \\fn exit(ret: usize) noreturn { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{x8}" (93), + \\ [arg1] "{x0}" (ret) + \\ : "memory", "cc" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index a0a4587983fd..b227d6a7839b 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -67,7 +67,22 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); + case.addCompareOutput( + \\export fn main() i32 { + \\ @setEvalBranchQuota(1001); + \\ const y = rec(1001); + \\ return y - 1; + \\} + \\ + \\inline fn rec(n: usize) usize { + \\ if (n <= 1) return n; + \\ return rec(n - 1); + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; diff --git a/test/stage2/llvm_backend.zig b/test/stage2/llvm_backend.zig new file mode 100644 index 000000000000..1b753621ea4e --- /dev/null +++ b/test/stage2/llvm_backend.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; +const build_options = @import("build_options"); + +// These tests should work with all platforms, but we're using linux_x64 for +// now for consistency. Will be expanded eventually. +const linux_x64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64); + + case.addCompareOutput( + \\fn add(a: i32, b: i32) i32 { + \\ return a + b; + \\} + \\ + \\export fn main() c_int { + \\ var a: i32 = -5; + \\ const x = add(a, 7); + \\ var y = add(2, 0); + \\ y -= x; + \\ return y; + \\} + , ""); + } +} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index a8a8edd59873..61e4eb629107 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -27,11 +27,11 @@ const wasi = std.zig.CrossTarget{ }; pub fn addCases(ctx: *TestContext) !void { - try @import("zir.zig").addCases(ctx); try @import("cbe.zig").addCases(ctx); try @import("spu-ii.zig").addCases(ctx); try @import("arm.zig").addCases(ctx); try @import("aarch64.zig").addCases(ctx); + try @import("llvm_backend.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); @@ -534,7 +534,8 @@ pub fn addCases(ctx: *TestContext) !void { ); } { - var case = ctx.exe("adding numbers at runtime", linux_x64); + var case = ctx.exe("adding numbers at runtime and comptime", linux_x64); + case.addCompareOutput( \\export fn _start() noreturn { \\ add(3, 4); @@ -558,6 +559,54 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + // comptime function call + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ return a + b; + \\} + \\ + \\const x = add(3, 4); + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (x - 7) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + // Inline function call + case.addCompareOutput( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); } { @@ -1547,4 +1596,102 @@ pub fn addCases(ctx: *TestContext) !void { \\} , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"}); } + + { + var case = ctx.exe("compile error in inline fn call fixed", linux_x64); + case.addError( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(10, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , &[_][]const u8{":8:18: error: bad"}); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + { + var case = ctx.exe("recursive inline function", linux_x64); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ const y = fibonacci(7); + \\ exit(y - 21); + \\} + \\ + \\inline fn fibonacci(n: usize) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + case.addError( + \\export fn _start() noreturn { + \\ const y = fibonacci(999); + \\ exit(y - 21); + \\} + \\ + \\inline fn fibonacci(n: usize) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"}); + } } diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig deleted file mode 100644 index da4038e79219..000000000000 --- a/test/stage2/zir.zig +++ /dev/null @@ -1,316 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; -// self-hosted does not yet support PE executable files / COFF object files -// or mach-o files. So we do the ZIR transform test cases cross compiling for -// x86_64-linux. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - ctx.transformZIR("referencing decls which appear later in the file", linux_x64, - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %11 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@fnty = fntype([], @void, cc=C) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@entry = fn(@fnty, { - \\ %a = str("\x32\x08\x01\x0a") - \\ %a_ref = ref(%a) - \\ %eptr0 = elemptr(%a_ref, @0) - \\ %eptr1 = elemptr(%a_ref, @1) - \\ %eptr2 = elemptr(%a_ref, @2) - \\ %eptr3 = elemptr(%a_ref, @3) - \\ %v0 = deref(%eptr0) - \\ %v1 = deref(%eptr1) - \\ %v2 = deref(%eptr2) - \\ %v3 = deref(%eptr3) - \\ %x0 = add(%v0, %v1) - \\ %x1 = add(%v2, %v3) - \\ %result = add(%x0, %x1) - \\ - \\ %expected = int(69) - \\ %ok = cmp_eq(%result, %expected) - \\ %10 = condbr(%ok, { - \\ %11 = returnvoid() - \\ }, { - \\ %12 = breakpoint() - \\ }) - \\}) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\@unnamed$6 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$6, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@entry__anon_1 = str("2\x08\x01\n") - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$11 = str("entry") - \\@unnamed$12 = export(@unnamed$11, "entry") - \\@11 = primitive(void_value) - \\ - ); - - { - var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64); - case.addTransform( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@b, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@unnamed$9 = fntype([], @void, cc=C) - \\@a = fn(@unnamed$9, { - \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@unnamed$11 = fntype([], @void, cc=C) - \\@b = fn(@unnamed$11, { - \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - // Now we introduce a compile error - case.addError( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@c, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = str("message") - \\ - \\@c = fn(@fnty, { - \\ %9 = compileerror(@b) - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - &[_][]const u8{ - ":20:21: error: message", - }, - ); - // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are - // referencing either of them. This tests that the cycle is detected, and the error - // goes away. - case.addTransform( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@c, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = str("message") - \\ - \\@c = fn(@fnty, { - \\ %9 = compileerror(@b) - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_3") - \\@9__anon_3 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - } - - if (std.Target.current.os.tag != .linux or - std.Target.current.cpu.arch != .x86_64) - { - // TODO implement self-hosted PE (.exe file) linking - // TODO implement more ZIR so we don't depend on x86_64-linux - return; - } - - ctx.compareOutputZIR("hello world ZIR", - \\@noreturn = primitive(noreturn) - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@msg = str("Hello, world!\n") - \\ - \\@start_fnty = fntype([], @noreturn, cc=Naked) - \\@start = fn(@start_fnty, { - \\ %SYS_exit_group = int(231) - \\ %exit_code = as(@usize, @0) - \\ - \\ %syscall = str("syscall") - \\ %sysoutreg = str("={rax}") - \\ %rax = str("{rax}") - \\ %rdi = str("{rdi}") - \\ %rcx = str("rcx") - \\ %rdx = str("{rdx}") - \\ %rsi = str("{rsi}") - \\ %r11 = str("r11") - \\ %memory = str("memory") - \\ - \\ %SYS_write = as(@usize, @1) - \\ %STDOUT_FILENO = as(@usize, @1) - \\ - \\ %msg_addr = ptrtoint(@msg) - \\ - \\ %len_name = str("len") - \\ %msg_len_ptr = fieldptr(@msg, %len_name) - \\ %msg_len = deref(%msg_len_ptr) - \\ %rc_write = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi, %rsi, %rdx], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) - \\ - \\ %rc_exit = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_exit_group, %exit_code]) - \\ - \\ %99 = unreachable() - \\}); - \\ - \\@9 = str("_start") - \\@11 = export(@9, "start") - , - \\Hello, world! - \\ - ); - - ctx.compareOutputZIR("function call with no args no return value", - \\@noreturn = primitive(noreturn) - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@exit0_fnty = fntype([], @noreturn) - \\@exit0 = fn(@exit0_fnty, { - \\ %SYS_exit_group = int(231) - \\ %exit_code = as(@usize, @0) - \\ - \\ %syscall = str("syscall") - \\ %sysoutreg = str("={rax}") - \\ %rax = str("{rax}") - \\ %rdi = str("{rdi}") - \\ %rcx = str("rcx") - \\ %r11 = str("r11") - \\ %memory = str("memory") - \\ - \\ %rc = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_exit_group, %exit_code]) - \\ - \\ %99 = unreachable() - \\}); - \\ - \\@start_fnty = fntype([], @noreturn, cc=Naked) - \\@start = fn(@start_fnty, { - \\ %0 = call(@exit0, []) - \\}) - \\@9 = str("_start") - \\@11 = export(@9, "start") - , ""); -} diff --git a/test/tests.zig b/test/tests.zig index 250c1cc4eacd..5ee381e5c23e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -482,7 +482,7 @@ pub fn addPkgTests( is_wasmtime_enabled: bool, glibc_dir: ?[]const u8, ) *build.Step { - const step = b.step(b.fmt("test-{}", .{name}), desc); + const step = b.step(b.fmt("test-{s}", .{name}), desc); for (test_targets) |test_target| { if (skip_non_native and !test_target.target.isNative()) @@ -523,7 +523,7 @@ pub fn addPkgTests( const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", .{ + these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s} ", .{ name, triple_prefix, @tagName(test_target.mode), @@ -570,7 +570,7 @@ pub const StackTracesContext = struct { const expect_for_mode = expect[@enumToInt(mode)]; if (expect_for_mode.len == 0) continue; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ "stack-trace", name, @tagName(mode), @@ -637,7 +637,7 @@ pub const StackTracesContext = struct { defer args.deinit(); args.append(full_exe_path) catch unreachable; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; defer child.deinit(); @@ -650,7 +650,7 @@ pub const StackTracesContext = struct { if (b.verbose) { printInvocation(args.items); } - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); + child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; defer b.allocator.free(stdout); @@ -659,14 +659,14 @@ pub const StackTracesContext = struct { var stderr = stderrFull; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); + debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); }; switch (term) { .Exited => |code| { const expect_code: u32 = 1; if (code != expect_code) { - warn("Process {} exited with error code {} but expected code {}\n", .{ + warn("Process {s} exited with error code {d} but expected code {d}\n", .{ full_exe_path, code, expect_code, @@ -676,17 +676,17 @@ pub const StackTracesContext = struct { } }, .Signal => |signum| { - warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum }); + warn("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum }); printInvocation(args.items); return error.TestFailed; }, .Stopped => |signum| { - warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum }); + warn("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum }); printInvocation(args.items); return error.TestFailed; }, .Unknown => |code| { - warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code }); + warn("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code }); printInvocation(args.items); return error.TestFailed; }, @@ -732,9 +732,9 @@ pub const StackTracesContext = struct { warn( \\ \\========= Expected this output: ========= - \\{} + \\{s} \\================================================ - \\{} + \\{s} \\ , .{ self.expect_output, got }); return error.TestFailed; @@ -856,7 +856,7 @@ pub const CompileErrorContext = struct { zig_args.append("-O") catch unreachable; zig_args.append(@tagName(self.build_mode)) catch unreachable; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (b.verbose) { printInvocation(zig_args.items); @@ -870,7 +870,7 @@ pub const CompileErrorContext = struct { child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); + child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ zig_args.items[0], @errorName(err) }); var stdout_buf = ArrayList(u8).init(b.allocator); var stderr_buf = ArrayList(u8).init(b.allocator); @@ -879,7 +879,7 @@ pub const CompileErrorContext = struct { child.stderr.?.inStream().readAllArrayList(&stderr_buf, max_stdout_size) catch unreachable; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); + debug.panic("Unable to spawn {s}: {s}\n", .{ zig_args.items[0], @errorName(err) }); }; switch (term) { .Exited => |code| { @@ -889,7 +889,7 @@ pub const CompileErrorContext = struct { } }, else => { - warn("Process {} terminated unexpectedly\n", .{b.zig_exe}); + warn("Process {s} terminated unexpectedly\n", .{b.zig_exe}); printInvocation(zig_args.items); return error.TestFailed; }, @@ -903,7 +903,7 @@ pub const CompileErrorContext = struct { \\ \\Expected empty stdout, instead found: \\================================================ - \\{} + \\{s} \\================================================ \\ , .{stdout}); @@ -926,7 +926,7 @@ pub const CompileErrorContext = struct { if (!ok) { warn("\n======== Expected these compile errors: ========\n", .{}); for (self.case.expected_errors.items) |expected| { - warn("{}\n", .{expected}); + warn("{s}\n", .{expected}); } } } else { @@ -935,7 +935,7 @@ pub const CompileErrorContext = struct { warn( \\ \\=========== Expected compile error: ============ - \\{} + \\{s} \\ , .{expected}); ok = false; @@ -947,7 +947,7 @@ pub const CompileErrorContext = struct { if (!ok) { warn( \\================= Full output: ================= - \\{} + \\{s} \\ , .{stderr}); return error.TestFailed; @@ -1023,7 +1023,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {s}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -1058,7 +1058,7 @@ pub const StandaloneContext = struct { pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8) void { const b = self.b; - const annotated_case_name = b.fmt("build {} (Debug)", .{build_file}); + const annotated_case_name = b.fmt("build {s} (Debug)", .{build_file}); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -1079,7 +1079,7 @@ pub const StandaloneContext = struct { const run_cmd = b.addSystemCommand(zig_args.items); - const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); + const log_step = b.addLog("PASS {s}\n", .{annotated_case_name}); log_step.step.dependOn(&run_cmd.step); self.step.dependOn(&log_step.step); @@ -1089,7 +1089,7 @@ pub const StandaloneContext = struct { const b = self.b; for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ root_src, @tagName(mode), }) catch unreachable; @@ -1103,7 +1103,7 @@ pub const StandaloneContext = struct { exe.linkSystemLibrary("c"); } - const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); + const log_step = b.addLog("PASS {s}\n", .{annotated_case_name}); log_step.step.dependOn(&exe.step); self.step.dependOn(&log_step.step); @@ -1172,7 +1172,7 @@ pub const GenHContext = struct { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); const full_h_path = self.obj.getOutputHPath(); const actual_h = try io.readFileAlloc(b.allocator, full_h_path); @@ -1182,9 +1182,9 @@ pub const GenHContext = struct { warn( \\ \\========= Expected this output: ================ - \\{} + \\{s} \\========= But found: =========================== - \\{} + \\{s} \\ , .{ expected_line, actual_h }); return error.TestFailed; @@ -1196,7 +1196,7 @@ pub const GenHContext = struct { fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } @@ -1232,7 +1232,7 @@ pub const GenHContext = struct { const b = self.b; const mode = builtin.Mode.Debug; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -1253,7 +1253,7 @@ pub const GenHContext = struct { fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 60f3d0e2c5da..6ebe27c0a795 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1375,11 +1375,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\void b(void) {} \\void c(); \\void d(void); + \\static void e() {} + \\static void f(void) {} + \\static void g(); + \\static void h(void); , &[_][]const u8{ \\pub export fn a() void {} \\pub export fn b() void {} \\pub extern fn c(...) void; \\pub extern fn d() void; + \\pub fn e() callconv(.C) void {} + \\pub fn f() callconv(.C) void {} + \\pub extern fn g() void; + \\pub extern fn h() void; }); cases.add("variable declarations", @@ -2938,7 +2946,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn a() callconv(.C) void {} \\pub fn b() callconv(.C) void {} \\pub export fn c() void {} - \\pub fn foo(...) callconv(.C) void {} + \\pub fn foo() callconv(.C) void {} }); cases.add("casting away const and volatile",